一、Logback配置文件
1、Logback通过加载配置文件的方式来初始化配置,配置文件的加载顺序如下:
1)Logback首先会去类路径下找名为logback-test.xml的文件。
2)如果没有找到如上文件,会去类路径下找名为logback.groovy的文件。
3)如果没有找到如上文件,会去类路径下找名logback.xml的文件。
4)如果以上文件都没有找到,spi机制会去类路径下的META-INF\services\ch.qos.logback.classic.spi.Configurator文件解析com.qos.logback.classic.spi.Configurator的实现。
5)如果以上配置都没有,logback会通过BasicConfigurator自动配置,将日志信息直接输出到控制台。
2、Logback自动配置
package chapters.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyApp1 {
final static Logger logger = LoggerFactory.getLogger(MyApp1.class);
public static void main(String[] args) {
logger.info("Entering application.");
Foo foo = new Foo();
foo.doIt();
logger.info("Exiting application.");
}
}
package chapters.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Foo {
static final Logger logger = LoggerFactory.getLogger(Foo.class);
public void doIt() {
logger.debug("Did it again!");
}
}
运行上面例子需将logback-classic.jar包放置到类路径,如果类路径下没有logback-test.xml或者logback.xml,logback默认会调用BasicConfigurator最小化配置。最小化配置会为root日志记录器绑定ConsoleAppender,日志信息通过PatternLayoutEncoder格式化,格式为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n。此外。root日志记录器默认被指派DEBUG级别。
3、通过logback-test.xml或者logback.xml初始化配置
正如前面所提到的,如果在类路径下找到了logback-test.xml或者logback.xml文件,logback或通过配置文件初始化配置。下面的配置和logback默认自动配置BasicConfigurator等价。
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<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>
4、打印状态数据
如果在解析配置文件的过程中有警告或者错误信息,logback会在控制台中自动打印内部状态数据。如果没有警告或者错误信息,想要监控logback的内部状态,可以通过调用StatusPrinter类的print()方法,如下所示。
package chapters.configuration;
15
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
18
19 import ch.qos.logback.classic.LoggerContext;
20 import ch.qos.logback.core.util.StatusPrinter;
21
22 public class MyApp2 {
23 final static Logger logger = LoggerFactory.getLogger(MyApp2.class);
24
25 public static void main(String[] args) {
26 // assume SLF4J is bound to logback in the current environment
27 LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
28 // print logback's internal status
29 StatusPrinter.print(lc);
30
31 logger.info("Entering application.");
32 Foo foo = new Foo();
33 foo.doIt();
34 logger.info("Exiting application.");
35 }
36 }
运行程序会打印如下日志:
17:44:58,578 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml]
17:44:58,671 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
17:44:58,671 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
17:44:58,687 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
17:44:58,812 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Popping appender named [STDOUT] from the object stack
17:44:58,812 |-INFO in ch.qos.logback.classic.joran.action.LevelAction - root level set to DEBUG
17:44:58,812 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[root]
17:44:58.828 [main] INFO chapters.configuration.MyApp2 - Entering application.
17:44:58.828 [main] DEBUG chapters.configuration.Foo - Did it again!
17:44:58.828 [main] INFO chapters.configuration.MyApp2 - Exiting application.
如果不想编程式调用StatusPrinter来监控状态数据,可以通过修改logback配置文件实现,将configuration元素的debug属性设为false即可,如下:
<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>
5、配置文件修改后重新加载配置文件
将configuration的scan属性设为true即可,默认情况下,配置文件每分钟会被扫描一次,通过scanPeriod属性可以指定扫描周期,值可以是milliseconds, seconds, minutes or hours,如下:
<configuration scan="true" scanPeriod="30 seconds" >
...
</configuration>
如果没有指定时间单元,默认为毫秒。当设定scan属性为true时,ReconfigureOnChangeTask
会被注册,这个任务运行在一个单独的线程中用来监控配置文件是否变化。
二、配置文件语法
配置文件的基础结构由<configuration>元素组成,里面包含多个<appender>、<logger>元素和至多一个<root>元素,下面图表描述了logback配置文件的基础结构。
1、标签名的大小写类型敏感性
从logback 0.9.17版本开始,显性规则标签(如appender、logger等)名不再大小类型敏感。例如:<logger>、<Logger>、<LOGGER>都是合理的,隐性规则标签名遵从驼峰法命名。
2、配置日志记录器
配置日志记录器之前首先要了解日志打印级别继承和日志方法打印的基本选择规则,日志记录器通过<logger>元素配置,该元素必须带有name属性,可选的属性有level和additivity。level属性的值大小写类型不敏感,有效值有TRACE、DEBUG、INFO、WARN、ERROR、ALL或者OFF。特殊值INHERITED(同义词是NULL)意为日志记录器的有效级别继承自高级别的日志记录器。<logger>元素可以包含多个<appender-ref>元素,每个引用的appender会被绑定到日志记录器。<root>元素用来配置根日志记录器,该元素同样可以包含多个<appender-ref>元素。
示例1(设置日志记录器的有效级别):
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration" level="INFO"/>
<!-- Strictly speaking, the level attribute is not necessary since -->
<!-- the level of the root level is set to DEBUG by default. -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
日志输出如下:
17:34:07.578 [main] INFO chapters.configuration.MyApp3 - Entering application.
17:34:07.578 [main] INFO chapters.configuration.MyApp3 - Exiting application.
示例2(设置多个日志记录器的有效级别):
<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" />
<logger name="chapters.configuration.Foo" level="DEBUG" />
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
日志输出如下:
17:39:27.593 [main] INFO chapters.configuration.MyApp3 - Entering application.
17:39:27.593 [main] DEBUG chapters.configuration.Foo - Did it again!
17:39:27.593 [main] INFO chapters.configuration.MyApp3 - Exiting application.
下表列出了日志记录器以及有效级别:
Logger name | Assigned Level | Effective Level |
---|---|---|
root | DEBUG | DEBUG |
chapters.configuration | INFO | INFO |
chapters.configuration.MyApp3 | null | INFO |
chapters.configuration.Foo | DEBUG | DEBUG |
示例3:
<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 |
日志输出如下:
17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Entering application.
17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Exiting application.
三、配置输出源
输出源通过<appender>元素配置,该元素带有name和class两个必选属性,name属性指定输出源的名字,class属性指定要实例化的Appender实现类的全限定类名。<appender>元素可以包含多个<layout>、<encoder>、<filter>元素。<appender>元素还可以包含Appender类相关的JavaBean属性,下面图表阐述了<appender>元素的基本结构。
<layout>元素必选属性指定要实例化的Layout实现类的名字,如果Layout实现类是PatternLayout,那么根据默认映射规则class属性可以忽略。
<encoder>元素必须属性指定要实例化的Encoder实现类的名字,如果Encoder实现类的名字为PatterLayoutEncoder,那个根据默认映射规则,class属性可以忽略。
默认映射规则如下表:
父类 | 属性名 | 默认实现类 |
---|---|---|
ch.qos.logback.core.AppenderBase | encoder | ch.qos.logback.classic.encoder.PatternLayoutEncoder |
ch.qos.logback.core.UnsynchronizedAppenderBase | encoder | ch.qos.logback.classic.encoder.PatternLayoutEncoder |
ch.qos.logback.core.AppenderBase | layout | ch.qos.logback.classic.PatternLayout |
ch.qos.logback.core.UnsynchronizedAppenderBase | layout | ch.qos.logback.classic.PatternLayout |
ch.qos.logback.core.filter.EvaluatorFilter | evaluator | ch.qos.logback.classic.boolex.JaninoEventEvaluator |
1、多个日志记录器
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myApp.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>
<root level="debug">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" />
</root>
</configuration>
以上配置定义了两个输出源:FILE和STDOUT。FILE输出源会把日志信息输出到myApp.log,该输出源的编码器为PatternLayoutEncoder,该编码器会输出日期,日志级别,线程名,日志记录器名,文件名和行号,以及日志消息和与平台无关的换行符。
第二个叫做STDOUT的输出源会将日志信息直接输出到控制台,该输出源只会输出日志消息和与平台无关的换行符。
2、日志叠加
默认情况下,输出是叠加的,也就是说,日志记录器会把日志输出到它本身绑定的输出源以及所有祖先日志记录器绑定的输出源。因此,绑定相同输出到多个日志记录器会造成日志重复打印。
以下示例配置会重复打印日志:
<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.
输出源可叠加对于新手不再是陷阱,反而这是logback一个非常方便的特性。例如,只配置一个输出源,利用输出源叠加性,日志既能输出到文件,也能输出到控制台,如下:
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myApp.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">
<appender-ref ref="FILE" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
3、重写默认叠加行为
如果默认叠加性行为不符合需求,可以通过设置日志记录器的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>
四、其它配置
1、设置上下文名字
默认情况下,日志记录器上下文为"default",可以通过<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>
2、变量替换
Logback配置文件支持变量的定义和替换,变量有作用域。此外,变量既可以在当前配置文件中定义,也可以在外部文件、外部资源或者通过实现PropertyDefiner定义。字符串替换可以在配置文件中任何一个位置中出现,通过${variable}即可引用。
HOSTNAME和CONTEXT_NAME变量被自动定义,作用域为context。
2.1 定义变量
因为历史原因,即使在1.0.7版本logback配置文件中定义变量的元素为<property>,后面版本可以使用<variable>元素。
示例1:简单变量替换
<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会在系统属性中查找值。
示例2:系统变量替换
<configuration>
<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>
运行时指定命令参数即可:
java -DUSER_HOME="/home/sebastien" MyApp2
示例3:文件变量替换(logback-examples/src/main/resources/chapters/configuration/variableSubstitution3.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>
属性文件(logback-examples/src/main/resources/chapters/configuration/variables1.properties):
USER_HOME=/home/sebastien
示例4:类路径下资源引用
<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>
2.2 变量默认值
在某些特定情况下,如果变量没声明或者值为空希望有默认值,logback和Bash shell一样,默认值可以通过":-"指定。例如,假设名为aName的变量没有被定义,那么"${aName:-golden}"将会被翻译为"golden"。
2.3 嵌套变量
变量嵌套在logback中是完全支持的,名字、默认值以及变量定义的值都能引用其它变量,示例如下:
示例1:变量值引用
属性文件(logback-examples/src/main/resources/chapters/configuration/variables2.properties):
USER_HOME=/home/sebastien
fileName=myApp.log
destination=${USER_HOME}/${fileName}
配置文件(logback-examples/src/main/resources/chapters/configuration/variableSubstitution4.xml):
<configuration>
<property file="variables2.properties" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${destination}</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
2.3 变量名嵌套
例如,如果变量名为"userId"的值为"alice",那么"${${userId}.password}"被翻译为"${alice.password}"。
2.4 默认值嵌套
如果变量"id"没有设定值且变量"userId"的值为"alice",那么表达式"${id:-${userId}}"的值为"alice"。
3、作用域
属性作用域可以为:local、context或者system。默认为local作用域。
local作用域:只在当前配置文件有效。
context作用域:在上下文范围内有效。
system作用域:在JVM系统属性中有效。
3.1 变量作用域定义为context
<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>