6

我目前正在将一个大型项目转移到使用 Common.Logging 并且我希望能够将日志记录事件输出到多个第 3 方日志记录实现。

我们目前有一个内部开发的跟踪库,我们希望继续使用它来跟踪和调试消息。我还想开始使用 log4net 将一些消息发送到数据库以进行报告或在某些级别发送电子邮件通知。

我正在寻找的是这样的:

<common>
<logging>
  <factoryAdapter type="CustomTraceFactoryAdapter, MyAssembly">
    <arg key="configType" value="INLINE"/>
  </factoryAdapter>
  <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net">
    <arg key="configType" value="INLINE"/>
  </factoryAdapter>
</logging>
</common>

有没有办法使用开箱即用的配置选项来做到这一点?

4

3 回答 3

5

AFAIK,Common.Logging 中没有任何内容可以满足您的需求。这里有几个选择。

1) 开发几个自定义记录器工厂适配器,如http://netcommon.sourceforge.net/docs/2.1.0/reference/html/ch01.html#logging-advanced-customfactoryaadapter所述。您似乎已经开发了一个自定义工厂,用于您的内部跟踪库。第二个自定义工厂将通过组合来自一个或多个其他工厂的记录器来创建复合记录器对象。

2) 为您的跟踪库开发自定义 log4net appender ( http://logging.apache.org/log4net/release/faq.html#custom-appender ) 并将 Common.Logging 配置为仅使用 log4net 工厂。

我觉得第二个选项最简单,因为它不涉及更改 Common.Logging 的配置行为,因此它可以从其他配置的工厂构成复合工厂。

于 2012-07-06T13:04:25.417 回答
4

现在有一种内置的方式来支持多个适配器,请查看Common.Logging.MultipleLogger这里有一个例子App.config说明如何使用它。

于 2017-10-20T21:41:26.853 回答
4

我知道这个问题很老了,但显然仍然没有开箱即用的解决方案,所以这里有一个适合我的自定义记录器(灵感来自https://github.com/ramonsmits/common-logging但我最终几乎改变了一切)。使用 Common.Logging 3.1.0 版测试。

工厂适配器

/// <summary>
/// Adapter hub for Common.Logging that can send logs to multiple other adapters
/// </summary>
public class MultiLoggerFactoryAdapter : AbstractCachingLoggerFactoryAdapter
{
    private readonly List<ILoggerFactoryAdapter> LoggerFactoryAdapters;

    /// <summary>
    /// Initializes a new instance of the <see cref="MultiFactoryLoggerFactoryAdapter"/> class.
    /// </summary>
    public MultiLoggerFactoryAdapter(CommonLogging.Configuration.NameValueCollection properties)
    {
        LoggerFactoryAdapters = new List<ILoggerFactoryAdapter>();

        foreach(var factoryAdapter in properties.Where(e => e.Key.EndsWith(".factoryAdapter")))
        {
            string adapterName = factoryAdapter.Key.Substring(0, factoryAdapter.Key.Length - 15);
            string adapterType = factoryAdapter.Value;

            var adapterConfig = new CommonLogging.Configuration.NameValueCollection();
            foreach(var entry in properties.Where(e1 => e1.Key.StartsWith(adapterName + ".")))
            {
                adapterConfig.Add(entry.Key.Substring(adapterName.Length + 1), entry.Value);
            }

            var adapter = (ILoggerFactoryAdapter)Activator.CreateInstance(Type.GetType(adapterType), adapterConfig);
            LoggerFactoryAdapters.Add(adapter);
        }
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="MultiFactoryLoggerFactoryAdapter"/> class.
    /// </summary>
    /// <param name="factoryAdapters">The factory adapters.</param>
    public MultiLoggerFactoryAdapter(List<ILoggerFactoryAdapter> factoryAdapters)
    {
        LoggerFactoryAdapters = factoryAdapters;
    }

    protected override ILog CreateLogger(string name)
    {
        var loggers = new List<ILog>(LoggerFactoryAdapters.Count);

        foreach (var f in LoggerFactoryAdapters)
        {
            loggers.Add(f.GetLogger(name));
        }

        return new MultiLogger(loggers);
    }
}

记录器

/// <summary>
/// Adapter hub for Common.Logging that can send logs to multiple other adapters
/// </summary>
public class MultiLogger : AbstractLogger
{
    private readonly List<ILog> Loggers;

    public static readonly IDictionary<LogLevel, Action<ILog, object, Exception>> LogActions = new Dictionary<LogLevel, Action<ILog, object, Exception>>()
    {
        { LogLevel.Debug, (logger, message, exception) => logger.Debug(message, exception) },
        { LogLevel.Error, (logger, message, exception) => logger.Error(message, exception) },
        { LogLevel.Fatal, (logger, message, exception) => logger.Fatal(message, exception) },
        { LogLevel.Info, (logger, message, exception) => logger.Info(message, exception) },
        { LogLevel.Trace, (logger, message, exception) => logger.Trace(message, exception) },
        { LogLevel.Warn, (logger, message, exception) => logger.Warn(message, exception) },
    };

    /// <summary>
    /// Initializes a new instance of the <see cref="MultiLogger"/> class.
    /// </summary>
    /// <param name="loggers">The loggers.</param>
    public MultiLogger(List<ILog> loggers)
    {
        Loggers = loggers;
    }

    public override bool IsDebugEnabled { get { return Loggers.Any(l => l.IsDebugEnabled); } }
    public override bool IsErrorEnabled { get { return Loggers.Any(l => l.IsErrorEnabled); } }
    public override bool IsFatalEnabled { get { return Loggers.Any(l => l.IsFatalEnabled); } }
    public override bool IsInfoEnabled { get { return Loggers.Any(l => l.IsInfoEnabled); } }
    public override bool IsTraceEnabled { get { return Loggers.Any(l => l.IsTraceEnabled); } }
    public override bool IsWarnEnabled { get { return Loggers.Any(l => l.IsWarnEnabled); } }

    protected override void WriteInternal(LogLevel level, object message, Exception exception)
    {
        List<Exception> exceptions = null;
        foreach(var logger in Loggers)
        {
            try
            {
                LogActions[level](logger, message, exception);
            }
            catch(Exception e)
            {
                if(exceptions == null)
                    exceptions = new List<Exception>();
                exceptions.Add(e);
            }
        }

        if(exceptions != null)
            throw new AggregateException("One or more exceptions occured while forwarding log message to multiple loggers", exceptions);
    }
}

你可以像这样配置它:

<common>
  <logging>
    <factoryAdapter type="MultiLoggerFactoryAdapter, YourAssemblyName">
      <arg key="Log4Net.factoryAdapter" value="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net1213" />
      <arg key="Log4Net.configType" value="INLINE" />

      <arg key="Debug.factoryAdapter" value="Common.Logging.Simple.TraceLoggerFactoryAdapter, Common.Logging" />
    </factoryAdapter>
  </logging>
</common>

也就是说,对于每个记录器,您添加一行 key LoggerName.factoryAdapter,然后您可以为该记录器添加属性,方法是为该键使用相同的名称,例如LoggerName.someProperty.

于 2015-12-21T13:22:44.593 回答