【问题标题】:Should logger be private static or not记录器是否应该是私有静态的
【发布时间】:2011-04-20 01:26:45
【问题描述】:

记录器是否应该被声明为静态的?通常我见过两种类型的记录器声明:

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

私有静态日志 log = new Log4JLogger(aClass.class);

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

【问题讨论】:

  • 日志记录是一个跨领域的关注点。使用方面,问题没有实际意义。
  • static 是每个班级的一个参考。非静态是每个实例一个引用(+初始化)。因此,在某些情况下,如果您有大量实例,后者会对内存产生重大影响。永远不要在 frequent 对象中使用非静态对象。我总是使用静态版本。 (应该大写 LOG
  • 如前所述,使用 AOP 和注解,例如:jcabi.com/jcabi-aspects/annotation-loggable.html
  • RobertHume 的静态版本使用一个常量。这正是它应该大写的原因。
  • 不应该是小写的private static final Log log。记录器不是一个常数,记录器是一个静态的最终对象(可以变异)。就我个人而言,我总是使用logger

标签: java logging


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 在抽象类中声明abstract Log getLogger();。实现此方法,返回特定实例的静态记录器。将 private final static Log LOG = LogManager.getLogger(Clazz.class); 添加到您的 IDE 类模板中。
  • 对于 slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
  • @BalusC 将 getClass() 传递给 getLogger 方法的问题在于它返回当前实例的类。通常,更希望将日志记录与代码所在的类相关联。例如,如果日志记录代码在 Parent 类中,那么我们希望日志记录与 Parent 相关联,即使执行实例是 Child 类的实例,它是 Parent 的子类。使用 getClass() 将错误地与子关联
  • @inor:“不正确”?如果你不想抽象类,那么你不应该首先使用继承的 getClass() 。有些开发人员确实发现它是正确且有用的,因为它揭示了逻辑已在哪个子类中执行的信息。
  • @BalusC getLogger(getClass()) 导致总是错误地记录子类的名称。日志记录类应始终执行 getLogger(Clazz.class) 以关联 Clazz 类中的代码生成的日志记录。想要知道哪些子类正在执行(例如 SubClazz 扩展 Clazz)的开发人员应该在 SubClazz 中执行:getLogger(SubClazz.class) 和类似:log.info("calling ");
【解决方案2】:

使用控制反转并将记录器传递给构造函数。如果您在类中创建记录器,您将在单元测试中度过难关。你不是在写单元测试吗?

【讨论】:

  • 单元测试检查你产生的日志,听起来既无用又非常脆弱。
  • 有用性取决于被测系统。有时日志是您可以访问的全部内容。
  • @Wayne Allen 当您进行单元测试时,根据定义,您也有测试结果。您是否建议进行单元测试但没有测试结果的情况?只有日志?
  • 当然。发送电子邮件的记录器怎么样。不想每次运行测试时都这样做。另外,您如何断言副作用?
  • 改变你的代码注入策略以适应你的测试从来都不是一个好主意。测试应该适应你的生产代码,而不是相反。有很多方法可以在不干预实际记录器实例的情况下测试日志输出 - 在测试中,只需将另一个(即定制的)捕获附加程序添加到日志管理器并检查传递给它的内容。
【解决方案3】:

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

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

    受保护的Log log = new Log4JLogger(getClass());

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

  • 在您的第二个选择(私有静态)中,日志类别是包含日志记录代码的类。所以通常情况下,正在做被记录的事情的类。

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

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

它也有缺点:

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

【讨论】:

    【解决方案4】:

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

    除了将记录器声明为非静态之外,我真的没有看到任何解决此类加载器泄漏问题的方法。

    【讨论】:

    • 我怀疑静态字段也会有内存泄漏问题。正如其他人所说,非静态可能存在性能问题。那么理想的方式是什么?
    • @piepera 您引用的文章中描述的主要问题是,当“考虑通过类加载器部署使用“私有静态日志日志 =”的类时,控制每个应用程序中的日志记录级别的能力那是多个所谓的独立“应用程序”的祖先。我不认为这是一个问题,因为在这种特殊情况下,应用程序有一个“共同点”,并且在那个“共同点”中,类的日志记录级别是确定的,是的,它适用于所有应用程序......但请保持请记住,此类在这些应用程序之外[加载]
    猜你喜欢
    • 2010-10-07
    • 2011-11-11
    • 1970-01-01
    • 1970-01-01
    • 2017-06-17
    • 1970-01-01
    • 1970-01-01
    • 2020-02-16
    • 1970-01-01
    相关资源
    最近更新 更多