加载中
Every Java programmers knows that logging is critical for any Java application, especially server side application, and many of them are already familiar with various logging libraries e.g. java.util.logging, Apache log4j, logback, but if you don't know about SLF4J, Simple logging facade for Java,  then it's time to learn and use SLF4J in your project. In this Java article, we will learn why using SLF4J is better than using log4j or java.util.logging. It’s been long time, since I wrote 10 logging tips for Java programmer,I don’t remember anything I have writing about logging. Anyway, let’s get back to topic, on contrary to all those logging libraries, there is a major difference between them and SLF4J. SLF4J or Simple logging Facade for Java is not really a logging implementation, instead it's an abstraction layer, which allows you to use any logging library in back-end. If you are writing API or utility library, which can be used internally or externally, then you really don't want that any client, which uses your library, should also stick with your choice of logging library. Suppose if a project is already using log4j, and you included a library say Apache Active MQ, which has dependency on logback, another logging library, then you need to include them as well, but if Apache Active MQ uses SL4J, you can continue with your logging library, without pain of adding and maintaining new logging framework. In short SLF4J make your code independent of any particular logging API, which is good think for public API developers. Though idea of abstracting logging library is not new and Apache commons logging is already using it, but now SLF4J is quickly becoming an standard for logging in Java world. Let's see couple of more reason to use SLF4J over log4j, logback or java.util.logging.
每个Java开发人员都知道日志记录对Java应用的重要性,尤其是对服务端应用,而且其中许多人都已经熟悉了各种记录日志的库,比如java.util.logging,Apache的log4j,logback,然而如果你不知道SLF4J,java的简单记录日志的设计的话 ,那么到了学习并在你的项目中使用它的时候了。在这篇Java文档里,我们将学习为什么使用SLF4J比使用log4j或者java.util.logging更好。从我写 Java开发人员的10个记录日志的技巧 算起已经过去了很长一段时间了。我不记得我所写的有关日志记录的任何事情了。无论如何,让我们回归到这个主题上来,与所有提到的这些日志记录库相比,SLF4J与它们之间有一个主要的区别。SLF4J或者说是Java的简单记录日志设计没有真正地实现日志记录,相反它只是一个允许你使用任何处于后端的日志记录库的 抽象层 。如果你正在编写内部或者外部使用的API或者应用库的话,那么你真的不需要让使用你所编写的库的客户端还去选择日志库。假设项目已经使用了log4j,而且你包含一个名为Apache Active MQ的库,这个库还依赖于另一个日志记录库logback的话,那么你还需要包含它们,然而,如果Apache Active MQ使用了SLF4J的话,你可以继续使用你的日志记录库,而不需要痛苦地添加和维护新的日志记录框架。简短的说,SLF4J让你的代码独立于任何特定的日志记录API,这个好的想法尤其适合于公共的API开发人员。虽然日志记录库的抽象理念不是新的,而且Apache的commons logging日志记录库也是用了这个理念,不过现在SLF4J很快就会成为Java世界里标准的日志记录库。让我们看一些使用 SLF4J而不使用log4j,logback或者java.util.logging的理由。

Prefer SLF4J over Log4J, logback and java.util.Logging

As I said earlier, main motivation of using SLF4J in your code to write log statements is, to make your program, independent of any particular logging library, which might require different configuration than you already have, and introduce more maintenance headache. But apart from that, there is one more feature of SLF4J API, which convinced me to use SL4J over my long time favorite Log4j, that is know as place holder and represented as {} in code. Placeholder is pretty much same as %s in format() method of String, because it get substituted  by actual string supplied at runtime. This not only reduce lot of String concatenation in your code, but also cost of creating String object. This is true even if you might not need that, depending upon your log level in production environment e.g. String concatenation on DEBUG and INFO levels. Since Strings are immutable and they are created in String pool, they consume heap memory and most of the time they are not needed e.g. an String used in DEBUG statement is not needed, when your application is running on ERROR level in production. By using SLF4J, you can defer String creation at runtime, which means only required Strings will be created. If you have been using log4j then you already familiar with a workaround of putting debug statement inside if() condition, but SLF4J placeholders are much better than that.

宁愿使用SLF4J也不愿使用Log4J,logback和java.util.Logging

正如我前面所说,在你的代码中编写日志记录语句使用SLF4J的主要动机是让你的程序独立于任何特定的日志记录库,这些日志记录库可能需要与你现在配置不同的配置,而且还会引入更多令人头疼的维护问题。然而除了这个之外,SLF4J API还有一个让你使用SLF4J而不是用长期感兴趣的 Log4j 更让人信服的功能,也就是占位符功能,在代码中用{}来表示。占位符功能与 String的format()方法中 的%s非常相似,因为它在运行时刻才提取所提供的真正的字符串。这不仅缩减了代码中的许多字符串连接,而且减少了创建String对象所需要的资源。即便在你生产环境日志级别比如DEBUG和INFO级别的字符串连接可能不需要的时候,仍然可以起到同样的效果。由于 字符串是不可更改的 ,而且它们是在字符串池中创建的,这些字符串使用了 堆内存 ,当应用在生产环境中运行在ERROR级别的时候,字符串在大多数情况下就不是必须的,比如DEBUG语句里的字符串就不是必须的。通过使用SLF4J,你可以延迟字符串的创建到运行时刻,这意味着只有在需要字符串的时候才创建它。如果你已经使用了log4j,那么你已经熟悉把调试语句放入if()条件内的工作场景,而SLF4J占位符功能比log4j更适合这种场景。

