Подтвердить что ты не робот

Лучшая практика обертки регистратора

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

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

4b9b3361

Ответ 1

Раньше я использовал логические фасады, такие как 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 всегда выполняются, когда используется абстракция журнала, даже если этот логгер заштрихован/издевается. Это дает больше уверенности в правильности запросов к журналу при запуске в тестовом наборе. Одночленный интерфейс упрощает тестирование; Наличие абстракции со многими участниками затрудняет создание реализаций (таких как макеты, адаптеры и декораторы).

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 6

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

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