如何将NLog包装在适配器类中

时间:2017-10-10 04:37:25

标签: c# logging nlog

阅读Steven's回答here之后,它让我想到了切换日志记录系统以及可能的PITA。接口的简单性是我最喜欢的,它是原始的,没有其他项目可以引入,除了你为编写事件而编写的代码。我修改了原始代码,以便我可以在LogEntry的context参数中传入类名:

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

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

public class LogEntry
{
    public readonly LoggingEventType Severity;
    public readonly string Message;
    public readonly Exception Exception;
    public readonly Type Context;

    public LogEntry(LoggingEventType severity, 
                    string message, 
                    Exception exception = null, 
                    Type context = 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;
        this.Context = context;
    }
}

问题#1:传入Type / context param会出现什么问题吗?

这个post也为编写基于Log4net的适配器提供了一些启示,并且是我的NLog适配器的基础,尽管我没有使用ILogger的构造函数注入。

class NLogAdapter : ILogger
{
    public void Log(LogEntry entry)
    {
        NLog.Logger log;
        if (entry.Context != null)
        {
            log = NLog.LogManager.GetLogger(entry.Context.GetType().Namespace);
        }
        else
        {
            log = NLog.LogManager.GetLogger("DefaultLogger");
        }
        switch (entry.Severity)
        {
            case LoggingEventType.Debug:
                log.Debug(entry.Exception, entry.Message);
                break;
            case LoggingEventType.Information:
                log.Info(entry.Exception, entry.Message);
                break;
            case LoggingEventType.Warning:
                log.Warn(entry.Exception, entry.Message);
                break;
            case LoggingEventType.Error:
                log.Error(entry.Exception, entry.Message);
                break;
            case LoggingEventType.Fatal:
                log.Fatal(entry.Exception, entry.Message);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(entry));
        }
    }
}

问题2:我对每次调用使用日志管理器有点不确定,这是获取NLog Logger实例的最正确方法吗?您可以给我任何其他建议吗?

注意:此适配器可能是DI容器中的单例,也可以用于/制作静态类。

谢谢你, 斯蒂芬

1 个答案:

答案 0 :(得分:2)

  

我对每次调用使用日志管理器有点不确定,这是获取NLog Logger实例的最正确方法吗?您可以给我任何其他建议吗?

更典型的设计(和高性能)设计是一种创建通用实现的设计,并注入一个封闭的通用单例实现,其泛型参数等于它注入的类,如下所示answer

class NLogAdapter<T> : ILogger
{
    private static readonly NLog.Logger log =
        NLog.LogManager.GetLogger(typeof(T).FullName);
}

这不仅可以防止您在每次调用时解析NLog记录器,还可以防止您必须将上下文传递给LogEntry。

将其注入消费者,看起来像这样:

new ProductController(new NLogAdapter<ProductController>())

new HomeController(new NLogAdapter<HomeController>())

如果您使用的是DI容器,则取决于您使用的容器,必须如何配置。例如,使用Simple Injector,可以按如下方式进行上下文注册:

container.RegisterConditional(typeof(ILogger),
    c => typeof(NLogAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
    Lifestyle.Singleton,
    c => true); 

当日志记录适配器是单例时,意味着开销减少到最小。

  

问题#1:传入Type / context param会出现什么问题吗?

在制作记录器实现上下文时,无需在记录期间传递上下文这样的上下文信息。

大警告:不要使ILogger抽象泛型(在他们的日志库中作为Microsoft did),这会使消费者及其测试变得复杂。这将是一个严重的设计缺陷。