Як програмно налаштувати 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 з нуля, без конфігураційного файлу?


Відповіді:


37

Один із способів, що я це робив раніше, - це включити файл конфігурації як вбудований ресурс і щойно використав log4net.Config.Configure (Потік) .

Таким чином, я міг використати синтаксис конфігурації, який був мені знайомий, і мені не довелося турбуватися про те, щоб розгорнути файл.


2
Повна назва методу - log4net.Config.XmlConfigurator.Configure (як у посиланні)
olorin

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

ну, це все одно не працювало, створюється порожній текстовий файл, проте в нього нічого не написано :(
Іван Г.

8
+1, hierarchy.Configured = true;що робить для мене фокус
Фіро

1
Фокусом для мене був ролик.ActivateOptions () ... Якийсь темний Вуду.
Асаф,

1
@Legends "dnsservices.txt" - це лише відносна назва для вашого журналу. Здається, відносно поточного робочого каталогу. Я змінив його на абсолютний шлях в системі користувача, щоб журнали завжди надходили до відомого каталогу.
Колм Бхандал,

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

Як Джонатан говорить , використання ресурсу є хорошим рішенням.

Це трохи обмежує тим, що вміст вбудованого ресурсу буде виправлено під час компіляції. У мене є компонент журналювання, який генерує XmlDocument з базовою конфігурацією Log4Net, використовуючи змінні, визначені як appSettings (наприклад, ім'я файлу для RollingFileAppender, рівень реєстрації за замовчуванням, можливо, ім'я рядка підключення, якщо ви хочете використовувати AdoNetAppender). А потім дзвонюlog4net.Config.XmlConfigurator.Configure налаштувати Log4Net, використовуючи кореневий елемент сформованого XmlDocument.

Тоді адміністратори можуть налаштувати "стандартну" конфігурацію, змінивши кілька Налаштувань програми (як правило, рівень, ім'я файлу, ...) або можуть вказати зовнішній файл конфігурації, щоб отримати більше контролю.


3

Я не можу сказати у фрагменті коду запитання, чи "" І так далі ... "включає дуже важливий apndr.ActivateOptions (), який вказаний у відповіді Тодда Стаута. Без ActivateOptions () Appender неактивний і не буде робити нічого, що могло б пояснити, чому він не працює.


Я не думаю, що у мене там було таке. Можливо, в цьому була проблема. Дякую.
Джон М Гант

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
Це майже працює, але мені потрібно було зателефонувати .ActiveOptions на PatternLayout і Appender, перш ніж він буде повністю працювати.
cjb110

Не знаю, чому. У мене це спрацювало як є, можливо, ми використовували різні версії.
oleksii

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

[Параметр Db]

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.