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


87

我知道这是个坏主意,但是...我想从头开始以编程方式配置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有很好的参考吗?


Answers:



122

这是一个示例类,可通过代码完全创建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

好吧,它仍然不起作用,创建了一个空文本文件,但是没有写入任何内容:(
Ivan G.11年

8
+1hierarchy.Configured = true;帮了我
大忙

1
对我来说,诀窍是roller.ActivateOptions()...一些黑暗的伏都教徒。
Asaf 2015年

1
@Legends“ dnsservices.txt”只是您的日志文件的相对名称。似乎相对于当前工作目录。我将其更改为用户系统上的绝对路径,因此日志将始终转到已知目录。
Colm Bhandal

32

更简洁的解决方案:

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
+1。呼叫ActivateOptions()肯定丢失,或者至少在文档中没有足够指出。
fbmd

5

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

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

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


3

我无法在问题的代码片段中分辨出“等等”是否包含非常重要的apndr.ActivateOptions(),这在Todd Stout的答案中已指明。如果没有ActivateOptions(),Appender将处于非活动状态,并且不会做任何可以解释其失败原因的事情。


我不认为我在那里。那可能就是问题所在。谢谢。
John M Gant

3

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

样品课

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 2013年

2

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

这是一个无所不包的示例,说明了如何AdoNetAdapter在没有任何App.config文件的情况下(完全没有Common.Logging)。继续,删除它!

这具有额外的好处,即可以根据新的命名约定对更新进行恢复,其中的程序集名称现在可以反映版本。(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("%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

[DbParameter]

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

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.