建议设计:应用程序中几乎每个对象都有记录器

本文关键字:对象 记录器 应用程序 | 更新日期: 2023-09-27 18:03:50

我正在写一个应用程序。我使用NLog进行日志记录。在这个应用程序中,几乎每个对象都可以写入日志。我为它定义了protected成员:

protected Logger logger;
protected virtual Logger Logger
{
     get { return logger ?? (logger = LogManager.GetLogger(this.GetType().ToString())); }
}

在这种情况下,我需要复制/粘贴应用程序中的每个基类的代码。或者我看到其他选项:定义特定于应用程序的根对象,其中包含logger并将其子类化。但从语义上讲,这听起来是错误的,因为对我来说,这不是真正的"is-a"情况。

有更好的选择吗?

建议设计:应用程序中几乎每个对象都有记录器

有时我真希望c#能支持多重继承或混合....

你可以写一个扩展方法:

public static Logger Logger(this object obj) {
    return LogManager.GetLogger(obj.GetType());
}

缺点是,它会有点慢,因为创建的实例没有缓存(除了内部的NLog,这是一个实现细节),但你可以自己做:

public static Logger Logger(this object obj) {
    Logger logger;
    Type type = obj.GetType();
    // s_loggers is static Dictionary<Type, Logger>
    if (!s_loggers.TryGetValue(type, out logger)) { // not in cache
        logger = LogManager.GetLogger(type);
        s_loggers[type] = logger;  // cache it
    }
    return logger;
}

你可以这样称呼它:

this.Logger.Log(...)

明显的缺点是任何对象都可以写入任何其他对象的记录器。

关于内存泄漏的注释(现已删除):

第一个实现解决了这个问题。但是,它并不比任何静态对象更容易泄漏。如果您不能访问这些对象,这将是一个泄漏。作为一种替代方案,您可以将WeakReference缓存到logger而不是logger本身,但我认为没有意义,因为我相信NLog本身已经有一些缓存。否则,NLog将不得不为每种类型创建一个新的logger实例。

我建议您使用静态日志记录器,以便获得每个类的日志记录器。这样可以避免为每个实例创建一个日志记录器的开销(日志记录器是线程安全的):

class MyClass
{
    static readonly Logger logger = LogManager.GetCurrentClassLogger();
}

GetCurrentClassLogger将使您不必显式地命名记录器,但缺点是额外的开销,因为它必须在运行时从堆栈跟踪中为您找出记录器的名称。

在大多数情况下,这可能不是什么大问题,但在其他情况下,这样做会更快一些:

class MyClass
{
    static readonly Logger logger = LogManager.GetLogger("MyClass");
}

我会坚持使用GetCurrentClassLogger,直到/除非在您的解决方案中发现轻微的开销是一个问题。

我得到的印象是你在努力减少打字,和/或你在解决(好的)自然厌恶复制和粘贴重复的代码。

然而,这种带有静态初始化器的每个类都有一个日志记录器的模式是可以接受的,并且在大多数情况下效果最好。您最好坚持这样做,也许可以设置一个代码片段来节省一些输入。

如果你仍然不满意,依赖注入(通过构造函数或属性注入)或甚至面向方面的日志管理可能是你研究的其他事情。