如何将 NLog 包装在适配器中 Class
How to wrap NLog in an Adapter Class
阅读 Steven's answer here 后,我开始考虑关闭日志系统以及它可以成为什么样的 PITA。界面的简单性是我最喜欢的,它是原始的,没有其他项目可以引入,除了您编写的用于生成事件的代码之外,什么都没有。我已经修改了原来的代码,这样我就可以在LogEntry的上下文参数中传入class名称:
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 参数有什么问题吗?
这个 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 容器中的单例,也可能 used/made 成静态 class。
谢谢,
斯蒂芬
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? Are there any other recommendations that you might give me?
一个更典型的设计(和性能)设计是创建一个通用实现,并注入一个封闭的通用单例实现,其通用参数等于它被注入的class,因为可以在下面看到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);
当logging adapter是单例时,意味着开销被降到了最低。
Question #1: Does anything seem wrong about passing in the Type/context param?
在使您的记录器实现具有上下文时,不需要像在记录期间那样传递上下文信息。
重要警告:不要使 ILogger
抽象成为通用的(如 Microsoft did 在他们的日志库中),这只会使消费者和他们的测试复杂化。那将是一个严重的设计缺陷。
阅读 Steven's answer here 后,我开始考虑关闭日志系统以及它可以成为什么样的 PITA。界面的简单性是我最喜欢的,它是原始的,没有其他项目可以引入,除了您编写的用于生成事件的代码之外,什么都没有。我已经修改了原来的代码,这样我就可以在LogEntry的上下文参数中传入class名称:
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 参数有什么问题吗?
这个 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 容器中的单例,也可能 used/made 成静态 class。
谢谢, 斯蒂芬
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? Are there any other recommendations that you might give me?
一个更典型的设计(和性能)设计是创建一个通用实现,并注入一个封闭的通用单例实现,其通用参数等于它被注入的class,因为可以在下面看到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);
当logging adapter是单例时,意味着开销被降到了最低。
Question #1: Does anything seem wrong about passing in the Type/context param?
在使您的记录器实现具有上下文时,不需要像在记录期间那样传递上下文信息。
重要警告:不要使 ILogger
抽象成为通用的(如 Microsoft did 在他们的日志库中),这只会使消费者和他们的测试复杂化。那将是一个严重的设计缺陷。