简体   繁体   English

如何在适配器类中包装NLog

[英]How to wrap NLog in an Adapter Class

Having read Steven's answer here , it got me thinking about switching out logging systems and what a PITA that can be. 这里阅读了史蒂文(Steven)的答案后,我开始思考切换日志系统以及可以使用的PITA。 The simplicity of the interface is what I like the most, and it's raw, no other project to bring in, nothing, except the code you write to produce the events. 接口的简单性是我最喜欢的,它是原始的,没有其他项目可以引入,除了编写用于生成事件的代码外,什么也没有。 I have modified the original code so that I can pass in the class name in the context parameter of the LogEntry: 我已经修改了原始代码,以便可以在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;
    }
}

Question #1: Does anything seem wrong about passing in the Type/context param? 问题1:传递类型/上下文参数似乎有什么问题吗?

This post also shed some light on writing an adapter based on Log4net, and is the basis of my NLog adapter, although I don't use constructor injection of an ILogger. 这篇文章也为基于Log4net的适配器的编写提供了一些启发,尽管我不使用ILogger的构造函数注入,但它还是我的NLog适配器的基础。

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));
        }
    }
}

Question #2: I'm a bit unsure about the use of the log manager for every call, is this the most correct way to get an instance of a NLog Logger? 问题2:对于每次调用都使用日志管理器我还是不太确定,这是获取NLog Logger实例的最正确方法吗? Are there any other recommendations that you might give me? 您还有其他建议吗?

Note: This Adapter may be a singleton in a DI container, or it could be used/made into a static class. 注意:此适配器可以是DI容器中的单例,也可以将其用作静态类。

Thank you, Stephen 谢谢斯蒂芬

I'm a bit unsure about the use of the log manager for every call, is this the most correct way to get an instance of a NLog Logger? 我不确定每次调用都使用日志管理器,这是否是获取NLog Logger实例的最正确方法? Are there any other recommendations that you might give me? 您还有其他建议吗?

A more typical design (and performant) design is one where you create a generic implementation, and inject a closed-generic singleton implementation with its generic argument being equal to the class it gets injected into, as can be seen in the following answer : 一种更典型的设计(和高性能)设计是在其中创建一个通用实现,并以其通用参数等于其注入到的类的方式注入一个封闭的通用单例实现,如以下答案所示

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

This not only prevents you from having to resolve a NLog logger on each call, it prevents you from having to pass along the context to the LogEntry. 这不仅使您不必在每次调用时都解析NLog记录器,而且还使您不必将上下文传递给LogEntry。

Injecting it into consumers, would look like this: 将其注入消费者,将如下所示:

new ProductController(new NLogAdapter<ProductController>())

new HomeController(new NLogAdapter<HomeController>())

If you're using a DI Container, it depends on the container you're using, how this must be configured. 如果您使用的是DI容器,则取决于您使用的容器以及必须如何配置它。 With Simple Injector for instance, it's a matter of making a contextual registration as follows: 例如,对于Simple Injector,只需进行如下上下文注册即可:

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

When the logging adapter is a singleton, it means the overhead is reduced to the minimum. 当日志适配器为单例时,这意味着开销减少到最小。

Question #1: Does anything seem wrong about passing in the Type/context param? 问题1:传递类型/上下文参数似乎有什么问题吗?

When making your logger-implementation contextual, there is no need to pass on contextual information like that during logging. 使记录器实现与环境相关时,无需在记录期间传递诸如此类的环境信息。

Big warning: do not make the ILogger abstraction generic (as Microsoft did in their logging library), that would just complicate things for the consumers and their tests. 重要警告:请勿使ILogger抽象通用(如Microsoft在其日志记录库中所做的那样 ),这只会使使用者及其测试复杂化。 That would be a severe design flaw. 那将是一个严重的设计缺陷。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM