Как выглядит класс Wrapper Log4Net?

Я искал фреймворк для .net(С#) и решил дать log4net перейти после прочтения нескольких вопросов/ответов в потоке stackoverflow. Я вижу, что люди снова и снова упоминают, что используют класс оболочки для log4net, и мне интересно, как это будет выглядеть.

У меня есть мой код, разделенный на разные проекты (доступ к данным/бизнес/webservice/..). Как выглядит класс оболочки log4net? Будет ли класс-обертка включен во все проекты? Должен ли я объединять его как отдельный проект?

Должна ли оболочка быть одноэлементным классом?

Ответ 1

По существу вы создаете интерфейс, а затем конкретную реализацию этого интерфейса, который напрямую переносит классы и методы Log4net. Дополнительные системы регистрации можно обернуть, создав более конкретные классы, которые объединяют другие классы и методы этих систем. Наконец, используйте factory для создания экземпляров ваших оберток на основе настроек конфигурации или строки изменения кода. (Примечание: вы можете стать более гибким и сложным - используя контейнер Inversion of Control, например StructureMap.)

public interface ILogger
{
    void Debug(object message);
    bool IsDebugEnabled { get; }

    // continue for all methods like Error, Fatal ...
}

public class Log4NetWrapper : ILogger
{
    private readonly log4net.ILog _logger;

    public Log4NetWrapper(Type type)
    {
        _logger = log4net.LogManager.GetLogger(type);
    }

    public void Debug(object message)
    {
        _logger.Debug(message);
    }

    public bool IsDebugEnabled
    {
        get { return _logger.IsDebugEnabled; }
    }

    // complete ILogger interface implementation
}

public static class LogManager
{
    public static ILogger GetLogger(Type type)
    {
        // if configuration file says log4net...
        return new Log4NetWrapper(type);
        // if it says Joe Logger...
        // return new JoesLoggerWrapper(type);
    }
}

И пример использования этого кода в ваших классах (объявлен как статическое поле readonly):

private static readonly ILogger _logger =
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

Вы можете получить тот же эффект, который отличается большей производительностью:

private static readonly ILogger _logger = 
    LogManager.GetLogger(typeof(YourTypeName));

Первый пример считается более ремонтопригодным.

Вы не хотите создавать Singleton для обработки всех протоколов, поскольку журналы Log4Net регистрируются для вызывающего типа; его намного более чистый и полезный, чтобы каждый тип использовал свой собственный регистратор, а не просто видел один тип в файле журнала, сообщающий обо всех сообщениях.

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

Ответ 2

Предполагая, что вы столкнулись с чем-то вроде ответа cfeduke выше, вы также можете добавить перегрузку к LogManager следующим образом:

public static ILogger GetLogger()
{
    var stack = new StackTrace();
    var frame = stack.GetFrame(1);
    return new Log4NetWrapper(frame.GetMethod().DeclaringType);
}

Таким образом, в вашем коде вы можете просто использовать:

private static readonly ILogger _logger = LogManager.GetLogger();

вместо любого из них:

private static readonly ILogger _logger =
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger _logger = 
    LogManager.GetLogger(typeof(YourTypeName));

Это фактически эквивалентно первой альтернативе (т.е. той, которая использует MethodBase.GetCurrentMethod().DeclaringType), только немного проще.

Ответ 3

Какие преимущества вы планируете получить от написания обертки для log4net. Я бы рекомендовал сначала освоить классы log4net, прежде чем писать обертку вокруг них. cfeduke прав в своем ответе о том, как писать указанную оболочку, но если вам не нужно добавлять фактическую функциональность к своему примеру, оболочке удалось бы только замедлить процесс регистрации и добавить сложность для будущих сопровождающих. Это особенно актуально, когда инструменты рефакторинга, доступные в .Net, делают такие изменения очень легкими.

Ответ 4

Я успешно изолировал зависимость log4net в одном проекте. Если вы намереваетесь сделать то же самое, вот как выглядит мой класс-оболочка:

using System;

namespace Framework.Logging
{
    public class Logger
    {
        private readonly log4net.ILog _log;

        public Logger()
        {
            _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        }

        public Logger(string name)
        {
            _log = log4net.LogManager.GetLogger(name);
        }

        public Logger(Type type)
        {
            _log = log4net.LogManager.GetLogger(type);
        }

        public void Debug(object message, Exception ex = null)
        {
            if (_log.IsDebugEnabled)
            {
                if (ex == null)
                {
                    _log.Debug(message);
                }
                else
                {
                    _log.Debug(message, ex);
                }
            }
        }

        public void Info(object message, Exception ex = null)
        {
            if (_log.IsInfoEnabled)
            {
                if (ex == null)
                {
                    _log.Info(message);
                }
                else
                {
                    _log.Info(message, ex);
                }
            }
        }

        public void Warn(object message, Exception ex = null)
        {
            if (_log.IsWarnEnabled)
            {
                if (ex == null)
                {
                    _log.Warn(message);
                }
                else
                {
                    _log.Warn(message, ex);
                }
            }
        }

        public void Error(object message, Exception ex = null)
        {
            if (_log.IsErrorEnabled)
            {
                if (ex == null)
                {
                    _log.Error(message);
                }
                else
                {
                    _log.Error(message, ex);
                }
            }
        }

        public void Fatal(object message, Exception ex = null)
        {
            if (_log.IsFatalEnabled)
            {
                if (ex == null)
                {
                    _log.Fatal(message);
                }
                else
                {
                    _log.Fatal(message, ex);
                }
            }
        }
    }
}

И не забудьте добавить это в AssemblyInfo.cs проекта сопряжения (мне понадобилось несколько часов, чтобы найти это)

[assembly: log4net.Config.XmlConfigurator(Watch = true, ConfigFile = "log4net.config")]

И поместите свой конфигурационный файл log4net в файл log4net.config, установите его как Content, Copy Always

Ответ 5

Я понимаю, что класс-оболочка для log4net будет статическим классом, который позаботится об инициализации объекта ведения журнала из app.config/web.config или по коду (например, интеграция с NUnit).

Ответ 6

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

Ответ 7

Alconja, мне нравится ваша идея использования stacktrace для возврата к вызывающему методу. Я думал о дальнейшем инкапсуляции вызовов, чтобы не просто получить объект журнала, но и выполнить фактическое выполнение журнала. Я хочу статический класс, который обрабатывает ведение журнала, путем абстрагирования от конкретной используемой реализации. То есть

LoggingService.LogError("my error message");

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

Итак, я использовал вашу идею, чтобы получить вызывающий объект, используя трассировку стека:

public static class LoggingService
{
    private static ILog GetLogger()
    {    
        var stack = new StackTrace();    
        var frame = stack.GetFrame(2);    
        return log4net.LogManager.GetLogger(frame.GetMethod().DeclaringType);
    }

    public static void LogError(string message)
    {
        ILog logger = GetLogger();
        if (logger.IsErrorEnabled)
            logger.Error(message);
    }
    ...
}

Кто-нибудь видит проблему с этим подходом?

Ответ 8

Существуют фреймворки, такие как Prism Library для WPF, которые способствуют использованию facade для структуры ведения журнала по вашему выбору.

Это пример, который использует log4net:

using System;
using log4net;
using log4net.Core;
using Prism.Logging;

public class Log4NetLoggerFacade : ILoggerFacade
{
    private static readonly ILog Log4NetLog = LogManager.GetLogger(typeof (Log4NetLoggerFacade));

    public void Log(string message, Category category, Priority priority)
    {
        switch (category)
        {
            case Category.Debug:
                Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Debug, message, null);
                break;
            case Category.Exception:
                Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Error, message, null);
                break;
            case Category.Info:
                Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Info, message, null);
                break;
            case Category.Warn:
                Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Warn, message, null);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(category), category, null);
        }
    }
}

Обратите внимание, что, указав callerStackBoundaryDeclaringType, вы все равно можете получить имя класса вызывающего, выдающего запрос регистрации. Все, что вам нужно сделать, это включить %C %M в шаблон преобразования:

<layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %C.%M - %message%newline" />
</layout>

Однако, как предупреждает документация, генерация информации класса вызывающего абонента выполняется медленно, поэтому ее необходимо использовать с умом.

Ответ 9

Я знаю, что этот ответ запоздал, но он может помочь кому-то в будущем.

Похоже, вам нужен программный API, который дает вам XQuiSoft Logging. Вам не нужно указывать, какой регистратор вы хотите использовать с XQuiSoft. это так просто:

Log.Write(Level.Verbose, "источник", "категория", "ваше сообщение здесь" );

Затем через конфигурацию вы направляете сообщения по источнику, категории, уровню или любому другому настраиваемому фильтру в разные местоположения (файлы, электронные письма и т.д.).

См. эту статью для введения.