This is how you would do in Log4j, but surely this is not fun and reduce readability of code by adding unnecessary boiler-plate code.

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

On the other hand if you use SLF4J, you can get same result in much concise format as shown below :

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

In SLF4J, we don't need String concatenation and don't incur cost of temporary not need String. Instead, we write log message in a template format with placeholder and supply actual value as parameters. You might be thinking about what if I have multiple parameters, well you can either use variable arguments version of log methods or pass them as Object array. This is really convenient and efficient way of logging. Remember, before generating final String for logging message, this method check if a particular log level is enabled or not, which not only reduce memory consumption but also CPU time involved for executing those String concatenation instruction in advance. Here is the code of SLF4J logger method from it's Log4j Adapter class Log4jLoggerAdapter from slf4j-log4j12-1.6.1.jar.

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());
    }
}

It's also worth knowing that logging has severe impact on performance of application, and it's always advised to only mandatory logging in production environment.

下面是你用Log4j时的做法,当然这并不好玩而且它增加了不必要的公式化的代码,减少了代码的可读性。

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

而如果你使用SLF4J,你可以使用更简洁的格式达到同样的效果,如下:

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

在SLF4J中,我们不需要进行字符串拼接,不会导致使用临时字符串带来的消耗。相反,我们使用带占位符的模板消息来记录日志信息,并提供实际值作为参数。也许你会想,要是有多个参数该怎么办,你可以使用带参数版的日志方法,也可以通过Object数组传入。这确实是非常方便而且高效的记日志的方法。记住,在为日志信息产生最终的字符串之前,该方法会检查是否开启了特定的日志级别,这不仅降低了内存占用,而且预先减少了执行字符串拼接所消耗的CPU时间。下面的SLF4J日志方法的代码,来自于slf4j-log4j12-1.6.1.jar包里的Log4j的适配器类Log4jLoggerAdapter.

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());
    }
}

同样值得了解是,日志也会对应用程序的性能产生压力,大家通常宣扬的是只在生产环境中才强制记录日志。

How to use SLF4J with Log4J for logging

Apart from above benefits, I think there is one caveat though, in order to use SLF4J you not only need to include SLF4J API Jar e.g. slf4j-api-1.6.1.jar, but also companion JAR, depending upon which logging library, you are using in backend. Suppose If you want to use SLF4J, Simple Logging Facade for Java,  along with Lo4J, you need to include following jars in your classpath, depending upon which version of SLF4J and log4J you are using e.g.

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

If you are using Maven to mange dependency in your project, you can just include SLF4J JAR, and maven will include it's dependent companion JAR. In order to use Log4J along with SLF4J, you can include following dependency in your project's pom.xml

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

By the way, if you are interested in using variable argument version of logger methods, than include SLF4J 1.7 version.

如何使用SLF4J和Log4J来做日志

除了上面所说的好处,我认为还有个警告需要说一下,为了使用SLF4J你不仅需要进入SLF4J API Jar包,比如slf4j-api-1.6.1.jar,还需要引入协同工作的JAR包,具体是什么jar包则依赖于后端你使用了什么日志工具库。假如你想使用SLF4J,Simple Logging Facade for Java,还想使用Lo4J,那么你需要把下列jar包引入到你的classpath中,具体版本要视你使用的SLF4J和log4J版本而定, 比如:

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会引入它所依赖的其它JAR包。为了使用Log4J和SLF4J,你可以在你项目的pom.xml中添加下列依赖:

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

顺便说一下,如果你对使用带参数版的日志方法感兴趣,那就需要引入SLF4J 1.7版本。

Summary

To summarize this post, I would suggest following reasons are good enough to choose SLF4J over Log4j, commons logging, logback or java.util.logging directly.

1) Using SLF4J in your open source library or internal library, will make it independent of any particular logging implementation, which means no need to manage multiple logging configuration for multiple libraries, your client will going to appreciate this.

2) SLF4J provides place holder based logging, which improves readability of code by removing checks lie isDebugEnabled(), isInfoEnabled() etc.

3) By using SLF4J logging method, you defer cost of constructing logging messages (String), until you need it, which is both memory and CPU efficient.

4) As a side note, less number of temporary strings means less work for Garbage Collector, which means better throughput and performance for your application.

These advantages are just tip of iceberg, you will learn about more benefits, when you start using SL4J and reading about it.  I strongly suggest, any new code development in Java, should use SLF4J for logging over any other logging API including log4J.

总结

总结这篇文章,我具有充分的理由的来选择SLF4J而不是直接选用Log4j, commons logging, logback 或者 java.util.logging。

1)在你的开源库或者私有库中使用SLF4J,可以使它独立于任何的日志实现,这就意味着不需要管理多个库和多个日志文件。你的客户端将会体会到这一点。

2)SLF4J提供了占位日志记录,通过移除对isDebugEnabled(), isInfoEnabled()等等的检查提高了代码的可读性。

3)通过使用日志记录方法,直到你使用到的时候,才会去构造日志信息(字符串),这就同时提高了内存和CPU的使用率。

4)做一个侧面的说明,越少的临时字符串,垃圾回收器就意味着越少的工作,这就意味着为你的应用程序提供更好的吞吐量和性能。

这些优势都只是冰山一角,当你开始使用SL4J并阅读它,你会学到更多的好处。我强烈建议,在java中任何新的代码开发,都应使用SLF4J而不是任何的日志API,包括log4J。

返回顶部
顶部