.net 如何从头开始以编程方式配置log4net(无配置)

.net 如何从头开始以编程方式配置log4net(无配置),.net,logging,log4net,.net,Logging,Log4net,这是个坏主意,我知道,但是。。。 我想从头开始以编程方式配置log4net,而不需要配置文件。我正在为我和我的团队开发一个简单的日志应用程序,用于我们负责的一些相对较小的部门应用程序。我希望他们都登录到同一个数据库。日志应用程序只是log4net的一个包装器,它预先配置了AdoNetAppender 所有应用程序都是ClickOnce部署的,这给部署配置文件带来了一个小问题。如果配置文件是核心项目的一部分,我可以将其属性设置为与程序集一起部署。但它是链接应用程序的一部分,因此我没有选择将其与主应

这是个坏主意,我知道,但是。。。 我想从头开始以编程方式配置log4net,而不需要配置文件。我正在为我和我的团队开发一个简单的日志应用程序,用于我们负责的一些相对较小的部门应用程序。我希望他们都登录到同一个数据库。日志应用程序只是log4net的一个包装器,它预先配置了AdoNetAppender

所有应用程序都是ClickOnce部署的,这给部署配置文件带来了一个小问题。如果配置文件是核心项目的一部分,我可以将其属性设置为与程序集一起部署。但它是链接应用程序的一部分,因此我没有选择将其与主应用程序一起部署。(如果不是这样,请有人告诉我)

可能是因为这是一个坏主意,似乎没有太多的示例代码可用于从头编程配置log4net。这是我到目前为止所拥有的

Dim apndr As New AdoNetAppender()
apndr.CommandText = "INSERT INTO LOG_ENTRY (LOG_DTM, LOG_LEVEL, LOGGER, MESSAGE, PROGRAM, USER_ID, MACHINE, EXCEPTION) VALUES (@log_date, @log_level, @logger, @message, @program, @user, @machine, @exception)"
apndr.ConnectionString = connectionString
apndr.ConnectionType = "System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
apndr.CommandType = CommandType.Text
Dim logDate As New AdoNetAppenderParameter()
logDate.ParameterName = "@log_date"
logDate.DbType = DbType.DateTime
logDate.Layout = New RawTimeStampLayout()
apndr.AddParameter(logDate)
Dim logLevel As New AdoNetAppenderParameter()
logLevel.ParameterName = "@log_level"
'And so forth...
在为
apndr
配置了所有参数之后,我首先尝试了以下方法

Dim hier As Hierarchy = DirectCast(LogManager.GetRepository(), Hierarchy)
hier.Root.AddAppender(apndr)
它不起作用。然后,在黑暗中,我尝试了这个

BasicConfigurator.Configure(apndr)

那也没用。关于如何在没有配置文件的情况下从头开始以编程方式配置log4net,有人有什么好的参考资料吗?

我在过去做过的一种方法是将配置文件作为嵌入式资源包含进来,然后直接使用

这样,我就可以使用我熟悉的配置语法,不必担心部署文件。

如前所述,使用资源是一个很好的解决方案

这有点限制,因为嵌入的资源内容将在编译时固定。我有一个日志组件,它使用定义为appSettings的变量(例如RollingFileAppender的文件名、默认日志记录级别,如果您想使用AdoNetAppender,可能是连接字符串名称),生成一个带有基本Log4Net配置的XmlDocument。然后我调用
log4net.Config.XmlConfigurator.Configure
来使用生成的XmlDocument的根元素配置log4net


然后,管理员可以通过修改一些应用设置(通常是级别、文件名等)自定义“标准”配置,或者可以指定一个外部配置文件以获得更多控制。

