记录器是否应为私有静态


103

是否应该将记录器声明为静态?通常,我已经看到记录器的两种类型的声明:

    受保护的日志日志=新的Log4JLogger(aClass.class);

要么

    专用静态日志log = new Log4JLogger(aClass.class);

应该使用哪一个?两者的优缺点是什么?


1
日志记录是一个跨领域的问题。使用方面,问题是没有意义的。
Dave Jarvis

4
static是每个课程一个参考。非静态是每个实例一个引用(+初始化)。因此,在某些情况下,如果您有大量实例,则后者会对内存产生重大影响。切勿在经常使用的物体上使用非静电物品。我总是使用静态版本。(应该大写 LOG
已退出– Anony-Mousse 2012年

2
如已经建议的那样,使用AOP和注释,例如:jcabi.com/jcabi-aspects/annotation-loggable.html
yegor256

1
RobertHume静态版本采用恒定。这就是为什么它应该大写的原因。
退出了--Anony-Mousse,2017年

2
不,应该private static final Log log是小写。记录器不是常量,记录器是静态最终对象(可以更改)。我个人经常使用logger
osundblad

Answers:


99

非静态形式的优点在于,您可以像下面的(抽象)基类中声明它,而不必担心会使用正确的类名:

protected Log log = new Log4JLogger(getClass());

但是,其缺点显然是将为该类的每个实例创建一个全新的记录器实例。这本身可能并不昂贵,但是会增加大量开销。如果您想避免这种情况,请改用static表格。但是它的缺点是,您必须在每个单独的类中声明它,并在每个类中都要注意在记录器的构造过程中使用了正确的类名,因为getClass()不能在静态上下文中使用它。但是,在普通的IDE中,您可以为此创建一个自动完成模板。例如logger+ ctrl+space

另一方面,如果您通过工厂获得记录器,而该记录器又可能会缓存已经实例化的记录器,那么使用非静态形式不会增加太多开销。例如Log4j为此具有一个LogManager

protected Log log = LogManager.getLogger(getClass());

6
abstract Log getLogger();在抽象类中声明。实现此方法,为特定实例返回静态记录器。添加private final static Log LOG = LogManager.getLogger(Clazz.class);到您的IDE类模板。
已退出-Anony-Mousse 2012年

2
对于slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Markus Pscheidt 2014年

3
@BalusC将getClass()传递给getLogger方法的问题是它返回当前实例的类。通常,更希望日志记录与代码所在的类相关联。例如,如果日志记录代码位于Parent类中,那么即使执行实例是Parent类的子类Child的实例,我们也希望日志记录与Parent关联。随着的getClass()将与儿童有关,错误地
INOR

@inor:“错误地”?如果您不想抽象该类,则首先不要使用继承的getClass()。有些开发人员确实发现它是正确的和有用的,因为它揭示了在哪个子类中确切地执行了逻辑的信息。
BalusC '18

2
@ BalusC getLogger(getClass())导致始终错误地记录子类的名称。日志记录类应始终执行getLogger(Clazz.class)来关联由Clazz类中的代码进行的日志记录。想要知道正在执行哪个子类的开发人员(例如SubClazz扩展了Clazz),应该在SubClazz中执行:getLogger(SubClazz.class)和诸如此类:log.info(“在<我的基类中调用<something>”);
INOR

44

我曾经认为所有记录器都应该是静态的。但是,wiki.apache.org上的这篇文章提出了一些与类加载器泄漏有关的重要内存问题。将记录器声明为静态可以防止在使用共享类加载器的J2EE容器中将声明类(和关联的类加载器)垃圾收集。如果您重新部署应用程序足够的时间,这将导致PermGen错误。

除了将记录器声明为非静态记录器之外,我真的没有其他方法可以解决该类加载器泄漏问题。


4
我怀疑静态字段也会出现内存泄漏问题。正如其他人所说,非静态可能存在性能问题。那么理想的方法是什么?
liang

@piepera您所引用的文章中描述的主要问题是,当“考虑通过使用ClassLoader部署了使用“ private static Log log =”的类的情况时,在每个应用程序中控制日志记录级别的能力”独立的“应用程序””。我认为这不是问题,因为在这种特定情况下,应用程序具有“共同点”,并且在该“共同点”下,确定了该类的日志记录级别,是的,它对所有应用程序都适用...但是请保留记住,这个类是这些应用的[加载]外
INOR

17

最重要的区别是它如何影响您的日志文件:日志进入哪个类别?

  • 在您的第一选择中,子类的日志最终属于超类的类别。这对我来说似乎很违反直觉。
  • 您的第一种情况有一个变体:

    受保护的日志日志=新的Log4JLogger(getClass());

    在这种情况下,您的日志类别会说明所记录的代码正在处理哪个对象。

  • 在第二种选择(私有静态)中,日志类别是包含日志记录代码的类。因此通常情况下,正在执行正在记录的事情的类。

我强烈建议您选择最后一个选项。与其他解决方案相比,它具有以下优点:

  • 日志和代码之间有直接关系。很容易找到日志消息的来源。
  • 如果某人必须调整日志记录级别(按类别完成),通常是因为他们对特定类编写的某些特定消息感兴趣(或不感兴趣)。如果类别不是编写消息的类别,则调整级别将更加困难。
  • 您可以登录静态方法
  • 记录器每个类仅需要初始化(或查询)一次,因此在启动时,无需为每个创建的实例初始化。

它也有缺点:

  • 它需要在您记录消息的每个类中声明(不得重用超类记录器)。
  • 在初始化记录器时,您需要注意输入正确的类名。(但是好的IDE会帮您解决这个问题)。

4

使用控件的反转并将记录器传递到构造函数中。如果您在类中创建记录器,那么单元测试将历久弥新。您正在编写单元测试,不是吗?


5
单元测试检查您正在产生的声音的记录,这些记录既无用又非常脆弱。
迈克尔(Michael)

1
有用性取决于被测系统。有时,日志记录就是您可以访问的全部。
韦恩·艾伦

@Wayne Allen在进行单元测试时,根据定义,您还将获得测试结果。您是否在提出一种情况,其中单元测试但没有测试结果?只有日志?
INOR

在类内部创建记录器不会产生问题。您能否显示一个简单的示例,因为该类创建了自己的记录器,所以很难使用UT?
INOR

1
当然。发送电子邮件的记录器怎么样。不想每次运行测试时都这样做。加上您如何断言副作用?
韦恩·艾伦
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.