为什么记录员建议每课使用记录器?


88

根据NLog的文档:

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

这与log4net的运行方式相同。为什么这是个好习惯?


1
嗯。似乎这里有两个问题-一个问题是每个类都有一个实际的日志对象,另一个问题是该日志的名称与该类相同。
Peter Recore 2010年

Answers:


59

使用log4net,每个类使用一个记录器可以轻松捕获日志消息的源(即,类写入日志)。如果每个班级没有一个记录器,而整个应用程序只有一个记录器,那么您需要采取更多的反射技巧来了解日志消息的来源。

比较以下内容:

每个班级的日志

using System.Reflection;
private static readonly ILog _logger = 
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);    

public void SomeMethod()
{
    _logger.DebugFormat("File not found: {0}", _filename);
}

每个应用(或类似应用)一个记录器

Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller

-- or --

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

使用第二个示例,记录器将需要构建堆栈跟踪以查看谁在调用它,否则您的代码将始终必须传入调用者。仍然使用每类记录器样式,您仍然可以执行此操作,但是您可以在每个类中执行一次操作,而不是在每个调用中执行一次操作,从而消除了严重的性能问题。


谢谢,这有助于澄清问题。我们只是手动将类名和方法放入消息中(例如,“ ImageCreator.CreateThumbnail()被调用”),但是最好由记录器处理。
Daniel T.

1
仅供参考,在每个实例而不是每个类(即静态)上都有一个Logger成为“更好”的做法,因为这样可以更轻松地捕获诸如线程信息之类的信息。显然,这只是一个口味问题,没有“硬性规定”,但我只想把它排除在外。
Will Hartung 2010年

7
@will,您能解释一下吗?使用每个类的记录器进行记录时,我总是记录线程ID,以便记录器可以获取当前线程信息。记录器也可以使用任何其他线程信息。
Jeremy Wiebe 2010年

@Jeremy Wiebe:这是唯一原因吗?如果我对整个应用程序使用类型logger的单个全局变量,在功能上没有问题吗?
giorgim 2015年

1
@Giorgi不,我不这么认为。这些天,您可以使用CallerInformation属性获得很多此类信息,这些属性使每个类的一个记录器的相关性降低-msdn.microsoft.com/zh-cn/library/hh534540.aspx
Jeremy Wiebe 2015年

15

在NLog中使用“每个文件记录器”的优点:您可以按名称空间和类名称来管理/过滤日志。例:

<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代码段将创建以下代码:

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

因此,只有几次击键,并且每个班级都有记录器。它将使用名称空间和类名称作为记录器的名称。要为类记录器设置不同的名称,可以使用以下命令:

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

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


5

我可以看到这种选择的一些原因。

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

4

对于NLog,还具有性能优势。大多数用户将使用

Logger logger = LogManager.GetCurrentClassLogger()

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


3

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

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


2

从开发角度来看,最简单的方法是不必每次都创建一个logger对象。另一方面,如果不这样做,而是使用反射来动态创建它,则会降低性能。为了解决这个问题,您可以使用以下代码以异步方式动态创建记录器:

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

        }
    }
}

1

立即想到两个原因:

  1. 每个类都有一个单独的日志,可以很容易地将与给定类有关的所有日志消息/错误分组在一起。
  2. 在类中包含日志可以使您记录内部细节,而这些细节在类外部可能无法访问(例如,私有状态,与类的实现有关的信息等)。

2
无论在类级别还是全局定义,您都在班级中拥有一个记录器。从可见性的角度来看,全局记录器并不在课堂之外。你还在从引用全局记录有问题的类,所以你有充分的可见性。
罗伯特

0

可能是因为您希望能够记录仅对类可见的方法而不会破坏封装,所以这也使在不破坏记录功能的情况下在另一个应用程序中使用该类变得容易。


1
这使得很难在另一个应用程序中使用该类。无论您是否喜欢,都必须引用日志记录库。
霹雳霹雳州

0

使按名称空间或类配置追加程序变得容易。


0

如果使用的是NLOG,则可以在配置中指定呼叫站点,这将记录日志记录语句所在的类名称和方法。

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

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

免责声明:我不知道NLOG如何收集这些信息,我的猜测是反思,因此您可能需要考虑性能。如果您不使用NLOG v4.4或更高版本,则异步方法会有一些问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.