[转载]为什么使用 SLF4J 而不是Log4J来做Java 日志

转载自《http://www.oschina.net/translate/why-use-sl4j-over-log4j-for-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的理由。


 

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

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

下面是你用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());
    }
}

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


如何使用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版本。

 

如何使用SLF4J和Logback来做日志

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <scope>test</scope>
</dependency>
具体的配置例子:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date %level [%thread] %logger [%file : %line] %msg%n
            </pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <logger name="org" level="INFO" />
    <root level="ALL">
        <appender-ref ref="stdout" />
    </root>
</configuration>

含义:<root>也是其实是一个name=root的logger,就是一个子节点。是所有root的父节点。

<logger name="org" level="INFO" />会接管org包下的日志,没有设置appender,表示此loger本身不打印任何信息;没有设置addtivity,默认为true,将此loger的打印信息向上级传递;但是只传递INFO级别(TRACE < DEBUG < INFO < WARN < ERROR)到root节点处理。

<root> 节点处理所有级别的输出,并将其全部输出到控制台。

在需要使用的类中:private final static Logger LOGGER = LoggerFactory.getLogger(LogProcessorImpl.class);

注意打出异常堆栈

//可以打出e的异常堆栈
 LOGGER.error("write to json string error: {}", paramBean, e);

//无法打出堆栈
 LOGGER.error("write to json string error: {}, {}", paramBean, e);

 

参考《http://www.cnblogs.com/cb0327/p/5759441.html

http://www.cnblogs.com/warking/p/5710303.html



posted @ 2017-06-06 19:06  神话小小哥  阅读(194)  评论(0编辑  收藏  举报