前面的例子都是讲logback采用编码的方式来进行日志输出级别设置的,一般应该采用配置文件的方式进行设置。
logback支持XML和Groovy两种格式的配置方式,此外,对于log4j的用户,可以直接采用PropertiesTranslator来将log4j的配置文件转换为logback.xml。
logback的配置文件加载顺序如下:
1.首先尝试在类路径下查找logback.groovy文件,如找不到进入下一步。
2.尝试在类路径中招logback-test.xml文件,如找不到进入下一步。
3.在类路径中尝试找logback.xml文件,如找不到进入下一步。
4,上述文件都找不到的话,就采用默认的方式,将所有的日志直接在console上进行输出。
如果是在maven环境中,那么可以在src/test/resources中设置logback-test.xml文件,这样,就可以构造测试环境下的日志配置,而不影响实际的环境。当进入生产环境时,则
在src/main/resources中设置logback.xml来完成生产环境的配置。这一点是非常有用的。
logback默认配置
如果没有在应用中进行任何配置,将调用默认的日志输出方式,即 root logger中的所有debug级别的日志将会在console中输出,输出格式为:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n.
下面的例子说明了这一点:源码参见chapt3 下的DefaultConfig.java
logger.info("info msg");
logger.debug("debug msg");
logger.error("error msg");
logger.warn("warn msg");
logger.trace("trace msg");
使用配置文件
在真实的应用中,我们还是尽量使用配置文件的方式进行日志输出的控制。当logback在解析配置文件出现错误时,会将其内部的状态信息进行输出。解析正常的话,将不会输出。当然也可以通过如下代码,强制查看logback的内部加载状态.
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
//将内部状态进行输出
StatusPrinter.print(lc);
也可以通过配置文件实现上述功能,在配置的文件的configuration中添加debug状态为true即可,例子如下。
<configuration debug="true"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- encoders are by default assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder --> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="STDOUT" /> </root> </configuration>实例文件为logbackshowinterstatus.xml,运行时将文件名改为logback.xml,并运行DefaultConfig.java即可看到效果。
自动更新配置文件
logback的一个优势就是可以不重启应用,而对配置文件进行更改。通过将configuration的sacn属性设置为true,即可以实现这一目标。
<configuration scan="true"> ... </configuration>默认情况下,将会每分钟重新扫描一次配置文件,一般应用中应该将这一频率降低,可以通过设置scanPeriod属性来设置间隔时间。实例如下:
<configuration scan="true" scanPeriod="30 seconds" > ... </configuration>时间单位可以为 milliseconds, seconds, minutes or hours。(毫秒,秒,分钟,小时)。
需要说明的是,在这里讲scan属性配置为true后,logback将会在后台加载一个名为ReconfigureOnChangeFilter的TurboFilter,这个ReconfigureOnChangeFilter将会在每个日志的的print方法调用时被启动,并且无论这个print方式是否被执行,都会被启动。举例来说,如果有一个logger.debug("msg"),但是系统设置为info级别。虽然最终的日志将不会输出对应的日志记录,但是ReconfigureOnChangeFilter还是会被启动。而配置文件是否发生了修改的具体实现逻辑就由ReconfigureOnChangeFilter来实现,显然ReconfigureOnChangeFilter对于系统存在很大的资源消耗,因此logback设定了一个N值,以设定当发生了N次的日志事件后再调用ReconfigureOnChangeFilter。默认值为16,这个值得设置范围为( 2^16 (= 65536))。
由此可见,当一个配置文件被改变后。只有当系统运行了N个日志事件,并且同时满足scanPeriod的时间条件后才能得到更新。
配置文件语法
logback的配置文件的语法非常的灵活,一般的采用xml的配置方式,其中将包含一个<configuration>元素,零或者多个<appender>元素,零或者多个<logger>元素,最多一个<root>元素。官方文档上用一张图做了说明,如下:
标签的名称大小写不敏感,所以<logger>
,<Logger>
和<LOGGER>都是有效并相同的标签。
logger元素:
logger 元素使用<logger>标签来进行定义,一个logger必须包含一个name属性,一个可选的level属性,一个可选的additivity属性,这个属性的值可以为true或者false。level属性的值可以为TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF。注意这些属性大小写敏感。还有两个值INHERITED或者NULL,这两个值将会强制当前的logger继承其父级的lever值。logger元素还可以包含零或者多个appender-ref元素,一个logger可以与多个appender进行关联,与log4j不同的是,logback不会将前面加载的appender进行关闭或者丢弃,而会向所有的appender中输出日志。
root元素:
root元素通过root标签进行定义。它只能包含两种属性一个是level,这个属性的值只能是TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF。而不能是INHERITED 或者NULL。另外的一个属性是appender-ref,这个属性可以应用零或者多个appender元素。在实际的应用中,一般会设置root元素为debug级别。
需要主要到,logback的最终一条日志是否能够得到输出,并不是取决于该logger所绑定的appender,而是取决于logger本身。logback会首先判断某条日志是否需要执行,如果需要执行,则再调用所绑定的appender进行输出,否则将根本不进行执行。下面的例子说明了这一点:
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender> <logger name="chapters.configuration" level="INFO" /> <!-- turn OFF all logging (children can override) --> <root level="OFF"> <appender-ref ref="STDOUT" /> </root> </configuration>
上面的配置最终的作用级别如下:
Logger name | Assigned Level | Effective Level |
---|---|---|
root | OFF | OFF |
chapters.configuration | INFO | INFO |
chapters.configuration.MyApp3 | null | INFO |
chapters.configuration.Foo | null | INFO |
由此可见,虽然root级别设置为了关闭,但是configuration级别中,设置为了info,那么configuration包下面的info级别的日志都将得到运行,然后他将调用root中配置的STDOUT进行输出。
配置appender
appender元素通过appender标签进行配置。appender元素有两个强制属性:name和class。name属性用以表示这个appender元素,而class属性用全路径的方式确定这个appender的具体实现类。此外,appender元素还可以包含零或者多个layout,encoder,filter元素。此外,根据appender元素的具体实现类的不同,appender元素中还可能包含一些实现类中需要的属性。官方文档对其构成提供了一张图,如下:
layout元素有一个必须的属性class,这个属性中配置了layout的具体实现类。如果这个实现类包含了一些属性,则在元素中进行设置,需要指出的是,如果实现类是PatternLayout,则可以不配置class属性,logback将会自动采用patternLayout作为默认的layout实现类。
encoder元素有一个必须的属性class,这个属性中配置了encoder的具体实现类。如果这个实现类包含了一些属性,则在元素中进行设置。需要指出的是,如果实现类是PatternLayoutEncoder,则可以不配置class属性,logback将会自动采用patternlayoutencoder作为默认的encoder实现类。
由于layout和encoder一般都是不同的,不存在公用性。因此logback将这两个元素定义在appender的内部。
appender的叠加:
appender存在叠加性;由于logger不但将向其绑定的appender输出日志,并且还要向其父级的logger的appender输出日志,这样如果子类和父类logger绑定了相同的appender,就会造成日志内容的重复。下面的配置文件说明了这个问题:
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <logger name="chapters.configuration"> <appender-ref ref="STDOUT" /> </logger> <root level="debug"> <appender-ref ref="STDOUT" /> </root> </configuration>
通过上面的配置文件运行应用结果会如下所示:
14:25:36.343 [main] INFO chapters.configuration.MyApp3 - Entering application.
14:25:36.343 [main] INFO chapters.configuration.MyApp3 - Entering application.
14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again!
14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again!
14:25:36.359 [main] INFO chapters.configuration.MyApp3 - Exiting application.
14:25:36.359 [main] INFO chapters.configuration.MyApp3 - Exiting application.
可以看到所有的日志都存在了重复输出现象。
在正常的应用中,由于一般不会将子类和父类的appender设置为相同的值,因此一般不会存在上述问题。如果确是需要对该问题进行解决,则可以通过将logger的additivity属性设置为false来进行解决,在上面的例子中,如果将configuration的additivity设置为false,最终的日志将不会出现重复现象。
需要注意的是,在如下的配置文件中:
<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>foo.log</file> <encoder> <pattern>%date %level [%thread] %logger{10} [%file : %line] %msg%n</pattern> </encoder> </appender> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> <logger name="chapters.configuration.Foo" additivity="false"> <appender-ref ref="FILE" /> </logger> <root level="debug"> <appender-ref ref="STDOUT" /> </root> </configuration>
foo的logger日志将只会发送到名为file的appender中,而在STDOUT的console中,将不会输出。
在configuration还可以通过添加contextName元素来设置日志的名称。在有系统有多套日志系统或者多个系统向同一个数据库中存储日志时,这一点很重要。contextName的使用如下例所示:
<configuration> <contextName>myAppName</contextName> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d %contextName [%t] %level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="STDOUT" /> </root> </configuration>通过这个配置文件,日志输出中将会把context中设置的名字进行输出。输出如下所示:
2013-10-09 16:19:37,492 myAppName [main] INFO c.w.s.S.chapt3.ContextNameShow - show the context name
参数的使用
logback提供了在日志中使用参数的方法。参数可以在配置文件或者外部文件等中进行定义。变量的使用采用${aname}的方式引用。需要注意的是HOSTNAME和CONTEXT_NAME作为logback默认包含的变量,可以直接赋值使用。
参数的定义
在xml文档中,参数是通过property元素进行定义的。而logback还提供了可以通过variable的方式进行定义。两种方式在logback中是等同的,但是考虑的兼容性,个人建议采用property的方式进行定义。今后的例子也将采用这种方式。下面展示一个使用参数的例子:
<configuration> <property name="USER_HOME" value="/home/sebastien" /> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>${USER_HOME}/myApp.log</file> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="FILE" /> </root> </configuration>
当文件中的变量参数较多时,全部在logback.xml中进行配置显然比较麻烦,并且考虑到系统的部署等原因,将参数放在单独的文件中配置显然也更加合理:下面的例子说明了配置文件的使用方法
logback.xml 如下所示:
<configuration> <property file="src/main/java/chapters/configuration/variables1.properties" /> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>${USER_HOME}/myApp.log</file> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="FILE" /> </root> </configuration>在上述的对应目录下建立variables1.properties文件。该文件的内容如下所示:
USER_HOME=/home/log
除了采用上述的文件地址的方式,还是采用类路径的方式定位配置文件。使用方法如下所示:
<configuration> <property resource="resource1.properties" /> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>${USER_HOME}/myApp.log</file> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="FILE" /> </root> </configuration>
参数范围
logback的参数范围有三种 local scope, context scope, system scope,默认的范围是local scope。个人感觉这个没有什么用,等到用到时候再仔细研究吧。将范围使用的例子在下面展示一下:
<configuration>
<property scope="context" name="nodeId" value="firstNode" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/opt/${nodeId}/myApp.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
变量的默认值
可以采用":-"来进行默认值的设置,例如aName没有定义,"${aName:-golden}"将会被赋值 "golden"。
变量的嵌套设置
logback支持变量的嵌套设置,具体参见如下例子:
USER_HOME=/home/sebastien fileName=myApp.log destination=${USER_HOME}/${fileName}此处destination就是由user_home和filename嵌套组合构成。
在运行中创建属性(参数)
在实际中很少会用到,原理就是代码实现一个接口PropertyDefiner,接口中有一个方法会返回所需要的值,相关参数可以在xml中进行配置,个人觉得既然还是需要进行配置,那么就直接配置成参数就行了。因此不再进行介绍。
不过logback提供了两个默认的实现PropertyDefiner的类,比较有用,具体如下:
Implementation name | Description |
---|---|
FileExistsPropertyDefiner | Set the named variable to "true" if the file specified by path property exists, to "false" otherwise. |
ResourceExistsPropertyDefiner | Set the named variable to "true" if the resource specified by the user is available on the class path, to "false" otherwise. |
<configuration>
<define name="fileexsit" class="FileExistsPropertyDefiner
">
<path >src/wj/log.txt</path >
</define>
</configuration>
FileExistsPropertyDefiner中有一个path属性,可以在其中指定某个文件的路径,如果该文件存在,则这个变量将返回true,如果这个文件不存在,将返回false。ResourceExistsPropertyDefiner于此类似,只是属性的名称为resource。这两个实现类在实际中有很大的用处,结合后面即将介绍的语法,可以实现如果日志文件存在,就将日志输出到日志文件中。如果日志文件不存在,就将日志输出到控制台这样较为弹性的日志结构。
条件处理语法的使用:
在开发过程中,经常需要根据依据开发,测试,产品来分别构造日志环境。但是实际上,这些环境之间的差别很小。为了避免重复的文件,logback提供了<if>
, <then>
and <else>
元素来实现不同环境间的自动适应切换,需要注意的是,如果要实现这个功能,需要Janino的包的支持。使用语法如下所示:
<!-- if-then form --> <if condition="some conditional expression"> <then> ... </then> </if> <!-- if-then-else form --> <if condition="some conditional expression"> <then> ... </then> <else> ... </else> </if>
需要注意的是,condition中的只能接受范围为context或者system基本的参数。当调用参数时,需要采用property
() 或者它的缩写方式 p
()来获取指定参数的对应的值。如要获取”key“对应的值,就需要采用property("key")或者p("key")来获取其对应的值。如果该key没有定义,将会返回一个空的string而不是null。
条件处理语法可以在<configuration>元素内部的任何位置使用,但是在xml中过多的使用这种语法将使得整个xml文件非常凌乱,难以看懂。
最后提供一个根据日志文件是否存在来决定日志输出位置的logback.xml的配置方案:
<configuration >
<!-- 如果日志文件存在就将日志保存到日志文件中,如果日志文件不存在就在console中显示-->
<define name="logexsit" class="ch.qos.logback.core.property.ResourceExistsPropertyDefiner">
<resource >logger.txt</resource >
</define>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>src/main/java/logger.txt</file>
<encoder>
<pattern>%d %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<if condition='property("logexsit").contains("true")'>
<then>
<appender-ref ref="FILE" />
</then>
<else>
<appender-ref ref="STDOUT" />
</else>
</if>
</root>
</configuration>
如果类路径下存在logger.txt文件,那么就向该文件中存储日志,否则采用console进行日志输出。具体例子可以运行chapt3下的ConditionUse.java.需要将logbackconditionuse.xml改名为logback.xml.
此外,由于需要添加janino的支持,maven中添加如下配置:
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>2.6.1</version>
</dependency>
配置文件的嵌套包含
通过一个例子说明一下,感觉没什么实际用途,毕竟这个不会像spring的配置那么复杂。
<configuration> <include file="src/main/java/chapters/configuration/includedConfig.xml"/> <root level="DEBUG"> <appender-ref ref="includedConsole" /> </root> </configuration>
这个配置包含了另外的一个配置文件 includedConfig.xml,并且使用了该文件中的includedConsole,需要注意的是,在被包含的配置文件中的对应元素必须包含在<included>元素中。
<included> <appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>"%d - %m%n"</pattern> </encoder> </appender> </included>