Common.Logging с несколькими заводскими адаптерами

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

В настоящее время у нас есть внутренняя библиотека трассировки, которую мы хотели бы продолжать использовать для трассировки и отладки сообщений. Я также хотел бы начать использовать 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>

Есть ли способ сделать это с опциями конфигурации из коробки?

3 ответа

Решение

AFAIK, ничего не существует в Common.Logging для того, что вам нужно. Вот пара альтернатив.

1) Разработайте несколько пользовательских заводских адаптеров для логгеров, как описано по адресу http://netcommon.sourceforge.net/docs/2.1.0/reference/html/ch01.html. Одна пользовательская фабрика, которую вы, похоже, уже разработали, предназначена для вашей внутренней библиотеки трассировки. Вторая настраиваемая фабрика будет создавать составные объекты регистраторов, комбинируя регистраторы с одного или нескольких других заводов.

2) Разработайте настраиваемое приложение log4net ( http://logging.apache.org/log4net/release/faq.html) для своей библиотеки трассировки и настройте Common.Logging для использования только фабрики log4net.

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

Теперь есть встроенный способ поддержки нескольких адаптеров, ознакомьтесь с ht tps://www.nuget.org/packages/Common.Logging.MultipleLogger/. Есть пример App.config это показывает, как использовать это здесь.

Я знаю, что вопрос старый, но, видимо, до сих пор нет готового решения для этого, так что вот специальный регистратор, который работает для меня (вдохновлено https://github.com/ramonsmits/common-logging но я изменил почти все в конце). Протестировано с Common.Logging версии 3.1.0.

FactoryAdapter

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

То есть для каждого регистратора вы добавляете строку с ключом LoggerName.factoryAdapter, а затем вы можете добавить свойства для этого регистратора, используя то же имя для ключа, например, LoggerName.someProperty,

Другие вопросы по тегам