Как настроить log4net программно с нуля (без настроек) - PullRequest
83 голосов
/ 21 апреля 2009

Это плохая идея, я знаю, но ... Я хочу настроить 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 с нуля без файла конфигурации?

Ответы [ 12 ]

119 голосов
/ 21 ноября 2009

Вот пример класса, который полностью создает конфигурацию 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");
    }
}

}

37 голосов
/ 21 апреля 2009

Один способ, которым я занимался в прошлом, - это включить файл конфигурации в качестве встроенного ресурса и просто использовать log4net.Config.Configure (Stream) .

Таким образом, я мог использовать синтаксис конфигурации, с которым я был знаком, и мне не пришлось беспокоиться о развертывании файла.

29 голосов
/ 27 сентября 2012

Более краткое решение:

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 этот объект находится в неопределенном состоянии и не должен использоваться.

5 голосов
/ 21 апреля 2009

Как говорит Джонатан , использование ресурса - хорошее решение.

Это немного ограничивает то, что содержимое встроенного ресурса будет исправлено во время компиляции. У меня есть компонент ведения журнала, который генерирует XmlDocument с базовой конфигурацией Log4Net, используя переменные, определенные как appSettings (например, имя файла для RollingFileAppender, уровень ведения журнала по умолчанию, возможно, имя строки подключения, если вы хотите использовать AdoNetAppender). И тогда я вызываю log4net.Config.XmlConfigurator.Configure для настройки Log4Net с использованием корневого элемента сгенерированного XmlDocument.

Затем администраторы могут настроить «стандартную» конфигурацию, изменив несколько настроек приложения (обычно уровень, имя файла, ...) или указав внешний файл конфигурации, чтобы получить больше контроля.

3 голосов
/ 10 апреля 2013

Немного опоздал на вечеринку. Но вот минимальный конфиг, который работал для меня.

Образец класса

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
3 голосов
/ 04 декабря 2009

Я не могу сказать в фрагменте кода вопроса, включает ли "'И так далее ..." очень важный apndr.ActivateOptions (), который указан в ответе Тодда Стаута. Без ActivateOptions () Appender неактивен и не будет делать ничего, что могло бы объяснить причину сбоя.

2 голосов
/ 17 ноября 2011

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 голос
/ 12 августа 2011

// Я встроил три файла конфигурации в качестве встроенного ресурса и имею к ним доступ так:

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 голосов
/ 14 сентября 2018

'Решение для 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
0 голосов
/ 22 февраля 2015

Вот простой пример того, как вы можете создавать и использовать 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
)

[Main]

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