Как будет выглядеть класс Log4Net Wrapper? - PullRequest
55 голосов
/ 03 октября 2008

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

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

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

Ответы [ 9 ]

50 голосов
/ 03 октября 2008

По сути, вы создаете интерфейс, а затем конкретную реализацию этого интерфейса, который оборачивает классы и методы Log4net напрямую. Дополнительные системы ведения журналов можно обернуть, создав более конкретные классы, которые обертывают другие классы и методы этих систем. Наконец, используйте фабрику для создания экземпляров ваших оболочек на основе параметра конфигурации или строки изменения кода. (Примечание: вы можете стать более гибкими - и сложными - используя 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's Logger...
        // return new JoesLoggerWrapper(type);
    }
}

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

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

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

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

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

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

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

24 голосов
/ 26 февраля 2009

Предполагая, что вы собираетесь что-то вроде ответа 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), только немного проще.

5 голосов
/ 03 октября 2008

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

1 голос
/ 15 июля 2016

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

Это пример, который использует 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>

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

1 голос
/ 03 февраля 2016

Я успешно изолировал зависимость 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")]

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

0 голосов
/ 06 декабря 2010

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

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

Log.Write (Level.Verbose, "source", "category", "your message here");

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

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

0 голосов
/ 17 августа 2009

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

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);
    }
    ...
}

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

0 голосов
/ 03 октября 2008

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

0 голосов
/ 03 октября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...