从静态最终变量初始值设定项获取Logger是否有效?

loc*_*cka 7 java log4j

我们有很多类代码,它们有一些如下所示的样板:

private static Logger logger = null;

private static Logger getLogger() {
  if (logger == null) {
    logger = Logger.getLogger(MyClass.class);
  }
  return logger;
}
Run Code Online (Sandbox Code Playgroud)

这个想法是类可以将调试内容记录到Logger中.需要记录某些东西的第一个代码调用getLogger()并使记录器存在.

关于这种模式,有几件我不喜欢的事情.首先,单例getLogger()不同步并同步它,而正确会无缘无故地给每个后续调用带来负担.

我真的希望能够将它简化为这样:

private static final Logger logger = Logger.getLogger(MyClass.class);
Run Code Online (Sandbox Code Playgroud)

然后我可以直接引用记录器,甚至不用单独的getter.

我担心的问题是,通过这样做,即使从未调用过记录器,我也会在加载类时创建一个Logger.我有10,000多个奇怪的类都调用了getLogger(),所以我实际上在这里创建了多少个Logger实例?如果我的log4j属性包含一些appender,我只是一遍又一遍地引用相同的记录器,或者我是在创建10,000个这样的东西?

Ido*_*lon 2

如果您使用默认的 Log4j 配置(即默认 LoggerRepository、DefaultCategoryFactory 等),那么您将创建 10'000 个 Logger 实例。它们消耗多少内存?除了上帝和你的分析器之外没有人知道这一点。(我猜只有后一个会告诉你这一点)。

如果内存占用对于您的环境来说太大,请将 Logger 初始化移至静态内部类,如下所示:

static class LoggerHolder {
  static Logger logger = Logger.getLogger(MyClass.class);
}

private static Logger getLogger() {
  return LoggerHolder.logger;
}
Run Code Online (Sandbox Code Playgroud)

这样, 的实例Logger将仅在第一次调用时创建getLogger。(这种技术称为按需初始化持有者(IODH),它是线程安全的并且具有零同步开销)。

我可以给你一个题外话的建议吗?考虑用SLF4J + Logback库的组合来替换Log4J 。它们是由同一位作者写的,并被描述为“ a successor to the popular log4j project, picking up where log4j leaves off”。您可以在此 SO 线程中阅读更多内容。