如何在适配器类中包装NLog



阅读了Steven在这里的回答后,我开始思考切换日志记录系统,以及它可以是一个什么样的PITA。界面的简单性是我最喜欢的,而且它很原始,除了你为生成事件而编写的代码外,没有其他项目可以引入,什么都没有。我修改了原始代码,这样我就可以在LogEntry的上下文参数中传递类名:

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/contence参数是否有任何错误?

这篇文章还介绍了如何编写一个基于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容器中的一个单例,也可能被使用/制成一个静态类。

谢谢,Stephen

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

一个更典型的设计(和性能)设计是创建一个泛型实现,并注入一个封闭的泛型单例实现,其泛型参数等于它被注入的类,如以下答案所示:

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/contence参数是否有任何错误?

当使记录器实现具有上下文时,不需要在日志记录期间传递类似的上下文信息。

大警告:不要让ILogger抽象通用(就像微软在他们的日志库中所做的那样),这只会让消费者和他们的测试变得复杂。这将是一个严重的设计缺陷。

最新更新