为什么logging器推荐使用每个类的logging器?

根据NLog的文件:

大多数应用程序将使用每个类的一个logging器,其中logging器的名称与类的名称相同。

这与log4net的运行方式相同。 为什么这是一个很好的做法?

使用log4net,每个类使用一个logging器可以很容易地捕获日志消息的来源(即写入日志的类)。 如果您没有每个类的logging器,而是为整个应用程序提供一个logging器,则需要使用更多的reflection技巧来知道日志消息的来源。

比较以下内容:

日志每class

using System.Reflection; private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public void SomeMethod() { _logger.DebugFormat("File not found: {0}", _filename); } 

一个logging器每个应用程序(或类似)

 Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller -- or -- Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller 

使用第二个例子,logging器需要build立一个堆栈跟踪来查看谁在调用它,否则你的代码将不得不传入调用者。 使用每class风格的logging器,您仍然可以做到这一点,但是您可以每个class级做一次,而不是每次调用一次,并消除严重的性能问题。

在NLog中使用“每个文件的logging器”的优点:您可以通过命名空间和类名来pipe理/过滤日志。 例:

 <logger name="A.NameSpace.MyClass" minlevel="Debug" writeTo="ImportantLogs" /> <logger name="A.NameSpace.MyOtherClass" minlevel="Trace" writeTo="ImportantLogs" /> <logger name="StupidLibrary.*" minlevel="Error" writeTo="StupidLibraryLogs" /> <!-- Hide other messages from StupidLibrary --> <logger name="StupidLibrary.*" final="true" /> <!-- Log all but hidden messages --> <logger name="*" writeTo="AllLogs" /> 

NLogger有一个非常有用的代码片段来做到这一点。 nlogger片段将创build以下代码:

 private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); 

所以只有很less的击键,而且每个类都有logging器。 它将使用名称空间和类名作为logging器的名称。 要为你的class级logging器设置不同的名字,你可以使用这个:

 private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName"); 

而且,正如@JeremyWiebe所说,你不必使用技巧来获取正在尝试login消息的类的名称:logging器的名称(通常是类的名称)可以很容易地logging到文件(或其他目标)在布局中使用${logger}

我可以看到这个select的一些原因。

  • 如果在日志输出格式中包含logging器的名称,您将始终知道特定日志语句的来源。
  • 您可以通过打开或closures某些logging器或设置其级别来控制在精细粒度级别看到的日志语句。

在大多数情况下,这个类的名字为logging器提供了一个很好的名字。 扫描日志文件时,您可以看到日志消息并将其直接与一行代码关联。

一个很好的例子,这不是最好的办法,是Hibernate的SQL日志。 有一个名为“Hibernate.SQL”或类似的共享logging器,其中有许多不同的类将原始SQL写入单个logging器类别。

立即想到两个原因:

  1. 为每个class级设置一个单独的日志可以很容易地将所有与给定class级相关的日志消息/错误分组在一起。
  2. 在一个类中有一个日志允许你logging内部的细节,这些细节可能在类之外是不可访问的(例如,私有状态,涉及类的实现的信息等等)。

可能是因为您希望能够在不破坏封装的情况下logging仅对类可见的方法,这也使得在不中断日志loggingfunction的情况下在另一个应用程序中使用该类变得容易。

通过命名空间或类可以很容易地configurationappender。

在NLog的情况下也有性能优势。 大多数用户将使用

 Logger logger = LogManager.GetCurrentClassLogger() 

从堆栈跟踪中查找当前类需要一些(但不是很多)性能。

从开发的angular度来看,最简单的方法是不必每次都创build一个logging器对象。 另一方面,如果你不这样做,而是使用reflectiondynamic创build它,则会降低性能。 为了解决这个问题,你可以使用下面的代码来dynamic地asynchronous创buildlogging器:

 using NLog; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WinForms { class log { public static async void Log(int severity, string message) { await Task.Run(() => LogIt(severity, message)); } private static void LogIt(int severity, string message) { StackTrace st = new StackTrace(); StackFrame x = st.GetFrame(2); //the third one goes back to the original caller Type t = x.GetMethod().DeclaringType; Logger theLogger = LogManager.GetLogger(t.FullName); //https://github.com/NLog/NLog/wiki/Log-levels string[] levels = { "Off", "Trace", "Debug", "Info", "Warn", "Error", "Fatal" }; int level = Math.Min(levels.Length, severity); theLogger.Log(LogLevel.FromOrdinal(level), message); } } } 

如果您使用的是NLOG,则可以在configuration中指定调用站点,这将logging日志语句所在的类名称和方法。

 <property name="CallSite" value="${callsite}" /> 

然后,您可以使用常量作为logging器名称或程序集名称。

免责声明:我不知道NLOG如何收集这些信息,我的猜测是反映,所以你可能需要考虑性能。 如果您不使用NLOG v4.4或更高版本,则Async方法有几个问题。