日志系统
Java中常用的日志系统有很多,比如logging、log4j、logback等等。广泛使用的是logback+slf4j,同时代码里加入Lombok的插件,这样快速方便的使用日志。
一、包引入
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
这个包已经包括了logback-core、slf4j-api,所以只要包含一个包就可以了。
二、配置文件
Logback默认配置的步骤:
- 检查是否配置了系统变量logback.configurationFile,有则使用此配置的路径中的logback配置文件
- 在classpath下查找 logback-test.xml文件
- 第二步没找到,则在classpath下查找logback.groovy文件
- 第三步没找到,则在classpath下查找logback.xml文件,推荐。
- 第四步没找到,通过spi机制,查找META-INF\services\ch.qos.logback.classic.spi.Configurator 配置的 com.qos.logback.classic.spi.Configurator(此类在logback-classic jar中)的实现类,调用他的configure方法进行配置。
- 第五步没找到,使用默认的BasicConfigurator(实现了Configurator接口)进行配置。以上的寻找配置的代码在ch.qos.logback.classic.util.ContextInitializer.autoConfig中可以找到。
三、组件
- logger:作为日志的记录器,拥有自己的日志级别。
- appender:主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQL、PostreSQL、 Oracle和其他数据库、 JMS和远程UNIX Syslog守护进程等。
- layout:格式化日志的样式,可以搭载在不同的appender中。
四、配置文件例子
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="60 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder>
<pattern><![CDATA[
%-20(%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] ) %-5level %logger{80}[%L] - %msg%n
]]></pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="AppenderError" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${catalina.base}/logs/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${catalina.base}/logs/error.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<append>true</append>
<encoder>
<pattern><![CDATA[
%-20(%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] ) %-5level %logger{80} - %msg%n
]]></pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="AppenderInfo" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${catalina.base}/logs/info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${catalina.base}/logs/info.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<append>true</append>
<encoder>
<pattern><![CDATA[
%-20(%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] ) %-5level %logger{80} - %msg%n
]]></pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="AppenderDebug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${catalina.base}/logs/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${catalina.base}/logs/debug.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<append>true</append>
<encoder>
<pattern><![CDATA[
%-20(%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] ) %-5level %logger{80} - %msg%n
]]></pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender>
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
<logger name="com.demo.service" level="error"/>
<root value="${log.level}">
<appender-ref ref="AppenderError"/>
<appender-ref ref="AppenderInfo"/>
<appender-ref ref="AppenderDebug"/>
</root>
</configuration>
1、configuration
配置的结构,以configuration包含块为文件,带有3个参数。
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
2、appender
日志输出的目的地,常用的有ConsoleAppender-输出到控制台、FileAppender-输出到文件、RollingFileAppender-滚动记录文件。
2.1 ConsoleAppender-输出到控制台
- <encoder>:对日志进行格式化。作用等同于layout
- <target>:输出控制台日志,字符串 System.out 或者 System.err ,默认 System.out
2.2 FileAppender-输出到文件
- <file>:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。
- <append>:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
- <encoder>:对日志进行格式化。作用等同于layout
- <prudent>:如果是 true,日志会被安全的写入文件,即使其他的FileAppender也在向此文件做写入操作,效率低,默认是 false。
2.3 RollingFileAppender-滚动记录文件
最为常见的日志方式。滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。这里的条件可以是时间、文件大小等等。
- <file>、<append>、<encoder>和之前都一样
- <rollingPolicy>指定滚动策略:TimeBasedRollingPolicy-基于时间的回滚、FixedWindowRollingPolicy-基于窗口滚动、SizeAndTimeBasedRollingPolicy-按照时间和大小滚动
- <filter> 过滤策略,根据条件输出:LevelFilter-根据日志级别进行过滤、ThresholdFilter-过滤掉低于指定临界值的日志、 EvaluatorFilter-评估、鉴别日志是否符合指定条件
例子:
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/logback/log/test-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
时间回滚策略。
- <FileNamePattern>设置滚动生成文件的格式,这里设置的精确到天,也就是按照天滚动,如果时间设置精确到秒,就按秒来滚动。
- <maxHistory>设定最大的文件数,比如按天滚动,这里设置了30天,在第31天日志生成的时候,第一天的日志就会被删掉
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>/logback/log/test-%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>1MB</maxFileSize>
</triggeringPolicy>
</appender>
固定文件大小回滚策略。
- <fileNamePattern>定义文件名称的时候使用%i作为占位符,滚动后会数值替换
- <minIndex> 开始的索引
- <maxIndex> 最大索引,听说有最大值12,还没验证过
- <triggeringPolicy> 触发器:SizeBasedTriggeringPolicy文件大小触发器,maxFileSize:最大文件大小
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
</appender>
固定文件大小和时间回滚策略。很多人认为TimeBasedRollingPolicy搭配SizeBasedTriggeringPolicy可以使用固定大小和时间的回滚,但是官网说了,这两个有冲突,会导致并不能安装想想去做,必须使用SizeAndTimeBasedRollingPolicy的回滚策略。
- <maxFileSize> 文件最大大小,每次当前日志文件在当前时间段结束之前达到maxFileSize时,将使用从0开始的递增索引进行归档。
- <maxHistory> 最大周期数,您需要使用maxHistory属性指定要保留的周期数。
- <totalSizeCap> 可选,控制所有存档文件的总大小。当超过总大小上限时,最早的档案将被异步删除。 totalSizeCap属性还需要设置maxHistory属性。此外,始终首先应用“最大历史记录”限制,然后应用“总大小上限”限制。
2.4 Filter
filter按照上诉的介绍,是appender的记入日志的过滤器。
filter过滤器,执行一个过滤器会有返回个枚举值,即DENY,NEUTRAL,ACCEPT其中之一。返回DENY,日志将立即被抛弃不再经过其他过滤器;返回NEUTRAL,有序列表里的下个过滤器过接着处理日志;返回ACCEPT,日志会被立即处理,不再经过剩余过滤器。
过滤器被添加到appender中,为appender添加一个或多个过滤器后,可以用任意条件对日志进行过滤。 有多个过滤器时,按照配置顺序执行。
2.4.1 LevelFilter
级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。有以下子节点:
- <level>:设置过滤级别
- <onMatch>:用于配置符合过滤条件的操作
- <onMismatch>:用于配置不符合过滤条件的操作
<appender name="AppenderDebug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
将过滤器的日志级别配置为INFO,所有INFO级别的日志交给appender处理,非INFO级别的日志,被过滤掉。
2.4.2 ThresholdFilter
临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。
<appender name="AppenderDebug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
过滤掉所有低于INFO级别的日志。
2.4.3 EvaluatorFilter
求值过滤器,评估、鉴别日志是否符合指定条件。
- <evaluator>:鉴别器,常用的鉴别器是JaninoEventEvaluator,也是默认的鉴别器,它以任意的java布尔值表达式作为求值条件,求值条件在配置文件解释过成功被动态编译,布尔值表达式返回true就表示符合过滤条件。evaluator有个子标签<expression>,用于配置求值条件。
- <onMatch>:用于配置符合过滤条件的操作
- <onMismatch>:用于配置不符合过滤条件的操作
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator> <!-- 默认为 ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
<expression>return message.contains("billing");</expression>
</evaluator>
<OnMatch>ACCEPT </OnMatch>
<OnMismatch>DENY</OnMismatch>
</filter>
</appender>
过滤掉所有日志消息中不包含“billing”字符串的日志。
3、logger
<configuration debug="true" scan="true" scanPeriod="60 seconds">
<logger name="com.example.logback.logger" level="info">
<!-- 指定输出的appender -->
<appender-ref ref="logger_stdout"/>
</logger>
</configuration>
可以根据logger中的name属性指定某个文件或者文件夹输出的日志级别,并通过appender-ref指定日志的输出appender。还有一个additivity属性,如果设置为false的话就不会向上传递。什么意思呢?就是你在name中指定地方产生日志,这个日志会根据当前的logger配置输出,如果additivity=true,并且有上一级(包括root级别),则向上传递,可能还会再打印一遍。
4、root
整个项目的根级别日志设置
<root value="${log.level}">
<appender-ref ref="AppenderError"/>
<appender-ref ref="AppenderInfo"/>
<appender-ref ref="AppenderDebug"/>
</root>
可以使用pom中的properties定义。
五、案例
1、巧妙运用logger和root
<configuration debug="false" scan="true" scanPeriod="60 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder>
<pattern><![CDATA[
%-20(%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] ) %-5level %logger{80}[%L] - %msg%n
]]></pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<logger name="org.example.logback.logger" level="ERROR"/>
<root value="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
在上面这个配置中,在org.example.logback.logger包下的日志,日志级别是ERROR,但是没有appender,说明ERROR日志会被传到上级被记录,本logger不起任何作用。如果是INFO日志,因为和本级日志级别优先级低,所以被抛弃也不传送到上层。所以这种写法很好的限制某个包下日志级别,但由不阻碍其他包的日志传递。