为什么记录器建议每个类使用一个记录器?

时间:2010-06-29 19:12:32

标签: c# log4net logging nlog

根据NLog的文档:

  

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

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

10 个答案:

答案 0 :(得分:56)

使用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

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

答案 1 :(得分: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}记录到文件(或其他目标)。

答案 2 :(得分:5)

我可以看到这个选择的几个原因。

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

答案 3 :(得分:3)

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

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

答案 4 :(得分:3)

NLog也有性能优势。大多数用户将使用

Run-time error '1004':
Application-defined or object defined error.

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

答案 5 :(得分:2)

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

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

        }
    }
}

答案 6 :(得分:1)

立即想到两个原因:

  1. 为每个类分别创建一个日志,可以很容易地将与给定类相关的所有日志消息/错误组合在一起。
  2. 在类中创建日志允许您记录在类之外可能无法访问的内部详细信息(例如,私有状态,处理类实现的信息等)。

答案 7 :(得分:0)

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

答案 8 :(得分:0)

使命名空间或类配置appender变得容易。

答案 9 :(得分:0)

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

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

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

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