如何从头开始以编程方式配置log4net(没有配置文件)

90

我知道这是个不好的想法,但是... 我想从头开始以编程方式配置log4net而没有配置文件。我正在为我和我的团队开发一个简单的日志应用程序,用于一堆相对较小的部门应用程序。我希望它们都记录到同一个数据库中。这个日志应用程序只是一个预先配置了AdoNetAppender的log4net包装器。

所有应用程序都是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的好参考资料?


请参见https://dev59.com/JnM_5IYBdhLWcg3wTRPL。 - Pavel Chuchuva
12个回答

125

这里有一个示例类,完全使用代码创建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");
    }
}

}


6
+1,看起来你已经找到了纯编程实现而无需配置文件的答案。 - Wil P
8
+hierarchy.Configured = true; 这一行代码对我来说就足够了。 - Firo
1
对我来说,关键是roller.ActivateOptions()... 有些黑暗的巫术。 - Asaf
如果您想将不同类型的配置合并为一种类型的配置,这非常有帮助。 - Wilko van der Veen
1
@Legends "dnsservices.txt" 只是您的日志文件的相对名称。似乎是相对于当前工作目录。我已将其更改为用户系统上的绝对路径,以便日志始终进入已知的目录。 - Colm Bhandal
显示剩余4条评论

38

我过去处理这个问题的一种方法是将配置文件作为嵌入式资源包含,并且只使用log4net.Config.Configure(Stream)

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


2
完整的方法名是log4net.Config.XmlConfigurator.Configure(如链接中所示) - olorin

33

更简洁的解决方案:

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)重载确实可以节省很多麻烦,谢谢。 - Shaun
1
+1。在文档中明确指出调用“ActivateOptions()”肯定是缺失的或者至少没有指出来。 - fbmd

5

有点晚了,但这里是我使用的最小配置。

示例类:

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

2
那差不多可以了,但在完全生效之前,我需要在PatternLayout和Appender上调用.ActiveOptions。 - cjb110
不确定为什么。对我来说它是可行的,也许我们使用了不同的版本。 - oleksii

5
正如Jonathan所说,使用资源是一个好的解决方案。
它有点受限,因为嵌入式资源内容将在编译时固定。我有一个日志记录组件,它生成一个带有基本Log4Net配置的XmlDocument,使用定义为appSettings的变量(例如RollingFileAppender的文件名、默认记录级别,也许是连接字符串名称,如果您想使用AdoNetAppender)。然后我调用log4net.Config.XmlConfigurator.Configure来使用生成的XmlDocument的根元素配置Log4Net。
然后管理员可以通过修改一些appSettings(通常是级别、文件名等)来自定义“标准”配置,或者指定外部配置文件以获得更多控制。

3

我无法从问题的代码段中确定 "'等等..." 是否包括在 Todd Stout 的答案中指示的非常重要的 apndr.ActivateOptions() 中。 如果没有 ActivateOptions(),则 Appender 无效并且不会执行任何操作,这可能解释了为什么它失败。


我不认为我在那里有那个。那可能是问题的原因。谢谢。 - John M Gant

2

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

1

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

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

    }
  }
}

0

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


0

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接