контекст контекст на уровне регистратора - PullRequest
2 голосов
/ 14 июля 2011

Есть ли способ установить значение свойства контекста в log4net на уровне регистратора?У нас есть области видимости в контексте потока, глобальном контексте и так далее.Мне было интересно, если есть способ установить переменную контекста на уровне экземпляра регистратора?

Я знаю, что такой вещи не существует, но, чтобы подчеркнуть, это было бы похоже на

 private static ILog _Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

_Log.LoggerContext.Properties["myVar"] = "someValue";
//now every log with this logger will use somevalue for the myVar property.

Есть ли способ сделать такую ​​вещь?

1 Ответ

2 голосов
/ 19 июля 2011

Насколько я знаю, такая возможность не существует в log4net (или NLog в этом отношении). У меня есть идея, которая должна работать. Я не знаю, хорошая ли это идея или нет, я оставлю это на ваше усмотрение ...

Вкратце, вы могли бы написать собственный PatternLayoutConverter ( см. Этот пост для одного примера того, как это сделать ). Этот конвертер будет искать «контекст» в вашем собственном статическом словаре (аналогично контекстам статического словаря, которые уже есть у log4net). «Контекст» будет храниться по имени регистратора. Значение в словаре будет другой словарь, который будет содержать ваши переменные.

Это немного более сложный процесс, чем я сейчас собираюсь затронуть, но я постараюсь дать хороший псевдокод, чтобы показать, как он может работать ...

UPDATE:

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

(Кажется, форматирование кода не учитывает отступы).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using log4net;
using log4net.Util;
using log4net.Layout.Pattern;

using log4net.Core;

namespace Log4NetTest
{
  //
  // Context container for loggers.
  // Indexable by logger or logger name.
  //
  public interface IContext
  {
    IContextProperties this [ILog logger] { get; }
    IContextProperties this [string name] { get; }
  }

  //
  // Context properties for a specific logger.
  //
  public interface IContextProperties
  {
    object this [string key] { get; set; }
    void Remove( string key );
    void Clear( );
  }

  //
  // Static class exposing the logger context container.
  //
  public static class LoggerProperties
  {
    private static readonly IContext context = new LoggerContext();

    public static IContext Properties { get { return context; } }
  }

  internal class LoggerContext : IContext
  {
    private readonly IDictionary<string, IContextProperties> dict = new Dictionary<string, IContextProperties>();

    #region IContext Members

    //
    // Get the properties asociated with this logger instance.
    //
    public IContextProperties this [ILog logger]
    {
      get
      {
        ILoggerWrapper w = logger as ILoggerWrapper;
        ILogger i = w.Logger;

        return this[i.Name];
      }
    }

    //
    // Get the properties associated with this logger name.
    //
    public IContextProperties this [string name]
    {
      get 
      {
        lock (dict)
        {
          IContextProperties props;
          if ( dict.TryGetValue( name, out props ) ) return props;
          props = new LoggerContextProperties();
          dict [name] = props;
          return props;
        }
      }
    }

    #endregion
  }

  //
  // Implementation of the logger instance properties.
  //
  internal class LoggerContextProperties : IContextProperties
  {
    private readonly IDictionary<string, object> loggerProperties = new Dictionary<string, object>();

    #region IContextProperties Members

    public object this [string key]
    {
      get
      {
        lock ( loggerProperties )
        {
          object value;
          if ( loggerProperties.TryGetValue( key, out value ) ) return value;
          return null;
        }
      }
      set
      {
        lock ( loggerProperties )
        {
          loggerProperties [key] = value;
        }
      }
    }

    public void Remove( string key )
    {
      lock ( loggerProperties )
      {
        loggerProperties.Remove( key );
      }
    }

    public void Clear( )
    {
      lock ( loggerProperties )
      {
        loggerProperties.Clear();
      }
    }

    #endregion
  }

  public class LoggerContextPropertiesPatternConverter : PatternLayoutConverter
  {
    protected override void Convert( System.IO.TextWriter writer, LoggingEvent loggingEvent )
    {
      IContextProperties props = LoggerProperties.Properties[loggingEvent.LoggerName];
      object value = props[Option];
      if (value != null)
      {
        writer.Write(value);
      }
      else
      {
        writer.Write("{0}.{1} has no value", loggingEvent.LoggerName, Option);
      }
    }
  }
}

Сконфигурируйте appender для использования PatternLayoutConverter:

<appender name="debug" type="log4net.Appender.DebugAppender">
  <layout type="log4net.Layout.PatternLayout">
    <param name="ConversionPattern" value="%d [%t] %logger %-5p [LOGPROP = %LOGPROP{test}] %m%n"/>
    <converter>
      <name value="LOGPROP" />
      <type value="Log4NetTest.LoggerContextPropertiesPatternConverter" />
    </converter>
  </layout>
</appender>

Как установить свойства для логгера:

  ILog loga = LogManager.GetLogger("A");
  ILog logb = LogManager.GetLogger("B");
  ILog logc = LogManager.GetLogger("C");

  LoggerProperties.Properties[loga]["test"] = "abc";
  LoggerProperties.Properties[logb]["test"] = "def";
  LoggerProperties.Properties[logc]["test"] = "ghi";

  loga.Debug("Hello from A");
  logb.Debug("Hello from B");
  logc.Debug("Hello from C");

Выход:

A: 2011-07-19 10:17:07,932 [1] A DEBUG [LOGPROP = abc] Hello from A
B: 2011-07-19 10:17:07,963 [1] B DEBUG [LOGPROP = def] Hello from B
C: 2011-07-19 10:17:07,963 [1] C DEBUG [LOGPROP = ghi] Hello from C

Удачи!

...