【问题标题】:context scope at logger level记录器级别的上下文范围
【发布时间】:2011-07-19 23:26:56
【问题描述】:

有没有办法在记录器级别的 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.

有没有办法做到这一点?

【问题讨论】:

    标签: log4net log4net-configuration


    【解决方案1】:

    据我所知,log4net(或 NLog)中不存在该功能。我确实有一个应该可行的想法。我不知道这是否是一个“好”的想法,我会让你决定......

    简而言之,您可以编写一个自定义 PatternLayoutConverter (see this post for one example of how to do this)。此转换器将在您自己的静态字典中查找“上下文”(类似于 log4net 已经拥有的静态字典上下文)。 “上下文”将按记录器名称存储。字典中的值将是 另一个 字典,它将保存您的变量。

    这比我现在准备要涉及的要多一点,但我会尝试提供一些好的伪代码来展示它是如何工作的......

    更新:

    我添加了一个有效的实现(至少在我所做的最小测试中)。我已经定义了一个“上下文”来为每个记录器保存一个属性包。我还实现了 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
    

    祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-21
      相关资源
      最近更新 更多