Лучшая практика обертывания лесорубов


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

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

6 74

6 ответов:

Раньше я использовал фасады лесозаготовок, такие как Common.Logging (даже чтобы скрыть мою собственную CuttingEdge.Logging library), но в настоящее время я использую шаблон внедрения зависимостей, и это позволяет мне скрывать логгеры за моей собственной (простой) абстракцией, которая придерживается как принципа инверсии зависимостей, так и принципа сегрегации интерфейса (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, к Серилогу, Microsoft.Увеличение.Logging , NLog или любую другую библиотеку журналов и настройте контейнер DI для его внедрения в классах, которые имеют ILogger в своем конструкторе.

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

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

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

PM> Install-Package NLog.Interface 

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

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

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

Вообще я предпочитаю создавать интерфейс типа

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

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

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

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

С 2015 года вы также можете использовать .Чистая одножильным, Если вы строите .Объем основных приложений.

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