为什么我们将Loggers声明为static final?


131

在Java中,为什么最佳实践是声明记录器static final

private static final Logger S_LOGGER

Answers:


209
  • private-这样其他任何人都无法劫持您的记录器
  • static -因此每个类只有一个记录器实例,也避免了尝试对记录器进行序列化
  • final -无需在课堂上更改记录器

另外,我更喜欢名字 log尽可能简单,但具有描述性。

编辑:但是这些规则有一个有趣的例外:

protected final Logger log = LoggerFactory.getLogger(getClass());

相对于:

private static final Logger log = LoggerFactory.getLogger(Foo.class);

前一种方法允许您在整个继承层次结构的所有类中使用相同的记录器名称(实际类的名称)。因此,如果Barextends Foo,两者都将记录到Bar记录器。有些人觉得它更直观。


36
如果是静态的和最终的,则取LOG(大写)
zacheusz 2011年

39
@zacheusz,我知道,这就是重点。有些人虔诚地遵循Java命名约定(这没什么不对),但是我更喜欢写起来更容易读log名字,而不喜欢用分散代码LOG。只是开发人员的问题。团队协议。
Tomasz Nurkiewicz 2011年

27
请注意,不再总是建议将记录器声明为静态和最终的,请参见slf4j.org/faq.html#declared_staticwiki.apache.org/commons/Logging/FrequentlyAskedQuestions部分我应该声明日志引用为静态还是非静态?
马修·法威尔


2
@zacheusz不是所有的静态最终属性应该是大写:stackoverflow.com/questions/1417190/...
bsmk

15

检查此博客文章:摆脱Java Static Loggers。这是将slf4j与jcabi-log结合使用的方式

import com.jcabi.log.Logger;
class Foo {
  void save(File f) {
    Logger.info(this, "file %s saved successfully", f);
  }
}

再也不要使用该静态噪声了。


一个有趣的选择,而且绝对干净。我想知道与单独的类记录器相比,它的扩展性如何。
罗斯

12
每次写入更长的Logger ..(此,...)。没事
米哈伊尔·博雅斯基2015年

相关博客文章中的第一条评论指出了静态方法的弊端:)因此,我认为使用私有最终Logger是最佳实践。
Bahadir Tasdemir

5

static意味着每个类仅创建一个Logger,而不是每个类实例创建一个Logger 。通常,这就是您想要的-因为记录器往往仅根据类而有所不同。

final表示您不会更改logger变量的值。的确如此,因为您几乎总是将所有日志消息(来自一个类)扔给同一记录器。即使在极少数的情况下,一个类可能希望向其他记录器发送一些消息,创建另一个记录器变量(例如widgetDetailLogger)也比通过动态更改静态变量的值更加清晰。


4

您何时想更改该字段的值?

如果你永远也不会改变的值,使得现场最终使其明显,你永远不会改变的价值。


1
在许多情况下,不添加final一词是显而易见的,在这种情况下,这变成了一种垃圾。
迪马

2
@Dima:我还是很感激,如果我在这些情况下小心尝试更改值,编译器仍然会抛出错误……
Jon Skeet 2015年

3

通常,您将记录器初始化为使用类名进行记录-这意味着,如果它们不是静态的,则最终会导致该类的每个实例都有一个实例(占用大量内存),但是所有这些记录器都会共享相同的配置,并且行为完全相同。这就是背后的原因static。另外,由于每个Logger类都使用类名进行了初始化,因此为了避免与子类发生冲突,请对其进行声明,private以使其无法继承。在final出现这种情况,是因为您通常Logger在执行过程中不会更改-因此,一旦初始化就永远不会“重新配置”它-在这种情况下,有必要将其确定为最终确定以确保没有人可以更改它(通过错误或其他)。当然,如果您要使用Logger以不同的方式,您可能需要来使用static final-但我冒昧猜测应用程序的80%的人会使用日志记录如上所述。


3

要回答这个问题,您应该问自己“静态”和“最终”是什么意思。

对于Logger(我假设您谈论Log4J Logger类),您希望每个类都有一个类别。这应该导致这样的事实,您只分配一次,并且每个类不需要多个实例。并且大概没有理由将一类的Logger对象公开给另一类,因此为什么不将其设为私有并遵循某些OO原理。

您还应该注意,编译器可以利用这一点。因此您的代码执行起来会更好:)


2

因为通常这是可以在对象的所有实例之间共享的一种功能。对于同一个类的两个实例,使用不同的记录器是没有任何意义的(90%的时间)。

但是,有时您还会看到记录器类声明为单例,甚至只是提供静态函数来记录您的内容。


2

该代码容易受到攻击,但是,在Java7之后,我们可以使用它Logger lgr = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 代替静态记录器。


This is code is vulnerable您能否澄清一下答案?
德米特里·扎戈鲁尔金

1

在大多数情况下,您不会更改参考,而final修饰符会对其进行标记。您不需要为每个类实例使用单独的实例-因此static。和第一这一切是性能 -它可以很好地优化(最终)和节省内存(静态)。


1

理想情况下,直到Java 7为止,Logger都应遵循以下规定,以便不给出Sonar并给出合规代码:private:永远不能在其父类之外访问。如果另一个类需要记录某些内容,则应实例化其自己的记录器。静态:不依赖于类(对象)的实例。在记录某些内容时,当然可以在消息中提供上下文信息,但是应该在类级别创建记录器,以防止与每个对象一起创建记录器,从而防止占用大量内存。final:每个班级只能创建一次。


0

除了其他答案中给出的原因外,我遇到的一件事是,如果我的记录器既不是静态的也不是最终的:

...
public Logger logger = LoggerFactory.getLogger(DataSummary.class);

public String toJson() {
  GsonBuilder gsonBuilder = new GsonBuilder();   
  return gsonBuilder.create().toJsonTree(this).toString();
}
...

在某些情况下(当我使用Gson库时),我会得到stackoverflow异常。我的具体情况是实例化包含非静态非最终记录器的类。然后调用调用GsonBuilder的toJson方法:

...
DataSummary ds = new DataSummary(data);    
System.out.println(ds.toJson());
...

0

实际上,静态记录器可能“有害”,因为它们应该在静态上下文中工作。当具有动态环境时 OSGi可能有助于使用非静态记录器。由于某些日志记录实现会在内部缓存日志记录器(AFAIK至少为log4j),因此对性能的影响可以忽略不计。

静态记录器的一个缺点是例如。垃圾回收(当一个类仅使用一次时,例如,在初始化期间,记录器仍会保留)。

有关更多详细信息,请检查:

也可以看看:


0

根据我从Internet上获得的有关使记录器静态或不静态的信息,最佳实践是根据用例使用它。

有两个主要参数:

1)当您将其设为静态时,它不会被垃圾回收(内存使用和性能)。

2)当您不使其静态时,将为每个类实例创建一个(内存用法)

因此,当您为单例创建记录器时,无需使其静态。因为只有一个实例,所以只有一个记录器。

另一方面,如果要为模型或实体类创建记录器,则应使其不创建重复的记录器为静态。


-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.