记录器包装最佳实践
收藏

我想在应用程序中使用nlogger,也许将来我需要更改日志记录系统。 所以我想使用伐木外观。

您知道对现有示例的任何建议如何编写这些建议吗? 或者只是给我链接到该领域的一些最佳实践。

最佳答案

我曾经使用诸如Common.Logging之类的日志记录外观(甚至隐藏了我自己的CuttingEdge.Logging库),但是如今,我使用了Dependency Injection模式,这使我可以将loggers隐藏在我自己的(简单的)抽象背后,该抽象遵循两种依赖性反转原理和接口隔离原理(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.
}

Since the interface contains just a single method, you can easily create an ILogger implementation that proxies to log4net, to Serilog, Microsoft.Extensions.Logging, NLog or any other logging library and configure your DI container to inject it in classes that have a ILogger in their constructor.

Do note that having static extension methods on top of an interface with a single method is quite different from having an interface with many members. The extension methods are just helper methods that create a LogEntry message and pass it through the only method on the ILogger interface. The extension methods become part of the consumer's code; not part of the abstraction. Not only does this allow the extension methods to evolve without the need to change the abstraction, the extension methods and the LogEntry constructor are always executed when the logger abstraction is used, even when that logger is stubbed/mocked. This gives more certainty about the correctness of calls to the logger when running in a test suite. The one-membered interface makes testing much easier as well; Having an abstraction with many members makes it hard to create implementations (such as mocks, adapters and decorators).

当您执行此操作时,几乎不需要日志外观(或任何其他库)可能提供的某些静态抽象。

    公众号
    关注公众号订阅更多技术干货!