Лучшая практика обертки Logger - PullRequest
85 голосов
/ 13 апреля 2011

Я хочу использовать nlogger в своем приложении, возможно, в будущем мне нужно будет изменить систему ведения журнала. Поэтому я хочу использовать лесозаготовительный фасад.

Знаете ли вы какие-либо рекомендации для существующих примеров, как их писать? Или просто дайте мне ссылку на лучшую практику в этой области.

Ответы [ 7 ]

193 голосов
/ 13 апреля 2011

Раньше я использовал каркасные фасады, такие как Common.Logging (даже чтобы скрыть свою собственную CuttingEdge.Logging библиотеку), но в настоящее время я использую шаблон внедрения зависимостей и это позволяет мне скрывать регистраторы за моей собственной (простой) абстракцией, которая соответствует как принципу инверсии зависимости , так и принципу разделения интерфейса (ISP), потому что он имеет одного члена и потому что интерфейс определяется моим приложением; не внешняя библиотека. Минимизируя знания о существовании внешних библиотек, которые есть у основных компонентов вашего приложения, тем лучше; даже если у вас нет намерения когда-либо заменить свою библиотеку журналов. Жесткая зависимость от внешней библиотеки усложняет тестирование вашего кода и усложняет ваше приложение API, который никогда не разрабатывался специально для вашего приложения.

Вот как часто абстракция выглядит в моих приложениях:

public interface ILogger
{
    void Log(LogEntry entry);
}

public enum LoggingEventType { Debug, Information, Warning, Error, Fatal };

// Immutable DTO that contains the log information.
public class LogEntry 
{
    public readonly LoggingEventType Severity;
    public readonly string Message;
    public readonly Exception Exception;

    public LogEntry(LoggingEventType severity, string message, Exception exception = null)
    {
        if (message == null) throw new ArgumentNullException("message");
        if (message == string.Empty) throw new ArgumentException("empty", "message");

        this.Severity = severity;
        this.Message = message;
        this.Exception = exception;
    }
}

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

public static class LoggerExtensions
{
    public static void Log(this ILogger logger, string message) {
        logger.Log(new LogEntry(LoggingEventType.Information, message));
    }

    public static void Log(this ILogger logger, Exception exception) {
        logger.Log(new LogEntry(LoggingEventType.Error, exception.Message, exception));
    }

    // More methods here.
}

Поскольку интерфейс содержит только один метод, вы можете легко создать реализацию ILogger, которая передает в log4net , в Serilog , Microsoft.Extensions.Logging , NLog или любую другую библиотеку журналов и настройте свой DI-контейнер для внедрения его в классы, имеющие в своем конструкторе ILogger.

Обратите внимание, что наличие статических методов расширения поверх интерфейса с одним методом сильно отличается от наличия интерфейса со многими членами. Методы расширения - это просто вспомогательные методы, которые создают сообщение LogEntry и передают его через единственный метод интерфейса ILogger. Методы расширения становятся частью кода потребителя; не является частью абстракции. Это позволяет не только расширять методы расширения без необходимости изменения абстракции, но и методы расширения и конструктор LogEntry всегда выполняются, когда используется абстракция регистратора, даже когда этот регистратор заглушен / осмеян. Это дает больше уверенности в правильности обращений к регистратору при запуске в тестовом наборе. Одночленный интерфейс также значительно облегчает тестирование; Наличие абстракции со многими членами затрудняет создание реализаций (таких как макеты, адаптеры и декораторы).

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

9 голосов
/ 20 февраля 2013

Я использовал небольшую оболочку интерфейса + адаптер от https://github.com/uhaciogullari/NLog.Interface, которая также доступна через NuGet :

PM> Install-Package NLog.Interface 
4 голосов
/ 09 мая 2016

Отличное решение этой проблемы появилось в виде проекта LibLog .

LibLog - это абстракция журналирования со встроенной поддержкой основных регистраторов, включая Serilog, NLog, Log4net и Enterprise logger. Он устанавливается через менеджер пакетов NuGet в целевую библиотеку в виде исходного файла (.cs) вместо ссылки на DLL. Такой подход позволяет включать абстракцию журналирования, не заставляя библиотеку принимать внешнюю зависимость. Это также позволяет автору библиотеки включать ведение журнала, не вынуждая приложение-потребитель явно предоставлять в библиотеку средства ведения журнала. LibLog использует отражение, чтобы выяснить, какой конкретный регистратор используется, и подключить его без какого-либо явного кода подключения в библиотечных проектах.

Итак, LibLog - отличное решение для входа в библиотечные проекты. Просто укажите и настройте конкретный регистратор (Serilog для победы) в своем основном приложении или службе и добавьте LibLog в свои библиотеки!

4 голосов
/ 13 апреля 2011

Обычно я предпочитаю создавать интерфейс, подобный

public interface ILogger
{
 void LogInformation(string msg);
 void LogError(string error);
}

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

2 голосов
/ 25 апреля 2019

На данный момент лучше всего использовать пакет Microsoft.Extensions.Logging (, как указано Джулианом ).С этим можно использовать большинство каркасов ведения журналов.

Определение собственного интерфейса, как описано в Ответ Стивена подходит для простых случаев, но в нем пропущено несколько вещей, которые я считаю важными:

  • Структурированные объекты логирования и деструктуризации (нотация @ в Serilog и NLog)
  • Задержка построения / форматирования строки: поскольку она принимает строку, она должна оценивать / форматировать все, когда вызывается, даже если в конце событие не будет зарегистрировано, потому что оно ниже порогового значения (стоимость производительности, см. предыдущий пункт)
  • Условные проверки, такие как IsEnabled(LogLevel), которые вы, возможно, захотите, по причинам производительности еще раз

Вероятно, вы можете реализовать все это в своей собственной абстракции, но в этот момент вы будете заново изобретать колесо.

2 голосов
/ 13 апреля 2011

Вместо написания собственного фасада, вы можете использовать Castle Logging Services или Simple Logging Façade .

Оба включают адаптеры для NLog и Log4net.

1 голос
/ 15 сентября 2016

С 2015 года вы также можете использовать .NET Core Logging , если вы создаете базовые приложения .NET.

Пакет для подключения NLog:

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