记录器应该是私有静态还是非静态

Dra*_*kar 99 java logging

记录器是否应声明为静态?通常我会看到两种类型的记录器声明:

    protected Log log = new Log4JLogger(aClass.class);

要么

    private static Log log = new Log4JLogger(aClass.class);

应该使用哪一个?这两者的专业和概念是什么?

Bal*_*usC 95

非静态表单的优点是您可以在(抽象)基类中声明它,如下所示,而不必担心将使用正确的类名:

protected Log log = new Log4JLogger(getClass());
Run Code Online (Sandbox Code Playgroud)

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

另一方面,如果您通过工厂获取记录器,而工厂又可以缓存已经实例化的记录器,那么使用非静态表单不会增加太多开销.例如,Log4j有一个LogManager用于此目的.

protected Log log = LogManager.getLogger(getClass());
Run Code Online (Sandbox Code Playgroud)

  • 在抽象类中声明`abstract Log getLogger();`.实现此方法,返回特定实例的静态记录器.将`private final static Log LOG = LogManager.getLogger(Clazz.class);`添加到IDE类模板中. (6认同)
  • @BalusC将getClass()传递给getLogger方法的问题是它返回当前实例的类.通常,更希望日志记录与代码所在的类相关联.例如,如果日志代码在Parent类中,那么我们希望日志记录与Parent相关联,即使正在执行的实例是类Child的实例,它是Parent的子类.使用getClass(),它将与Child关联,不正确 (3认同)
  • 对于slf4j:`protected Logger log = LoggerFactory.getLogger(getClass());` (2认同)
  • @BalusC getLogger(getClass())导致始终错误地记录子类的名称.记录类应始终使用getLogger(Clazz.class)来关联类Clazz中的代码所做的日志记录.想要知道哪些子类正在执行的开发人员(例如SubClazz扩展Clazz)应该在SubClazz中执行:getLogger(SubClazz.class)和类似:log.info("在我的基类中调用<something>"); (2认同)

pie*_*era 44

我以前认为所有记录器都应该是静态的; 但是,wiki.apache.org上的这篇文章提出了一些关于类加载器泄漏的重要内存问题.将logger声明为static会阻止声明类(和关联的类加载器)在使用共享类加载器的J2EE容器中进行垃圾回收.如果您重新部署应用程序足够多次,这将导致PermGen错误.

除了将记录器声明为非静态外,我真的没有办法解决这个类加载器泄漏问题.

  • 我怀疑静态字段也会有内存泄漏问题.其他人说,非静态可能会有性能问题.那么理想的方式是什么? (4认同)

Wou*_*rts 15

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

  • 在您的第一个选择中,子类的日志最终在超类的类别中.这对我来说似乎非常违反直觉.
  • 你的第一个案例有一个变种:

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

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

  • 在第二个选择(私有静态)中,日志类别是包含日志记录代码的类.通常是正在记录事物的类.

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

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

它也有缺点:

  • 它需要在您记录消息的每个类中声明(不重用超类记录器).
  • 在初始化记录器时,您需要注意放置正确的类名.(但是好的IDE会为你处理这个问题).


Way*_*len 5

使用控制反转并将记录器传递给构造函数。如果您在类中创建记录器,您的单元测试将会有一段时间。您正在编写单元测试,不是吗?

  • 检查您生成的日志的单元测试听起来既无用又非常脆弱。 (6认同)
  • 有用性取决于被测系统。有时日志是您可以访问的全部内容。 (3认同)
  • 当然。发送电子邮件的记录器怎么样。不想每次运行测试时都这样做。另外你如何断言副作用? (3认同)