为自己的程序记录日志,是好的程序员需要用多年时间培养的好习惯。慢慢地要用日志代替sout。日志好处多多,可以持久化,便于分析程序运行状态,分析用户行为等。
之前的Commons Logging+Log4J组合已经略显老旧,目前主流的日志框架是SLF4J+Logback。SLF4J可以看成是一个接口,Logback可以看成是该接口的具体实现。编写Java代码时,只需要调用接口。Logback不在Java代码中出现,只需要配置好XML文件即可。
Logback会根据不同的优先级,在classpath中读取XML文件。优先级顺序在网上查即可,平时应用中主要用到logback.xml这个命名,SpringBoot中用到logback-spring.xml这个命名。
针对哪个包,采用什么格式,输出到什么地方,文件命名规则,日志级别,时间周期和大小周期等,都是在XML文件中配置,在代码中只需要实例化logger,然后调用logger的方法即可。
所以关键是配置文件,这有一个样例,可以解释大部分配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 两个必填属性name和class,name是自定义名字,class是类的全限定名-->
<!-- 有0到1个layout标签(因为默认是PatternLayout,通常省略),0到多个encoder标签,0到多个filter标签 -->
<!-- 每个appender都用专用的encoder,logback不提倡也不支持共享encoder和layout-->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 强制class属性,因为默认是PatternLayoutEncoder,通常省略-->
<encoder>
<pattern>%d %-4relative [%thread] %-5level %logger{35} [%X{traceId}] - %msg %n
</pattern>
</encoder>
</appender>
<appender name="APPENDER"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- RollingPolicy is responsible for the what and TriggeringPolicy is responsible for the when.-->
<!-- a RollingFileAppender must have both a RollingPolicy and a TriggeringPolicy set up. However,
if its RollingPolicy also implements the TriggeringPolicy interface,
then only the former needs to be specified explicitly-->
<!-- one mandatory <fileNamePattern> property and several optional properties-->
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名,有了它可以省略<file>标签,有<file>标签的话就输出到<file>标签。存档日志的位置不变。 -->
<!-- 根据%d为周期滚动,多个%d只有1个作为滚动周期,其他设为辅助-->
<!-- 只写d%,省略后面内容,则默认是yyyy-MM-dd;yyyy-ww是每星期;yyyy-MM-dd_HH是每小时;yyyy-MM-dd_HH-mm每分钟,
连接符可自定义-->
<!-- 滚动行为不是时钟驱动,是事件驱动-->
<!-- 如果文件扩展名用.gz,则存档日志会被自动压缩-->
<!-- 在SizeAndTimeBasedRollingPolicy中,%i也是必填,日志达到maxFileSize但还没有到maxHistory时,i从0自增-->
<fileNamePattern>/data/logs/cec-evcs-charge-service-${server.port}-%d{yyyy-MM-dd}-%i.log
</fileNamePattern>
<!--文件最多保留的天数。数值单位对应上面的滚动周期 -->
<maxHistory>7</maxHistory>
<!--单个文件最大为多少,可以配合totalSizeCap(总文件最大为多少)一起使用 -->
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<encoder>
<!--格式化输出:%d表示日期,%relative毫秒数,%thread表示线程名,%-5level:级别从左显示5个字符宽度,
%msg:日志消息,%n换行符 -->
<!-- 格式定义在Layout接口中的doLayout()方法中-->
<!-- 借鉴C语言的printf()函数-->
<!-- %-数字,如果输出结果长于数字,全部输出不裁剪-->
<!-- 小括号是分组,例如可以分组指定左padding或右padding-->
<!-- 可以指定颜色-->
<!-- 输出结果示例:2021-05-20 12:56:12,066 184127996 [http-nio-9266-exec-97] INFO c.e.egsc.cec.base.tool.EvcsDataUtil[1621486563632020074448578F1E3C85D440B9D76D93F7DC5AF33DE1] - postRequest-->
<!-- %X用于输出MDC键对应的值,MDC是跟着线程走的-->
<pattern>%d %-4relative [%thread] %-5level %logger{35} [%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>
<appender name="ERRORAPPENDER"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<fileNamePattern>/data/logs/error/cec-evcs-charge-service-${server.port}-%d{yyyy-MM-dd}-%i.log
</fileNamePattern>
<!--文件最多保留的天数 -->
<maxHistory>7</maxHistory>
<!--文件最大为多少 -->
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<encoder>
<pattern>%d %-4relative [%thread] %-5level %logger{35} [%X{traceId}] - %msg%n
</pattern>
</encoder>
<!-- 可以点LevelFilter查看源码,属于Regular Filters,还包括ThresholdFilter,EvaluatorFilter-->
<!-- 另有TurboFilters-->
<!-- 只要是ERROR,就通过,不是,就拒绝。-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--设置日志打印级别 -->
<!-- 有强制的name属性,可选的level属性和additivity属性-->
<!-- name=包名,additivity表示是否继承祖先的appender-->
<logger name="com.eg.egsc" additivity="false">
<level value="INFO"/>
<!-- Each appender thus referenced is added to the named logger-->
<appender-ref ref="APPENDER"/>
<appender-ref ref="ERRORAPPENDER"/>
<appender-ref ref="Console"/>
</logger>
<logger name="com.eg.egsc.framework.dao.intercepter">
<level value="WARN"/>
</logger>
<logger name="com.eg.egsc.common.component.rabbitmq">
<level value="WARN"/>
</logger>
<!--设置框架日志打印级别 -->
<!-- 只有一个level属性-->
<root>
<level value="WARN"/>
<!-- 用appender的name作为ref属性值-->
<appender-ref ref="APPENDER"/>
<appender-ref ref="ERRORAPPENDER"/>
<appender-ref ref="Console"/>
</root>
<!--liph 验证滚动更新部署 增加标记注释2-->
</configuration>