目录
本文概览:介绍logback.xml配置中的logger、appender和root三个节点信息。
日志框架选择
目前常遇见的日志框架有log4j、logback、log4j 2。logback是为了优化log4j而生的,log4j2又在logback基础上做了优化。所以日志框架选型顺序为:log4j2 > logback > log4j。
1 Logback知识点概览
1. 如何使用Logback
(1)第一部分:配置lackback.xml文件,在xml中配置中配置如上三个节点<logger>、<appender>和<root>三种节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<cofiguratin> <!-- 1. 第一种节点 定义logger对象,可以有多个--> <loger> <!-- 1.1 可以有多个appender-ref --> <appender-ref ref="" /> </loger> <!--2. 第二种节点 配置输出格式-,可以有多个,一个logger对象对应多个appender --> <appender> <!-- 2.1 格式化,确定输出的pattern--> <encoder> </encoder> <!-- 2.2 过滤器--> <filter> </filter> <!-- 2.3 用来表示文件path--> <file></file> <!-- 2.4 按照一定规则来保存日志 --> <rollingPolicy></rollingPolicy> </appender> <!--3 第三种节点 配置root属性--> <root></root> </root> <configuration> |
(2) 第二部分:在程序中使用
- 获取logger对象,定义一个static变量如下:
1private static finall Logger log= LoggerFactory.getLogger(HelloWord.class); -
使用定义的log对象的五个函数trace()、debug()、info()、warn()和errn()记录信息。
2. 级别理解
(1)日志记录器级别
- 如何确定一个记录器的级别的规则? 每一个日志记录器必须有一个级别,如果没有配置,就寻找其父亲节点的级别,向上直至根 logger
- 如何设置logger对象的级别?在logback.xml中对于<logger>的属性进行设置。
(2)信息级别获取
所有的日志信息在写入日志时,必须赋予一个级别。通过logger的trace()、debug()、info()、warn()和 error()五个方法。
(3)日志器记录日志的规则
只记录比logger对象级别高的日志
2 Logback.xml的节点说明
2.1 logger
1. 命名规则
如果 logger的名称带上一个点号后是另外一个 logger的名称的前缀,那么,前者就被称为后者的祖先。如果 logger与其后代 logger之间没有其他祖先,那么,前者就被称后者之父节点。 这个规则可以实现按模块保存日志。
2. 实践-按一个Class所属的package进行保存日志
(1)在logback.xml中,logger对象的名称,常常定义为一个包名,如下
1 2 3 |
<logger name="com.company.department.application.biz" level="${LEVEL}" additivity="false"> <appender-ref ref="..."/> </logger> |
(2)代码中
在包biz的类中或者子包的类中,进行如下定义一个logger对象,
1 |
private static finall Logger log= LoggerFactory.getLogger(HelloWord.class); |
此时这个log对象就是上面logback.xml中定义的logger对象的子节点,所以这个包下所有的类都可以共享logger对象中定义的appender。
2.2 appender
2.2.1 encoder格式配置
这里主要是<pattern>的模式,它的作用类似于C中printf()的作用,即:根据一定的格式输出一个字符串。
举例如下:
1 |
%d %-5level [%X{ctxLogId}][%thread] %logger{5} - %msg%n |
logger.info(“日志信息”)最终打印的日志信息如下
1 |
2016-09-26 13:37:32,114 INFO [2251882130][http-nio-8027-exec-123] c.b.l.f.s.i.c.i.FinanceCoreClientImpl - 日志信息 |
下面解析每一个参数
- %d 和%date一样,结果举例如下: 2006-10-20 14:06:49,812
- %level 日志级别
- %thread tomca的线程名字,如http-nio-8027-exec-119 ,其中8027是服务的端口号,119是线程标识线程池的线程index
- %logger{5} ,打印logger对象的名字
- %msg 输出logger对象记录的信息,如通过logger.info,logger.error等方法记录的信息。
- %X{key} MDC (mapped diagnostic context)中对应的key的值。
-
%n 输出一个回车符,进行分行。一般都是在一个日志末尾加上这个符号。
2.2.2 过滤器filter
1. 返回值
过滤器的返回值只能是ACCEPT、DENY和NEUTRAL的其中一个。
- 如果返回DENY,那么记录事件立即被抛弃,不再经过剩余过滤器;
- 如果返回NEUTRAL,那么有序列表里的下一个过滤器会接着处理记录事件;
- 如果返回ACCEPT,那么记录事件被立即处理,不再经过剩余过滤器。
2. 过滤日志级别-logback自带过滤器
1 2 3 |
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> |
3. 自定义过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class ErrorLogFilter extends Filter { @Override public FilterReply decide(Object event) { // 1.条件判断 if (!isStarted()) { return FilterReply.NEUTRAL; } // 2. 如果满足过滤规则 if (.....)) { return FilterReply.DENY; } // 3. return FilterReply.NEUTRAL; } |
2.2.3 rollingPollicy
参考:https://logback.qos.ch/manual/appenders.html
提供了两种滚动策略:基于大小和时间。分别对应如下两个策略类FixedWindowRollingPolicy和 TimeBasedRollingPolicy。
1、基于大小的策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/error.log</file> <!--基于大小回滚 beg--> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>${LOG_HOME}/error.log.%i</fileNamePattern> <minIndex>1</minIndex> <maxIndex>3</maxIndex> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>5MB</maxFileSize> </triggeringPolicy <!--基于大小回滚 end--> <encoder> <pattern>${DEFAULT_PATTERN}</pattern> <charset>${CHARSET}</charset> </encoder> </appender> |
参数解析:
(1)minIndex和maxIndex
如minIndex=1,maxIdex=3,表示值保留error.log.1~error.log.5五个文件,超过这个个数,就删除最老的数据
(2)maxFileSize
如果值是5Mb,表示日志文件超过5Mb就会进行滚动。
2、基于时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/error.log</file> <!--基于时间 beg--> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/error.log</fileNamePattern> <!--保留30天日志--> <maxHistory>30</maxHistory> </rollingPolicy> <!--基于时间 beg--> <encoder> <pattern>${DEFAULT_PATTERN}</pattern> <charset>${CHARSET}</charset> </encoder> </appender> |
解释参数:
(1) fileNamePattern
常用分类格式有按日和按月
- 按日分类,以天为单位进行滚动
1 |
<fileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/error.log</fileNamePattern> |
- 按月分类,以月为单位进行滚动
1 |
<fileNamePattern>${LOG_HOME}/%d{yyyyMM}/error.log</fileNamePattern> |
(2) maxHistory:假设此时值为30,如果上面的filleNamePattern设置是按日分类,则表示30天;如果按月分类的,则此时就表示30个月了。如下是按月分类。
<fileNamePattern>${LOG_HOME}/%d{yyyyMM}/error.log</fileNamePattern>
(3)cleanHistoryOnStart:true表示每次启动时,会删除日志。默认是false,所以一般都是使用默认值
3、两种策略比较
对于基于大小的策略,都是在磁盘空间有限的情况下一种选择,如部署到docker容器中,虚拟机磁盘有限,所以一般使用这种策略。
对于时间策略,只要磁盘空间不是特别紧张,都建议使用这种策略。(最最常用的策略)
2.2.4 Appender叠加性
叠加性,就是一个记录器logger记录记录了一个信息,那么这个信息就会发送给此记录器logger绑定的所有Appender和此logger父亲祖先的所有Appender。
如对于一个com.baidu.logger记录器记录信息是。则会com,com.baidu,com.baidu.logger三个记录器的所有appender都会接受到消息。这种行为成为累积行为。可以设置logger的addivitity为false就可以了。
在日志配置过程中,一般都把“additivity属性”设置为false。如下,否则所有日志都会重复让root中apenders重新输出一份。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<logger name="org.apache.ibatis" level="${LEVEL}" additivity="false"> <appender-ref ref="DAL"/> <appender-ref ref="ERROR"/> </logger> <logger name="org.springframework" level="${LEVEL}" additivity="false"> <appender-ref ref="MISC"/> <appender-ref ref="ERROR"/> </logger> <root level="${LEVEL}"> <appender-ref ref="ERROR"/> <appender-ref ref="DEFAULT"/> </root> |
2.3 root的配置
1. 配置root的作用
可以通过“继承”的作用来理解定义root作用:
- 如果在logback.xml中没有找到匹配的<logger>,那么此时就使用<root>的配置,如对于如下
1 2 3 4 |
class A{ Logger logger = LoggerFactory.getLogger(A.class); ... } |
- Appender的叠加性。自定义的Logger,会把当前日志信息传递给root。如果additivity属性设置为false,那么此时就不会传递了。
- 自定义的Logger可以共享root的日志级别
2. 举例
1 2 3 4 |
<root level="info"> <appender-ref ref="ERROR"/> <appender-ref ref="DEFAULT"/> </root> |
2.4 property
1. 作用
类似于pom.xml中<property>,在<property>中可以定义一些变量的值,如下:
1 |
<property name="CHARSET" value="GBK"/> |
那么在定义其他节点时,就可以使用如下
1 2 3 4 5 6 7 |
<appender name="MISC" class="ch.qos.logback.core.rolling.RollingFileAppender"> ...... <encoder> ............ <charset>${CHARSET}</charset> </encoder> </appender> |
2. 在<property>中使用properties文件中值,这是怎么实现的?通过读取系统的环境变量来实现。
1 |
<property name="LEVEL" value="${logs.level}"/> |
3 日志规范
3.1 原则
1、日志目的
日志作用,就是记录程序中快照信息,方便定位问题。所以原则是:正确使用日志级别;在方便定位问题的前提下,尽量少。
2、规范主要包括两个方面
(1) logback配置时的原则
如涉及到partern、滚动策略等。
(2)代码中打印日志时原则。
3.2 logback.xml配置的规范原则
1、 统一个parten,不要使用多种pattern
<property name=”REQUEST_PATTERN” value=”%d %-5level [%X{ctxLogId}][%thread] %logger{5}- %msg%n”/>
2、只使用一种回滚策略
如果磁盘空间不紧张,都建议使用基于时间回滚的策略。这样可以按照日期进行归类日志,方便查看问题。
3、appender迭代性
避免打印同一个日志到多个文件中,造成重复日志,需要设置additivity=false。
3.3 代码规范原则
1、在代码中打印日志
需要关注如下两点:正确的使用日志级别 ;打印日志信息时,需要包含重要的参数信息
(1)Error级别
谨慎使用Error。只有系统中发生了非常严重的问题,必须马上有人进行处理的场景才需要Error。如果打印大量的Error,可能这些问题级别并不高,这样就造成我们不方便添加监控,不知道那些error是需要马上处理的。
(2)warn
(3)info日志
打印info日志时,要从“方便定位问题”的角度出发,并且突出“业务流的流向”。但是Info不要随心所欲的打,不是日志越多越好。
(4) debug
开发人员,在使用开发环境测试时打印的日志,在线上的日志级别一定是INFO级别的。
2、避免重复日志
(1)logback配置
配置输出数据源时,避免同一个日志分别输出到多个文件保存。需要设置additivity=”false”。
(2) 代码
调用函数时打印了参数,在被调用函数开始地方又打印了日志,如下,f()和g()两个函数中都打印了调用传给g()的参数,所以是重复日志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 函数1 f(){ // 打印调用g()的参数 logger.info(xxxxx); // 2.调用g g(); } // 函数2 g(){ // 打印g()函数的参数 logger.info(xxx); } |
4 一个完整的实例
1、pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!-- 配置logback beg--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.7</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.1.7</version> </dependency> <!-- 配置logback end--> |
2、logback.xml配置
选择基于时间的回滚策略,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<configuration> <!-- 1.定义一些变量,作用类似于maven中property --> <property name="LOG_HOME" value="xxxx"/> <property name="DEFAULT_PATTERN" value="%d %-5level [%X{ctxLogId}][%thread] %logger{5} - %msg%n"/> <property name="CHARSET" value="GBK"/> <!-- 2.定义了两个appender--> <appender name="WEB-COMMON" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/web-common.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/web-common.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>${DEFAULT_PATTERN}</pattern> <charset>${CHARSET}</charset> </encoder> </appender> <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/error.log</file> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/error.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>${DEFAULT_PATTERN}</pattern> <charset>${CHARSET}</charset> </encoder> </appender> <!-- 3.定义了logger对象 --> <logger name="com.company.department.application.web" level="${LEVEL}" additivity="false"> <appender-ref ref="WEB-COMMON"/> <appender-ref ref="ERROR"/> </logger> <!-- 4.定义了root对象--> <root level="info"> <appender-ref ref="ERROR"/> <appender-ref ref="DEFAULT"/> </root> </configuration> |
3、 在代码中使用
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.company.application.web.son class Test { /** 这个logger对象的一些属性信息,会使用logback.xml中名字 为"com.company.department.application.web"的logger对象配置 **/ public static final Logger logger = LoggerFactory.getLogger(Test.class); public void method(){ String info; .... logger.info("this is info{}",info); ..... } } |
5 扩展
5.1 打印外部依赖的日志
1 2 3 4 |
<logger name="com.spring.xxx" level="${LEVEL}" additivity="false"> <appender-ref ref="COMMON" /> <appender-ref ref="ERROR" /> </logger> |
5.2 从log4j切换到logback
需要两步:
- 在pom.xml引入需要的logback包,替换log4j
- 在resource下面添加logback.xml就可以了
5.3 生成一个logID
定义一个filter,通过MDC.put(key,value)来为一个请求赋值一个唯一的logID。这个logId获取目前一般有两个方法:
- 在代码里面通过UUID来生成,并添加到MDC中。UUID的生成参考
- 在nginx中通过在header中增加一个logId属性。然后在代码里面通过request.getHeader(“logId”)来获取,添加到MDC中。
6 参考资料
【1】 logback用户手册中文版,下载地址 https://pan.baidu.com/s/1eRIshXK
【2】 layout的参数的解析 http://logback.qos.ch/manual/layouts.html。
(全文完)