java中的日志处理简介
在Java中我们可以使用自定义的、可扩展的日志处理方式。我们不仅可以使用Java中java.util.logging包提供的基本的日志相关的API来进行日志的处理,也可以使用一种或者多种其它的日志处理办法。
这些方法虽然在创建日志数据时会使用不同的方式,但是他们的目的都只有一个:将日志信息从应用程序中输出到具体的位置(这些位置可以是控制台,也可以是文件)。
java中日志处理的原理分析
探索Java日志背后的原理,并说明如何通过日志来让你成为一个更好的Java开发人员。
基本的日志组件
Java日志API包含如下的三个核心组件:
- Loggers:Logger负责捕捉日志事件并将其发送给合适的Appender。
- Appenders:也被称为Handlers,负责将日志事件记录到目标位置。在将日志事件输出之前, Appenders使用Layouts来对事件进行格式化处理。
- Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。
日志处理过程描述
当Logger捕捉到一个日志事件时,它会将事件转发给适当的Appender。然后Appender使用Layout来对日志记录进行格式化,并将其发送给控制台、文件或者其它目标位置。另外,Filters可以让你进一步指定一个Appender是否可以应用在一条特定的日志记录上。在日志配置中,Filters并不是必需的,但可以让你更灵活地控制日志消息的流动。
各个组件详情
Loggers:
Loggers是用来触发日志事件的对象,在我们的Java应用程序中被创建和调用,然后Loggers才会将事件传递给Appender。一个类中可以包含针对不同事件的多个独立的Loggers,你也可以在一个Loggers里面内嵌一个Loggers,从而创建一种Loggers层次结构。
创建新Logger
在不同的日志框架下面创建新Logger过程大同小异,尽管调用的具体方法名称可能不同。在使用 java.util.logging 时,你可以通过 Logger.getLogger().getLogger() 方法创建新Logger,这个方法接收一个string参数,用于指定Logger的名字。如果指定名字的Logger已经存在,那么只需要返回已经存在的Logger;否则,程序会创建一个新Logger。通常情况下,一种好的做法是,我们在当前类下使用 class.getName() 作为新Logger的名字。
Logger logger = Logger.getLogger(MyClass.class.getName());
记录日志事件
Logger提供了几种方法来触发日志事件。然而,在你记录一个事件之前,你还需要设置级别。日志级别用来确定日志的严重程度,它可以用来过滤日志事件或者将其发送给不同的Appender(想了解更多信息,请参考“日志级别”一节),Logger.log() 方法除了日志消息以外,还需要一个日志级别作为参数:
logger.log(Level.WARNING, “This is a warning!”);
大部分日志框架都针对输出特定级别日志提供了快捷方式。例如,下面语句的作用和上面语句的作用是一样的:
logger.warning(“This is a warning!”);
你还可以阻止Logger输出低于指定日志级别的消息。在下面的示例中,Logger只能输出高于WARNING级别的日志消息,并丢弃日志级别低于WARNING的消息:
logger.setLevel(Level.WARNING);
我们还有另外一些方法可以用来记录额外的信息。logp()(精确日志)可以让你指定每条日志记录的源类(source class)和方法,而 logrb()(使用资源绑定的日志)可以让你指定用于提取日志消息的资源。entering() 和 exiting() 方法可以让你记录方法调用信息,从而追踪程序的执行过程。
Appenders
Appenders将日志消息转发给期望的输出。它负责接收日志事件,使用Layout格式化事件,然后将其发送给对应的目标。对于一个日志事件,我们可以使用多个Appenders来将事件发送到不同的目标位置。例如,我们可以在控制台上显示一个简单的日志事件的同时,将其通过邮件的方式发送给指定的接收者。
请注意,在java.util.logging中,Appenders被称作Handlers。
增加Appender
大部分日志框架的Appender都会执行类似的功能,但在实现方面大相径庭。如果使用 java.util.logging,你可以使用 Logger.addHandler() 方法将Appender添加到Logger中。例如,下面的代码添加了一个新的ConsoleHandler,它会将日志输出到控制台:
logger.addHandler(new ConsoleHandler());
一种更常用的添加Appender的方式是使用配置文件。如果使用 java.util.logging,Appenders会定义一个以逗号隔开的列表,下面的示例将日志事件输出到控制台和文件:
handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler
如果使用基于XML的配置文件,Appenders会被添加到元素下面,如果使用Log4j,我们可以很容易地添加一个新ConsoleAppender来将日志消息发送到System.out:
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="[%p] %t: %m%n" />
</Console>
Appenders类型
这一节描述了一些更通用的Appenders,以及它们在各种日志框架中是如何实现的。
ConsoleAppender
ConsoleAppender是最常用的Appenders之一,它只是将日志消息显示到控制台上。许多日志框架都将其作为默认的Appender,并且在基本的配置中进行预配置。例如,在Log4j中ConsoleAppender的配置参数如下所示。
参数 | 描述 |
---|---|
filter | 用于决定是否需要使用该Appender来处理日志事件 |
layout | 用于决定如何对日志记录进行格式化,默认情况下使用“%m%n”,它会在每一行显示一条日志记录 |
follow | 用于决定Appender是否需要了解输出(system.out或者system.err)的变化,默认情况是不需要跟踪这种变化 |
name | 用于设置Appender的名字 |
ignoreExceptions | 用于决定是否需要记录在日志事件处理过程中出现的异常 |
target | 用于指定输出目标位置,默认情况下使用SYSTEM_OUT,但也可以修改成SYSTEM_ERR |
一个完整的Log4j2的配置文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp">
<Appenders>
<Console name="MyAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="MyAppender"/>
</Root>
</Loggers>
</Configuration>
这个配置文件创建了一个名为MyAppender的ConsoleAppender,它使用PatternLayout来对日志事件进行格式化,然后再将其输出到System.out。元素对定义在程序代码中的Loggers进行了配置。在这里,我们只配置了一个LoggerConfig,即名为Root的Logger,它会接收哪些日志级别在ERROR以上的日志消息。如果我们使用logger.error()来记录一个消息,那么它就会出现在控制台上,就像这样:
An unexpected error occurred.
你也可以使用Logback实现完全一样的效果:
<configuration>
<appender name="MyAppender" class="ch.qos.Logback.core.ConsoleAppender">
<encoder>
<pattern>%m%n</pattern>
</encoder>
</appender>
<root level="error">
<appender-ref ref="MyAppender" />
</root>
</configuration>
FileAppenders
FileAppenders将日志记录写入到文件中,它负责打开、关闭文件,向文件中追加日志记录,并对文件进行加锁,以免数据被破坏或者覆盖。
在Log4j中,如果想创建一个FileAppender,需要指定目标文件的名字,写入方式是追加还是覆盖,以及是否需要在写入日志时对文件进行加锁:
<Appenders>
<File name="MyFileAppender" fileName="myLog.log" append="true" locking="true">
<PatternLayout pattern="%m%n"/>
</File>
</Appenders>
这样我们创建了一个名为MyFileAppender的FileAppender,并且在向文件中追加日志时会对文件进行加锁操作。
如果使用Logback,你可以同时启用prudent模式来保证文件的完整性。虽然Prudent模式增加了写入文件所花费的时间,但它可以保证在多个FileAppender甚至多个Java程序向同一个文件写入日志时,文件的完整性。
<appender name="FileAppender" class="ch.qos.Logback.core.FileAppender">
<file>myLog.log</file>
<append>true</append>
<prudent>true</prudent>
<encoder>
<pattern>%m%n</pattern>
</encoder>
</appender>
SyslogAppender
SyslogAppenders将日志记录发送给本地或者远程系统的日志服务。syslog是一个接收日志事件服务,这些日志事件来自操作系统、进程、其它服务或者其它设备。事件的范围可以从诊断信息到用户登录硬件失败等。syslog的事件按照设备进行分类,它指定了正在记录的事件的类型。例如,auth facility表明这个事件是和安全以及认证有关。
Log4j和Logback都内置支持SyslogAppenders。在Log4j中,我们创建SyslogAppender时,需要指定syslog服务监听的主机号、端口号以及协议。下面的示例演示了如何设定装置:
<Appenders>
<Syslog name="SyslogAppender" host="localhost" port="514" protocol="UDP" facility="Auth" />
</Appenders>
在Logback中,我们可以实现同样的效果:
<appender name="SyslogAppender" class="ch.qos.Logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>514</port>
<facility>Auth</facility>
</appender>
其它Appender
我们已经介绍了一些经常用到的Appenders,还有很多其它Appender。它们添加了新功能或者在其它的一些Appender基础上实现了新功能。例如,Log4j中的RollingFileAppender扩展了FileAppender,它可以在满足特定条件时自动创建新的日志文件;SMTPAppender会将日志内容以邮件的形式发送出去;FailoverAppender会在处理日志的过程中,如果一个或者多个Appender失败,自动切换到其他Appender上。
如果想了解更多关于其他Appender的信息,可以查看Log4j Appender参考以及Logback Appender参考。
Layouts
Layouts将日志记录的内容从一种数据形式转换成另外一种。日志框架为纯文本、HTML、syslog、XML、JSON、序列化以及其它日志提供了Layouts。
请注意:在java.util.logging中Layouts也被称为Formatters。
例如,java.util.logging提供了两种Layouts:SimpleFormatter和XMLFormatter。默认情况下,ConsoleHandlers使用SimpleFormatter,它输出的纯文本日志记录就像这样:
Mar 31, 2015 10:47:51 AM MyClass main
SEVERE: An exception occurred.
而默认情况下,FileHandlers使用XMLFormatter,它的输出就像这样:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2015-03-31T10:47:51</date>
<millis>1427903275893</millis>
<sequence>0</sequence>
<logger>MyClass</logger>
<level>SEVERE</level>
<class>MyClass</class>
<method>main</method>
<thread>1</thread>
<message>An exception occurred.</message>
</record>
</log>
配置Layout
我们通常使用配置文件对Layouts进行配置。从Java 7开始,我们也可以使用system property来配置SimpleFormatter。
例如,在Log4j和Logback中最常用的Layouts是PatternLayout。它可以让你决定日志事件中的哪些部分需要输出,这是通过转换模式(Conversion Pattern)完成的,转换模式在每一条日志事件的数据中扮演了“占位符”的角色。例如,Log4j默认的PatternLayout使用了如下转换模式:
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
- %d{HH:mm:ss.SSS} 将日期转换成时、分、秒和毫秒的形式
- %level显示日志事件的严重程度
- %C显示生成日志事件的类的名字
- %t显示Logger的当前线程
- %m显示时间的消息
- %n为下一个日志事件进行了换行
改变Layouts
如果在java.util.logging中使用一个不同的Layout,需要将Appender的formatter属性设置成你想要的Layout。在代码中,你可以创建一个新的Handler,调用setFormatter方法,然后通过logger.AddHandler()方法将Handler放到Logger上面。下面的示例创建了一个ConsoleAppender,它使用XMLFormatter来对日志进行格式化,而不是使用默认的SimpleFormatter:
Handler ch = new ConsoleHandler();
ch.setFormatter(new XMLFormatter());
logger.addHandler(ch);
这样Logger会将下面的信息输出到控制台上:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2015-03-31T10:47:51</date>
<millis>1427813271000</millis>
<sequence>0</sequence>
<logger>MyClass</logger>
<level>SEVERE</level>
<class>MyClass</class>
<method>main</method>
<thread>1</thread>
<message>An exception occurred.</message>
</record>
如果想了解更多信息,你可以查看Log4j Layouts参考以及Logback Layouts参考。
使用自定义Layouts
自定义Layouts可以让你指定Appender应该如何输出日志记录。从Java SE 7开始,尽管你可以调整SimpleLogger的输出,但有一个限制,即只能够调整简单的纯文本消息。对于更高级的格式,例如HTML或者JSON,你需要一个自定义Layout或者一个单独的框架。
如果想了解更多使用java.util.logging创建自定义Layouts的信息,你可以查看Jakob Jenkov的Java日志指南中的Java Logging: Formatters章节。
日志级别
日志级别提供了一种方式,我们可以用它来根据严重程度对日志进行分类和识别。java.util.logging 按照严重程度从重到轻,提供了以下级别:
- SEVERE(最高级别)
- WARNING
- INFO
- CONFIG
- FINE
- FINER
另外, 还有两个日志级别:ALL和OFF。ALL会让Logger输出所有消息,而OFF则会关闭日志功能。
设置日志级别
在设定日志级别后,Logger会自动忽略那些低于设定级别的日志消息。例如,下面的语句会让Logger忽略那些低于WARNING级别的日志消息:
logger.setLevel(Level.WARNING);
然后,Logger会记录任何WARNING或者更高级别的日志消息。我们也可以在配置文件中设置Logger的日志级别:
<Loggers>
<Logger name="MyLogger" level="warning">
...
转换模式
Log4j和Logback中的PatternLayout类都支持转换模式,它决定了我们如何从每一条日志事件中提取信息以及如何对信息进行格式化。下面显示了这些模式的一个子集,对于Log4j和Logback来说,虽然这些特定的字段都是一样的,但是并不是所有的字段都会使用相同的模式。想要了解更多信息,可以查看Log4j和Logback的PatternLayout文档。
字段名称 | Log4j/Logback 模式 |
---|---|
消息 | %m |
级别/严重程度 | %p |
异常 | %ex |
线程 | %t |
Logger | %c |
方法 | %M |
例如,下面的PatternLayout会在中括号内x显示日志级别,后面是线程名字和日志事件的消息:
[%p] %t: %m
下面是使用了上述转换模式后的日志输出示例:
[INFO] main: initializing worker threads
[DEBUG] worker: listening on port 12222[INFO] worker: received request from 192.168.1.200[ERROR] worker: unknown request ID from 192.168.1.200
日志框架
在Java中,输出日志需要使用到一个或多个日志框架,这些框架提供了必要的对象、方法和配置来传输消息。
常用的日志框架:
- java.util.logging(java中默认提供的日志框架)
- Log4j(最新版本(版本2)中进行了一些改进,如支持多API、提升了在用Disruptor库的性能)
- Logback(基于Log4j之前的版本开发 —-版本1,所以其功能集合与Log4j类似)
- tinylog(由于缺少了一些功能,运行特别快,非常适合小项目)
在Java中的包下提供了一个默认的框架。除此之外,还有很多其它第三方框架,包括还有其它一些开发包,例如SLF4J
和Apache-Commons-Logging
,它们提供了一些抽象层,对你的代码和日志框架进行解耦,从而允许你在不同的日志框架中进行切换。
如何选择合适的日志处理方式
如何选择一个日志解决方案,需要考虑的因素比较的多:
- 比如你的日志需求的复杂度
- 其它日志解决方案的兼容性
- 易用性以及个人喜好
- 框架在基于Java的各种不同项目上的支持程度。例如Android程序只能使用Log4j、Logback或者第三方包来记录日志, Apache Tomcat可以使用Log4j来记录内部消息,但只能使用版本1的Log4j。
日志抽象层
诸如SLF4J这样的抽象层,会将你的应用程序从日志框架中解耦。应用程序可以在运行时选择绑定到一个特定的日志框架(例如java.util.logging、Log4j或者Logback),这通过在应用程序的类路径中添加对应的日志框架来实现。如果在类路径中配置的日志框架不可用,抽象层就会立刻取消调用日志的相应逻辑。抽象层可以让我们更加容易地改变项目现有的日志框架,或者集成那些使用了不同日志框架的项目。
日志的配置
尽管所有的Java日志框架都可以通过代码进行配置,但是大部分配置还是通过外部配置文件完成的。这些文件决定了日志消息在何时通过什么方式进行处理,日志框架可以在运行时加载这些文件。
java.util.logging
默认的Java日志框架 java.util.logging会将其配置存储到一个名为 logging.properties 的文件中:
- 在这个文件中,每行是一个配置项,配置项使用点标记(dot notation)的形式。
- Java在其安装目录的lib文件夹下面安装了一个全局配置文件,但在启动一个Java程序时,你可以通过指定 java.util.logging.config.file 属性的方式来使用一个单独的日志配置文件,同样也可以在个人项目中创建和存储 logging.properties 文件。
下面的示例描述了如何在全局的logging.properties文件中定义一个Appender:
#default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XmlFormatter
Log4j
Log4j版本1使用的语法和 java.util.logging 的语法很类似。使用了Log4j的程序会在项目目录中寻找一个名为 log4j.properties 的文件。默认情况下,Log4j配置会将所有日志消息输出到控制台上。Log4j同样也支持XML格式的配置文件,对应的配置信息会存储到 log4j.xml 文件中。
Log4j版本2支持XML、JSON和YAML格式的配置,这些配置会分别存储到 log4j2.xml、log4j2.json 和 log4j2.yaml 文件中。和版本1类似,版本2也会在工程目录中寻找这些文件。你可以在每个版本的文档中找到相应的配置文件示例。
Logback
对于Logback来说,大部分配置都是在 logback.xml 文件中完成的,这个文件使用了和Log4j类似的XML语法。Logback同时也支持通过Groovy语言的方式来进行配置,配置信息会存储到 logback.groovy 文件中。你可以通过每种类型配置文件的链接找到对应的配置文件示例。
java日志的使用实例
记录栈跟踪信息
如果你在Java程序中使用过异常,那么很有可能已经看到过栈跟踪信息。它提供了一个程序中方法调用的快照,让你准确定位程序执行的位置。例如,下面的栈跟踪信息是程序试图打开一个不存在的文件后生成的:
[ERROR] main: Unable to open file! java.io.FileNotFoundException: foo.file (No such file or directory)
at java.io.FileInputStream.open(Native Method) ~[?:1.7.0_79]
at java.io.FileInputStream.<init>(FileInputStream.java:146) ~[?:1.7.0_79]
at java.io.FileInputStream.<init>(FileInputStream.java:101) ~[?:1.7.0_79]
at java.io.FileReader.<init>(FileReader.java:58) ~[?:1.7.0_79]
at FooClass.main(FooClass.java:47)
这个示例使用了一个名为FooClass的类,它包含一个main方法。在程序第47行,FileReader独享试图打开一个名为foo.file的文件,由于在程序目录下没有名字是foo.file的文件,因此Java虚拟机抛出了一个FileNotFoundException。因为这个方法调用被放到了try-catch语块中,所以我们能够捕获这个异常并记录它,或者至少可以阻止程序崩溃。
PatternLayout记录栈跟踪信息
在写本篇文章时最新版本的Log4j和Logback中,如果在Layout中没有和可抛异常相关的信息,那么都会自动将%xEx(这种栈跟踪信息包含了每次方法调用的包信息)添加到PatternLayout中。如果对于普通的日志信息的模式如下:
[%p] %t: %m
它会变为:
[%p] %t: %m%xEx
这样不仅仅错误信息会被记录下来,完整的栈跟踪信息也会被记录:
[ERROR] main: Unable to open file! java.io.FileNotFoundException: foo.file (No such file or directory)
at java.io.FileInputStream.open(Native Method) ~[?:1.7.0_79]
at java.io.FileInputStream.<init>(FileInputStream.java:146) ~[?:1.7.0_79]
at java.io.FileInputStream.<init>(FileInputStream.java:101) ~[?:1.7.0_79]
at java.io.FileReader.<init>(FileReader.java:58) ~[?:1.7.0_79]
at FooClass.main(FooClass.java:47)
%xEx中的包查询是一个代价昂贵的操作,如果你频繁的记录异常信息,那么可能会碰到性能问题,例如:
// ...
} catch (FileNotFoundException ex) {
logger.error(“Unable to open file!”, ex);
}
解决方法
一种解决方法是在模式中显式的包含%ex,这样就只会请求异常的栈跟踪信息:
[%p] %t: %m%ex
另外一种方法是通过追加%xEx(none)的方法排除(在Log4j)中所有的异常信息:
[%p] %t: %m%xEx{none}
或者在Logback中使用%nopex:
[%p] %t: %m%nopex
使用结构化布局输出栈跟踪信息
如你在“解析多行栈跟踪信息”一节中所见,对于站跟踪信息来说,使用结构化布局来记录是最合适的方式,例如JSON和XML。 这些布局会自动将栈跟踪信息按照核心组件进行分解,这样我们可以很容易将其导出到其他程序或者日志服务中。对于上述站跟踪信息,如果使用JSON格式,部分信息显示如下:
...
"loggerName" : "FooClass",
"message" : "Foo, oh no! ",
"thrown" : {
"commonElementCount" : 0,
"localizedMessage" : "foo.file (No such file or directory)",
"message" : "foo.file (No such file or directory)",
"name" : "java.io.FileNotFoundException",
"extendedStackTrace" : [ {
"class" : "java.io.FileInputStream",
"method" : "open",
"file" : "FileInputStream.java",
...
记录未捕获异常
通常情况下,我们通过捕获的方式来处理异常。如果一个异常没有被捕获,那么它可能会导致程序终止。如果能够留存任何日志,那么这是一个可以帮助我们调试为什么会发生异常的好办法,这样你就可以找到发生异常的根本原因并解决它。下面来说明我们如何建立一个默认的异常处理器来记录这些错误。
Thread类中有两个方法,我们可以用它来为未捕获的异常指定一个ExceptionHandler:
- setDefaultUncaughtExceptionHandler 可以让你在任何线程上处理任何异常。
- setUncaughtExceptionHandler可以让你针对一个指定的线程设定一个不同的处理方法。
而ThreadGroup则允许你设定一个处理方法。大部分人会使用默认的异常处理方法。
下面是一个示例,它设定了一个默认的异常处理方法,来创建一个日志事件。它要求你传入一个UncaughtExceptionHandler:
import java.util.logging.*;
public class ExceptionDemo {
private static final Logger logger = Logger.getLogger(ExceptionDemo.class);
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
logger.log(Level.SEVERE, t + " ExceptionDemo threw an exception: ", e);
};
});
class adminThread implements Runnable {
public void run() {
throw new RuntimeException();
}
}
Thread t = new Thread(new adminThread());
t.start();
}
}
下面是一个未处理异常的输出示例:
May 29, 2015 2:21:15 PM ExceptionDemo$1 uncaughtException
SEVERE: Thread[Thread-1,5,main] ExceptionDemo threw an exception:
java.lang.RuntimeException
at ExceptionDemo$1adminThread.run(ExceptionDemo.java:15)
at java.lang.Thread.run(Thread.java:745)
推荐的java日志处理方式:使用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>
参考资料:http://www.importnew.com/16331.html
使用SLF4J做日志处理 :http://www.oschina.net/translate/why-use-sl4j-over-log4j-for-logging