89

根据 NLog 的文档:

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

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

4

10 回答 10

62

使用 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 将需要构建堆栈跟踪以查看是谁在调用它,否则您的代码将始终必须传入调用者。使用 logger-per-class 样式,您仍然可以这样做,但您可以在每个类中执行一次,而不是在每次调用中执行一次,从而消除严重的性能问题。

于 2010-06-29T19:18:27.253 回答
17

在 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}在布局中使用。

于 2013-07-24T16:09:33.230 回答
5

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

  • 如果您在日志输出格式中包含记录器的名称,您将始终知道特定日志语句的来源。
  • 您可以通过打开或关闭某些记录器或设置它们的级别来控制在细粒度级别上看到的日志语句。
于 2010-06-29T19:16:18.843 回答
4

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

Logger logger = LogManager.GetCurrentClassLogger()

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

于 2016-01-11T23:29:59.453 回答
3

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

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

于 2010-06-29T19:16:41.153 回答
1

两个原因立即浮现在脑海:

  1. 每个类都有一个单独的日志,可以轻松地将与给定类有关的所有日志消息/错误分组在一起。
  2. 在类中拥有日志允许您记录在类外部可能无法访问的内部细节(例如,私有状态、处理类实现的信息等)。
于 2010-06-29T19:15:44.243 回答
1

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

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

        }
    }
}
于 2016-01-19T19:45:11.270 回答
0

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

于 2010-06-29T19:15:05.833 回答
0

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

于 2010-06-29T19:20:06.260 回答
0

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

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

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

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

于 2017-10-24T07:40:35.363 回答