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

发布于 2024-09-07 17:43:57 字数 133 浏览 9 评论 0原文

根据 NLog 的文档:

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

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

As per NLog's documentation:

Most applications will use one logger per class, where the name of the logger is the same as the name of the class.

This is the same way that log4net operates. Why is this a good practice?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

千纸鹤 2024-09-14 17:43:57

使用 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-per-class 风格,您仍然可以这样做,但是您可以为每个类执行一次而不是每次调用一次,从而消除严重的性能问题。

With log4net, using one logger per class makes it easy to capture the source of the log message (ie. the class writing to the log). If you don't have one logger per class, but instead have one logger for the entire app, you need to resort to more reflection tricks to know where the log messages are coming from.

Compare the following:

Log per class

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

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

One logger per app (or similar)

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

-- or --

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

Using the second example, the Logger would need to build a stack trace to see who was calling it or your code would always have to pass in the caller. With the logger-per-class style, you still do this, but you can do it once per class instead of once per call and eliminate a serious performance problem.

樱花细雨 2024-09-14 17:43:57

在 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} 轻松记录到文件(或其他目标)。

Advantage for using "logger per file" in NLog: you have possibility to manage/filter logs by namespace and class name. Example:

<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 has a very useful code snippet to do this. The nlogger snippet will create the following code:

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

So only few keystrokes and you have logger per class. It will use namespace and class name as the name of the logger. To set different name to your class logger, you can use this:

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

And, as @JeremyWiebe said, you don't have to use tricks to get the name of the class which is trying to log a message: Name of the logger (which is usually the name of the class) can be easy logged to file (or other target) by using ${logger} in layout.

洛阳烟雨空心柳 2024-09-14 17:43:57

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

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

I can see a few reasons for this choice.

  • You will always know where a particular log statement came from, if you include the name of the logger in your log output format.
  • You can control what log statements you see at a fine grained level by turning certain loggers on or off, or setting their level.
弱骨蛰伏 2024-09-14 17:43:57

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

Logger logger = LogManager.GetCurrentClassLogger()

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

There is also a performance benefit in the case of NLog. Most users will use

Logger logger = LogManager.GetCurrentClassLogger()

Looking up the current class from stack trace take some (but not much) performance.

烂柯人 2024-09-14 17:43:57

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

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

In most cases, the name of the class provides a good name for the logger. When scanning the log files, you can see the log message and associate it directly with a line of code.

A good example where this is not the best approach, is Hibernate's SQL logs. There is a shared logger named "Hibernate.SQL" or something like that, where a number of different classes write raw SQL out to a single logger category.

二智少女猫性小仙女 2024-09-14 17:43:57

我立即想到两个原因:

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

Two reasons immediately spring to mind:

  1. Having a separate log for each class makes it easy to group together all log messages/errors pertaining to a given class.
  2. Having a log within a class allows you to log internal details which may not be accessible outside the class (e.g., private state, information dealing with a class's implementation, etc.).
烟凡古楼 2024-09-14 17:43:57

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

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

        }
    }
}

From a development standpoint, it's easiest if you don't have to create a logger object each time. On the other hand, if you don't, but rather you create it dynamically using reflection, it'll slow down performance. To solve this, you can use the following code which creates the logger dynamically asynchronously:

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

        }
    }
}
橘和柠 2024-09-14 17:43:57

可能是因为您希望能够记录仅对该类可见的方法而不破坏封装,这也使得您可以轻松地在另一个应用程序中使用该类,而不会破坏日志记录功能。

Probably because you want to be able to log methods that are only visible to the class without breaking encapsulation, this also makes it easy to use the class in another application without breaking the logging functionality.

横笛休吹塞上声 2024-09-14 17:43:57

可以轻松地按命名空间或类配置附加程序。

Makes it easy to configure appenders by namespace or class.

抹茶夏天i‖ 2024-09-14 17:43:57

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

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

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

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

If you are using NLOG you can specify the callsite in the config, this will record the class name and method where the logging statement was located.

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

You could then use a constant for your logger name or the assembly name.

Disclaimer: I don't know how NLOG collects this information, my guess would be reflection so you may need to consider the performance. There are a few issues with Async methods if you are not using NLOG v4.4 or later.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文