奇怪的是,
BasicConfigurator.Configure(apndr)
不起作用。在我的情况下,它做了它的工作。。。但是,不管怎样,答案来了——你应该写
hier.Configured=true(c#代码)在完成所有设置之后。

下面是一个示例类,它完全用代码创建log4net配置。我应该提到,通过静态方法创建记录器通常被认为是不好的,但在我的上下文中,这正是我想要的。无论如何,您可以分割代码以满足您的需要

using log4net;
using log4net.Repository.Hierarchy;
using log4net.Core;
using log4net.Appender;
using log4net.Layout;

namespace dnservices.logging
{
public class Logger
{
    private PatternLayout _layout = new PatternLayout();
    private const string LOG_PATTERN = "%d [%t] %-5p %m%n";

    public string DefaultPattern
    {
        get { return LOG_PATTERN; }
    }

    public Logger()
    {
        _layout.ConversionPattern = DefaultPattern;
        _layout.ActivateOptions();
    }

    public PatternLayout DefaultLayout
    {
        get { return _layout; }
    }

    public void AddAppender(IAppender appender)
    {
        Hierarchy hierarchy = 
            (Hierarchy)LogManager.GetRepository();

        hierarchy.Root.AddAppender(appender);
    }

    static Logger()
    {
        Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();
        TraceAppender tracer = new TraceAppender();
        PatternLayout patternLayout = new PatternLayout();

        patternLayout.ConversionPattern = LOG_PATTERN;
        patternLayout.ActivateOptions();

        tracer.Layout = patternLayout;
        tracer.ActivateOptions();
        hierarchy.Root.AddAppender(tracer);

        RollingFileAppender roller = new RollingFileAppender();
        roller.Layout = patternLayout;
        roller.AppendToFile = true;
        roller.RollingStyle = RollingFileAppender.RollingMode.Size;
        roller.MaxSizeRollBackups = 4;
        roller.MaximumFileSize = "100KB";
        roller.StaticLogFileName = true;
        roller.File = "dnservices.txt";
        roller.ActivateOptions();
        hierarchy.Root.AddAppender(roller);

        hierarchy.Root.Level = Level.All;
        hierarchy.Configured = true;
    }

    public static ILog Create()
    {
        return LogManager.GetLogger("dnservices");
    }
}

}

我无法在问题的代码片段中判断“'等等…”是否包括Todd Stout的答案中指出的非常重要的apndr.ActivateOptions()。如果没有ActivateOptions(),Appender将处于非活动状态,并且不会执行任何可以解释其失败原因的操作。

//我已将三个配置文件作为嵌入式资源嵌入,并按如下方式访问它们:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Resources;
using System.IO;

namespace Loader
{
  class Program
  {
    private static log4net.ILog CustomerLog = log4net.LogManager.GetLogger("CustomerLogging");
    private static log4net.ILog OrderLog = log4net.LogManager.GetLogger("OrderLogging");
    private static log4net.ILog DetailsLog = log4net.LogManager.GetLogger("OrderDetailLogging");


    static void Main(string[] args)
    {
      // array of embedded log4net config files
      string[] configs = { "Customer.config", "Order.config", "Detail.config"};

      foreach (var config in configs)
      {
        // build path to assembly config
        StringBuilder sb = new StringBuilder();
        sb.Append(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
        sb.Append(".");
        sb.Append(config);

        // convert to a stream
        Stream configStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(sb.ToString());

        // configure logger with ocnfig stream
        log4net.Config.XmlConfigurator.Configure(configStream);

        // test logging
        CustomerLog.Info("Begin logging with: " + config);
        OrderLog.Info("Begin logging with: " + config);
        DetailsLog.Info("Begin logging with: " + config);
        for (int iX = 0; iX < 10; iX++)
        {
          CustomerLog.Info("iX=" + iX);
          OrderLog.Info("iX=" + iX);
          DetailsLog.Info("iX=" + iX);
        }
        CustomerLog.Info("Ending logging with: " + config);
        OrderLog.Info("Ending logging with: " + config);
        DetailsLog.Info("Ending logging with: " + config);
      }

    }
  }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
利用系统资源;
使用System.IO;
名称空间加载器
{
班级计划
{
私有静态log4net.ILog CustomerLog=log4net.LogManager.GetLogger(“CustomerLogging”);
私有静态log4net.ILog OrderLog=log4net.LogManager.GetLogger(“OrderLogging”);
私有静态log4net.ILog DetailsLog=log4net.LogManager.GetLogger(“OrderDetailLogging”);
静态void Main(字符串[]参数)
{
//嵌入式log4net配置文件数组
字符串[]configs={“Customer.config”、“Order.config”、“Detail.config”};
foreach(配置中的变量配置)
{
//程序集配置的生成路径
StringBuilder sb=新的StringBuilder();
sb.Append(System.Reflection.Assembly.GetExecutionGassembly().GetName().Name);
某人加上(“.”);
某人追加(配置);
//转换成流
Stream configStream=System.Reflection.Assembly.GetExecutionGassembly().GetManifestResourceStream(sb.ToString());
//使用ocnfig流配置记录器
log4net.Config.XmlConfigurator.Configure(configStream);
//测试记录
CustomerLog.Info(“以“+config”开始日志记录);
Info(“以“+config”开始日志记录);
DetailsLog.Info(“以“+config”开始日志记录);
对于(int-iX=0;iX<10;iX++)
{
CustomerLog.Info(“iX=”+iX);
OrderLog.Info(“iX=“+iX”);
DetailsLog.Info(“iX=”+iX);
}
CustomerLog.Info(“以“+config”结束日志记录);
Info(“以“+config”结束日志记录);
DetailsLog.Info(“以“+config”结束日志记录);
}
}
}
}
我最后用了这个:

4小时后,摆弄配置,越来越沮丧

希望它能帮助别人。

有这样一个功能,用于以编程方式设置连接字符串:

// Get the Hierarchy object that organizes the loggers
log4net.Repository.Hierarchy.Hierarchy hier = 
  log4net.LogManager.GetLoggerRepository() as log4net.Repository.Hierarchy.Hierarchy;

if (hier != null)
{
  //get ADONetAppender
  log4net.Appender.ADONetAppender adoAppender = 
    (log4net.Appender.ADONetAppender)hier.GetLogger("MyProject",
      hier.LoggerFactory).GetAppender("ADONetAppender");
  if (adoAppender != null)
  {
    adoAppender.ConnectionString =
      System.Configuration.ConfigurationSettings.AppSettings["MyConnectionString"];
    adoAppender.ActivateOptions(); //refresh settings of appender
  }
}
更简洁的解决方案:

var layout = new PatternLayout("%-4timestamp [%thread] %-5level %logger %ndc - %message%newline");
var appender = new RollingFileAppender {
    File = "my.log",
    Layout = layout
};
layout.ActivateOptions();
appender.ActivateOptions();
BasicConfigurator.Configure(appender);
不要忘记调用方法:

设置配置属性后,必须在此对象上调用ActivateOptions方法。在调用ActivateOptions之前,此对象处于未定义状态,不得使用


晚会有点晚了。但这里有一个对我有效的最小配置

样本类

public class Bar
{
    private readonly ILog log = LogManager.GetLogger(typeof(Bar));
    public void DoBar() { log.Info("Logged"); }
}
最小log4net跟踪配置(NUnit测试内部)

打印到跟踪侦听器

Namespace+Bar: Logged

下面是一个简单的示例,演示了如何在没有任何
App.config
文件的情况下(甚至对于
Common.Logging
),完全在代码中创建和使用
AdoneAdapter
)。继续,删除它

这还有一个额外的好处,那就是可以重新调整
Namespace+Bar: Logged
CREATE TABLE [Log](
  [Id] [int] IDENTITY(1,1) NOT NULL,
  [Date] [datetime] NOT NULL,
  [Thread] [varchar](255) NOT NULL,
  [Level] [varchar](20) NOT NULL,
  [Source] [varchar](255) NOT NULL,
  [Message] [varchar](max) NOT NULL,
  [Exception] [varchar](max) NOT NULL
)
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Config
Imports log4net.Appender

Module Main
  Sub Main()
    Dim oLogger As ILog
    Dim sInput As String
    Dim iOops As Integer

    BasicConfigurator.Configure(New DbAppender)
    oLogger = LogManager.GetLogger(GetType(Main))

    Console.Write("Command: ")

    Do
      Try
        sInput = Console.ReadLine.Trim

        Select Case sInput.ToUpper
          Case "QUIT" : Exit Do
          Case "OOPS" : iOops = String.Empty
          Case Else : oLogger.Info(sInput)
        End Select

      Catch ex As Exception
        oLogger.Error(ex.Message, ex)

      End Try

      Console.Clear()
      Console.Write("Command: ")
    Loop
  End Sub
End Module
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Appender
Imports log4net.Repository.Hierarchy

Public Class DbAppender
  Inherits AdoNetAppender

  Public Sub New()
    MyBase.BufferSize = 1
    MyBase.CommandText = Me.CommandText

    Me.Parameters.ForEach(Sub(Parameter As DbParameter)
                            MyBase.AddParameter(Parameter)
                          End Sub)

    Me.ActivateOptions()
  End Sub



  Protected Overrides Function CreateConnection(ConnectionType As Type, ConnectionString As String) As IDbConnection
    Return MyBase.CreateConnection(GetType(System.Data.SqlClient.SqlConnection), "Data Source=(local);Initial Catalog=Logger;Persist Security Info=True;User ID=username;Password=password")
  End Function



  Private Overloads ReadOnly Property CommandText As String
    Get
      Dim _
        sColumns,
        sValues As String

      sColumns = Join(Me.Parameters.Select(Function(P As DbParameter) P.DbColumn).ToArray, ",")
      sValues = Join(Me.Parameters.Select(Function(P As DbParameter) P.ParameterName).ToArray, ",")

      Return String.Format(COMMAND_TEXT, sColumns, sValues)
    End Get
  End Property



  Private ReadOnly Property Parameters As List(Of DbParameter)
    Get
      Parameters = New List(Of DbParameter)
      Parameters.Add(Me.LogDate)
      Parameters.Add(Me.Thread)
      Parameters.Add(Me.Level)
      Parameters.Add(Me.Source)
      Parameters.Add(Me.Message)
      Parameters.Add(Me.Exception)
    End Get
  End Property



  Private ReadOnly Property LogDate As DbParameter
    Get
      Return New DbParameter("Date", DbType.Date, 0, New DbPatternLayout("%date{yyyy-MM-dd HH:mm:ss.fff}"))
    End Get
  End Property



  Private ReadOnly Property Thread As DbParameter
    Get
      Return New DbParameter("Thread", DbType.String, 255, New DbPatternLayout("%thread"))
    End Get
  End Property



  Private ReadOnly Property Level As DbParameter
    Get
      Return New DbParameter("Level", DbType.String, 50, New DbPatternLayout("%level"))
    End Get
  End Property



  Private ReadOnly Property Source As DbParameter
    Get
      Return New DbParameter("Source", DbType.String, 255, New DbPatternLayout("%logger.%M()"))
    End Get
  End Property



  Private ReadOnly Property Message As DbParameter
    Get
      Return New DbParameter("Message", DbType.String, 4000, New DbPatternLayout("%message"))
    End Get
  End Property



  Private ReadOnly Property Exception As DbParameter
    Get
      Return New DbParameter("Exception", DbType.String, 2000, New DbExceptionLayout)
    End Get
  End Property



  Private Const COMMAND_TEXT As String = "INSERT INTO Log ({0}) VALUES ({1})"
End Class
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Appender
Imports log4net.Repository.Hierarchy

Public Class DbParameter
  Inherits AdoNetAppenderParameter

  Private ReadOnly Name As String

  Public Sub New(Name As String, Type As DbType, Size As Integer, Layout As ILayout)
    With New RawLayoutConverter
      Me.Layout = .ConvertFrom(Layout)
    End With

    Me.Name = Name.Replace("@", String.Empty)
    Me.ParameterName = String.Format("@{0}", Me.Name)
    Me.DbType = Type
    Me.Size = Size
  End Sub



  Public ReadOnly Property DbColumn As String
    Get
      Return String.Format("[{0}]", Me.Name)
    End Get
  End Property
End Class
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Appender
Imports log4net.Repository.Hierarchy

Public Class DbPatternLayout
  Inherits PatternLayout

  Public Sub New(Pattern As String)
    Me.ConversionPattern = Pattern
    Me.ActivateOptions()
  End Sub
End Class
Imports log4net
Imports log4net.Core
Imports log4net.Layout
Imports log4net.Appender
Imports log4net.Repository.Hierarchy

Public Class DbExceptionLayout
  Inherits ExceptionLayout

  Public Sub New()
    Me.ActivateOptions()
  End Sub
End Class
Private Shared EscanerLog As log4net.ILog = log4net.LogManager.GetLogger("Log4Net.Config")

Public Sub New(ByVal sIDSesion As String)
    Dim sStream As Stream
    Dim JsText As String
    Using reader As New StreamReader((GetType(ClsGestorLogsTraza).Assembly).GetManifestResourceStream("Comun.Log4Net.Config"))
        JsText = reader.ReadToEnd()
        sStream = GenerateStreamFromString(JsText)
        log4net.Config.XmlConfigurator.Configure(sStream)
    End Using
End Sub

Public Function GenerateStreamFromString(ByVal s As String) As Stream
    Dim stream = New MemoryStream()
    Dim writer = New StreamWriter(stream)
    writer.Write(s)
    writer.Flush()
    stream.Position = 0
    Return stream
End Function

Public Function StreamFromResource(ByVal sFilename As String) As Stream
    Dim nAssembly As System.Reflection.Assembly = System.Reflection.Assembly.GetExecutingAssembly()
    Dim s As Stream = nAssembly.GetManifestResourceStream(System.Reflection.MethodBase.GetCurrentMethod.DeclaringType, sFilename)
    Return s
End Function