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

Posted

技术标签:

【中文标题】如何从头开始以编程方式配置 log4net(无配置)【英文标题】:How to configure log4net programmatically from scratch (no config) 【发布时间】:2010-10-20 16:15:21 【问题描述】:

我知道这是个坏主意,但是... 我想在没有配置文件的情况下从头开始以编程方式配置 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 的好的参考资料?

【问题讨论】:

另见***.com/questions/1436713/… 【参考方案1】:

这是一个完全在代码中创建 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");
    

【讨论】:

+1 来自我,您似乎在这里得到了如何在没有配置文件的情况下纯粹以编程方式执行此操作的答案。 好吧,它仍然不起作用,创建了空文本文件,但是没有写入任何内容:( +1 for hierarchy.Configured = true; 这对我有用 对我来说诀窍是roller.ActivateOptions()...一些黑暗的巫毒教。 @Legends "dnsservices.txt" 只是您的日志文件的相对名称。似乎与当前工作目录有关。我将其更改为用户系统上的绝对路径,因此日志将始终转到已知目录。【参考方案2】:

我过去这样做的一种方法是将配置文件作为嵌入式资源包含在内,并且只使用了log4net.Config.Configure(Stream)。

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

【讨论】:

完整的方法名称是 log4net.Config.XmlConfigurator.Configure(如链接)【参考方案3】:

更简洁的解决方案:

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 方法。在调用 ActivateOptions 之前,此对象处于未定义状态,不得使用。

【讨论】:

使用 BasicConfigurator.Configure(IAppender) 重载确实可以省去很多麻烦,干杯。 +1 为那个。调用 ActivateOptions() 肯定是缺失的,或者至少在文档中没有得到足够的指出。【参考方案4】:

正如Jonathan 所说,使用资源是一个很好的解决方案。

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

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

【讨论】:

【参考方案5】:

我无法在问题的代码 sn-p 中判断“'等等...”是否包含非常重要的 apndr.ActivateOptions() ,这在 Todd Stout 的回答中有所指示。如果没有 ActivateOptions(),Appender 将处于非活动状态,并且不会做任何可以解释其失败原因的事情。

【讨论】:

我不认为我有那个。这可能是问题所在。谢谢。【参考方案6】:

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

示例类

public class Bar

    private readonly ILog log = LogManager.GetLogger(typeof(Bar));
    public void DoBar()  log.Info("Logged"); 

最小的 log4net 跟踪配置(在 NUnit 测试中)

[Test]
public void Foo()

    var tracer = new TraceAppender();
    var hierarchy = (Hierarchy)LogManager.GetRepository();
    hierarchy.Root.AddAppender(tracer);
    var patternLayout = new PatternLayout ConversionPattern = "%m%n";
    tracer.Layout = patternLayout;
    hierarchy.Configured = true;

    var bar = new Bar();
    bar.DoBar();

打印到跟踪侦听器

Namespace+Bar: Logged

【讨论】:

这几乎可以工作,但我需要在 PatternLayout 和 Appender 上调用 .ActiveOptions 才能完全工作。 不知道为什么。它对我有用,也许我们使用了不同的版本。【参考方案7】:

Dr. Netjes 有这个以编程方式设置连接字符串:

// 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
  

【讨论】:

【参考方案8】:

// 我已将三个配置文件嵌入为嵌入式资源并像这样访问它们:

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);
      

    
  

【讨论】:

【参考方案9】:

奇怪的是BasicConfigurator.Configure(apndr) 不起作用。在我的情况下,它完成了它的工作......但是,无论如何,答案就在这里 - 你应该在完成所有设置后写hier.Configured = true;(c#代码)。

【讨论】:

【参考方案10】:

我最终使用了这个:

http://www.mikebevers.be/blog/2010/09/log4net-custom-adonetappender/

经过 4 个小时的修改配置并逐渐变得更加沮丧。

希望对某人有所帮助。

【讨论】:

【参考方案11】:

这是一个简单明了的示例,说明如何完全在代码中创建和使用AdoNetAdapter,完全没有任何App.config 文件(甚至Common.Logging 也不行)。来吧,删除它!

这还有一个额外的好处是可以抵御new naming conventions 下的更新,其中程序集名称现在反映了版本。 (Common.Logging.Log4Net1213等)

[SQL]

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

[DbAppender]

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("%dateyyyy-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

[DbPatternLayout]

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

[DbExceptionLayout]

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

【讨论】:

【参考方案12】:

'Vb.Net 的解决方案

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

【讨论】:

以上是关于如何从头开始以编程方式配置 log4net(无配置)的主要内容,如果未能解决你的问题,请参考以下文章

通过 log4net 可配置的敏感数据屏蔽

如何以编程方式强制 log4net 释放日志文件以便可以访问它?

如何在 laravel 中以编程方式即时设置 .env 值

如何配置 RollingFileAppender 以使用 log4net 按日期和大小滚动?

有没有办法以可编程方式刷新 log4net 中的缓冲区

如何配置log4net以便log.IsDebugEnabled为true