为什么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器类别。
立即想到两个原因:
- 为每个class级设置一个单独的日志可以很容易地将所有与给定class级相关的日志消息/错误分组在一起。
- 在一个类中有一个日志允许你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方法有几个问题。