Come configurare log4net a livello di codice da zero (nessuna configurazione)


87

Questa è una cattiva idea, lo so, ma ... voglio configurare log4net a livello di programmazione da zero senza alcun file di configurazione. Sto lavorando a una semplice applicazione di registrazione per me e il mio team da utilizzare per un gruppo di applicazioni dipartimentali relativamente piccole di cui siamo responsabili. Voglio che accedano tutti allo stesso database. L'applicazione di registrazione è solo un wrapper per log4net con AdoNetAppender preconfigurato.

Tutte le applicazioni sono distribuite con ClickOnce, il che presenta un piccolo problema con la distribuzione del file di configurazione. Se il file di configurazione facesse parte del progetto principale, potrei impostare le sue proprietà da distribuire con l'assembly. Ma fa parte di un'applicazione collegata, quindi non ho la possibilità di distribuirlo con l'applicazione principale. (Se non è vero, qualcuno me lo faccia sapere).

Probabilmente perché è una cattiva idea, non sembra esserci molto codice di esempio disponibile per la configurazione programmatica di log4net da zero. Ecco cosa ho finora.

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

Dopo aver configurato tutti i parametri per apndr, all'inizio ho provato questo ...

Dim hier As Hierarchy = DirectCast(LogManager.GetRepository(), Hierarchy)
hier.Root.AddAppender(apndr)

Non ha funzionato. Poi, come scatto al buio, ho provato questo invece.

BasicConfigurator.Configure(apndr)

Neanche questo ha funzionato. Qualcuno ha dei buoni riferimenti su come configurare log4net a livello di programmazione da zero senza file di configurazione?


Risposte:


37

Un modo in cui l'ho fatto in passato è includere il file di configurazione come risorsa incorporata e ho usato solo log4net.Config.Configure (Stream) .

In questo modo, potevo utilizzare la sintassi di configurazione con cui avevo familiarità e non dovevo preoccuparmi di ottenere un file distribuito.


2
Il nome completo del metodo è log4net.Config.XmlConfigurator.Configure (come nel collegamento)
olorin

122

Ecco una classe di esempio che crea la configurazione di log4net completamente nel codice. Dovrei menzionare che la creazione di un logger tramite un metodo statico è generalmente considerata negativa, ma nel mio contesto, questo è ciò che volevo. Indipendentemente da ciò, puoi ritagliare il codice per soddisfare le tue esigenze.

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 da parte mia, sembra che tu abbia la risposta qui su come farlo in modo puramente programmatico senza file di configurazione.
Wil P

beh, continua a non funzionare, viene creato un file di testo vuoto, tuttavia non viene scritto nulla :(
Ivan G.

8
+1 per hierarchy.Configured = true;cui fa il trucco per me
Firo

1
Il trucco per me era roller.ActivateOptions () ... Some dark Voodoo.
Asaf

1
@Legends "dnsservices.txt" è solo un nome relativo per il file di registro. Sembra essere relativo alla directory di lavoro corrente. L'ho cambiato in un percorso assoluto sul sistema dell'utente in modo che i registri andassero sempre a una directory nota.
Colm Bhandal

32

Soluzione più concisa:

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

Non dimenticare di chiamare il metodo ActivateOptions :

Il metodo ActivateOptions deve essere chiamato su questo oggetto dopo aver impostato le proprietà di configurazione. Fino a quando ActivateOptions non viene chiamato, questo oggetto è in uno stato indefinito e non deve essere utilizzato.


L'uso dell'overload di BasicConfigurator.Configure (IAppender) consente di risparmiare un sacco di problemi, applausi.
Shaun

1
+1 per quello. La chiamata ActivateOptions()è decisamente mancante o almeno non è stata indicata abbastanza nei documenti.
fbmd

5

Come dice Jonathan , l'utilizzo di una risorsa è una buona soluzione.

È un po 'restrittivo in quanto il contenuto della risorsa incorporata verrà corretto in fase di compilazione. Ho un componente di registrazione che genera un XmlDocument con una configurazione Log4Net di base, utilizzando variabili definite come appSettings (ad esempio nome file per RollingFileAppender, livello di registrazione predefinito, forse nome stringa di connessione se si desidera utilizzare un AdoNetAppender). E poi chiamo log4net.Config.XmlConfigurator.Configureper configurare Log4Net utilizzando l'elemento radice del XmlDocument generato.

Quindi gli amministratori possono personalizzare la configurazione "standard" modificando alcune impostazioni di app (in genere livello, nome file, ...) o possono specificare un file di configurazione esterno per ottenere un maggiore controllo.


3

Non posso dire nello snippet di codice della domanda se "'E così via ..." include l'importantissimo apndr.ActivateOptions () che è indicato nella risposta di Todd Stout. Senza ActivateOptions () l'Appender è inattivo e non farà nulla che possa spiegare il motivo per cui non funziona.


Non credo di averlo avuto lì dentro. Questo potrebbe essere stato il problema. Grazie.
John M Gant

3

Un po 'tardi per la festa. Ma ecco una configurazione minima che ha funzionato per me.

Classe di esempio

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

Configurazione minima della traccia log4net (all'interno del test 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();
}

Stampa al listener di traccia

Namespace+Bar: Logged

2
Quasi funziona, ma avevo bisogno di chiamare .ActiveOptions su PatternLayout e Appender prima che funzionasse completamente.
cjb110

Non so perché. Per me ha funzionato così com'è, forse abbiamo usato versioni diverse.
oleksii

2

Dr. Netjes ha questo per impostare la connectiontring a livello di programmazione:

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

// Ho incorporato tre file di configurazione come una risorsa incorporata e accedervi in ​​questo modo:

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

È strano che BasicConfigurator.Configure(apndr)non abbia funzionato. Nel mio caso ha fatto il suo lavoro ... Ma, comunque, ecco la risposta: avresti dovuto scrivere hier.Configured = true;(codice c #) dopo aver terminato tutte le impostazioni.



0

Ecco un esempio semplicissimo di come creare e utilizzare un AdoNetAdaptercodice interamente incluso, completamente in assenza di App.configfile (nemmeno per Common.Logging). Vai avanti, cancellalo!

Questo ha l'ulteriore vantaggio di essere resiliente contro gli aggiornamenti secondo le nuove convenzioni di denominazione , dove il nome dell'assembly ora riflette la versione. ( Common.Logging.Log4Net1213, ecc.)

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

[Principale]

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

'Soluzione per 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.