转载:为什么要使用 slf4j 而不是 log4j

每一个 java 程序员都知道日志对于任何一个 java 应用程序,尤其是服务端程序是至关重要的,而很多程序员也已经熟悉各种不同的日志库,如 java.util.logging、Apache log4j,以及 logback 等。但如果你还不知道 slf4j(Simple logging facade for Java)的话,那么是时候去在你项目中学习使用 slf4j 了。

在这篇文章中,我们将学习为什么使用 slf4j 比 log4j 或者 java.util.logging 要优秀。自从上次我写 java 程序员的 10 个日志技巧已经有一段时间了,我已经不记得我写的关于日志的一切了。

不管怎样,让我们回到这个话题,slf4j 不同于其他日志类库,与其它有很大的不同。 slf4j(Simple logging Facade for Java)不是一个真正的日志实现,而是一个抽象层(abstraction layer),它允许你在后台使用任意一个日志类库 。如果是在编写供内外部都可以使用的 API 或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库。

如果一个项目已经使用了 log4j,而你加载了一个类库,比方说 Apache Active MQ,它依赖于于另外一个日志类库 logback,那么你就需要把它也加载进去。但如果 Apache Active MQ 使用了 slf4j,你可以继续使用你的日志类库而无语忍受加载和维护一个新的日志框架的痛苦。

总的来说,slf4j 使你的代码独立于任意一个特定的日志 API,这是一个对于开发API的开发者很好的思想。虽然抽象日志类库的思想已经不是新鲜的事物而且 Apache commons logging 也已经在使用这种思想了,但现在 slf4j 正迅速成为 java 世界的日志标准。让我们再看看几个使用 slf4j 而不是 log4j、logback 或者 java.util.logging 的理由。

slf4j 对比 log4j,logback 和 java.util.Logging 的优势

正如我之前说的,在你的代码中使用 slf4j 写日志语句的主要出发点是使得你的程序独立于任意特定的日志类库,依赖于特定类可能需要不同与你已有的配置,并且导致更多维护的麻烦。但除此之外,还要一个 slf4j API 的特性使得我坚持使用 slf4j 而抛弃我长期间钟爱的 log4j 的理由,是被称为占位符(place holder),在代码中表示为 “{}” 的特性。占位符是一个非常类似于在 String 的 format() 方法中的 %s,因为它会在运行时被某个提供的实际字符串所替换。这不仅降低了你代码中字符串连接次数,而且还节省了新建的 String 对象。即使你可能没需要那些对象,但这个依旧成立,取决于你的生产环境的日志级别,例如在 DEBUG 或者 INFO 级别的字符串连接。因为 String 对象是不可修改的并且它们建立在一个 String 池中,它们消耗堆内存(heap memory)而且大多数时间他们是不被需要的,例如当你的应用程序在生产环境以 ERROR 级别运行时候,一个 String 使用在 DEBUG 语句就是不被需要的。

通过使用 slf4j,你可以在运行时延迟字符串的建立,这意味着只有需要的 String 对象才被建立。而如果你已经使用 log4j,那么你已经对于在 if 条件中使用 debug 语句这种变通方案十分熟悉了,但 slf4j 的占位符就比这个好用得多。

这是你在 log4j 中使用的方案,但肯定这一点都不有趣并且降低了代码可读性因为增加了不必要的繁琐重复代码(boiler-plate code):

1
2
3
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}

另一方面,如果你使用 slf4j 的话,你可以得到在极简洁的格式的结果,就像以下展示的一样:

1
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);

在 slf4j,我们不需要字符串连接而且不会导致暂时不需要的字符串消耗。取而代之的,我们在一个以占位符和以参数传递实际值的模板格式下写日志信息。你可能会在想万一我有很个参数怎么办?嗯,那么你可以选择使用变量参数版本的日志方法或者用以 Object 数组传递。这是一个相当的方便和高效方法的打日志方法。记住,在生产最终日志信息的字符串之前,这个方法会检查一个特定的日志级别是不是打开了,这不仅降低了内存消耗而且预先降低了 CPU 去处理字符串连接命令的时间。这里是使用 slf4j 日志方法的代码,来自于 slf4j-log4j12-1.6.1.jar 中的 log4j 的适配器类 log4jLoggerAdapter。

1
2
3
4
5
6
public void debug(String format, Object arg1, Object arg2) {
if (logger.isDebugEnabled()) {
FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
}
}

同时,我们也很值得知道打日志是对应用程序的性能有着很大影响的,在生产环节上只进行必要的日志记录是我们所建议的。

怎么用 slf4j 做 log4j 的日志记录

除了以上好处,我想还有一个告诫,就是为了使用 slf4j,你不仅需要包含 slf4j 的 API jar 包,例如 slf4j-api-1.6.1.jar,还需要相关 jar 包,这取决于你在后台使用的日志类库。如果你想要使用和 log4j 一起使用 slf4j ,Simple Logging Facade for Java,,你需要包含以下的 jar 包在你的 classpath 中,取决于哪个 slf4j 和你在使用的 log4j 的版本。例如:

1
2
3
slf4j-api-1.6.1.jar – JAR for slf4j API
log4j-1.2.16.jar – JAR for log4j API
slf4j-log4j12-1.6.1.jar – log4j Adapter for slf4j

如果你在使用 maven 去管理你的项目依赖,你只需要包含 slf4j jar 包,maven 会包含它的依赖的相关包。为了和 slf4j 一起中使用 log4j,你可以包含以下的依赖在你项目中的 pom.xml。

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>

还有,如果你对于使用变量参数版本(variable argument version )的日志方法感兴趣的话,那么就导入 slf4j 1.7 的版本吧。

总结

总结这次说的,我建议使用 slf4j 的而不是直接使用 log4j, commons logging, logback 或者 java.util.logging 已经足够充分了。

  1. 在你的开源或内部类库中使用 slf4j 会使得它独立于任何一个特定的日志实现,这意味着不需要管理多个日志配置或者多个日志类库,你的客户端会很感激这点。
  2. slf4j 提供了基于占位符的日志方法,这通过去除检查 isDebugEnabled(), isInfoEnabled() 等等,提高了代码可读性。
  3. 通过使用 slf4j 的日志方法,你可以延迟构建日志信息(Srting)的开销,直到你真正需要,这对于内存和CPU都是高效的。
  4. 作为附注,更少的暂时的字符串意味着垃圾回收器(Garbage Collector)需要做更好的工作,这意味着你的应用程序有为更好的吞吐量和性能。
  5. 这些好处只是冰山一角,你将在开始使用 slf4j 和阅读其中代码的时候知道更多的好处。我强烈建议,任何一个新的 java 程序员,都应该使用 slf4j 做日志而不是使用包括 log4j 在内的其他日志 API。

转自:http://www.importnew.com/7450.html