1. Java 日志体系
1.1 市面上常用的日志框架
-
日志门面技术有 JCL(Jakarta Commons Logging)、SLF4j(Simple Logging Facade for Java)、jboss-logging,它们都不提供具体的日志实现
-
日志实现技术有 Log4j、JUL(java.util.logging)、Log4j2、Logback ,它们都提供了不同的 API 使用
1.2 SLF4J 技术
1.2.1 SLF4J 绑定器
-
每一个日志的实现框架都有自己的配置文件。使用 SLF4J 以后,配置文件还是做成日志实现框架自己本身的配置文件
-
用户除了需要引入 SLF4J API 的 jar 包外,还需要引入 SLF4J 对日志实现的绑定包
1.2.2 SLF4J 桥接器
-
SLF4J 桥接器是为了解决 Jar 包冲突的问题,即有些第三方 Jar 包可能直接使用 Log4j 打印,然后系统中使用的是 SLF4J + Logback 打印,那么就会出现两种日志
-
使用 SLF4J 桥接器的步骤是:先把其他日志实现包排除掉,然后使用桥接包来替换,最后导入 SLF4J 绑定器的实现包
2. SpringBoot 日志
2.1 SpringBoot 日志关系
- SpringBoot 底层是使用 SLF4J + Logback 的方式进行日志记录,同时引入了
jul-to-slf4j
和log4j-to-slf4j
两个桥接包,从而将 JUL 和 Log4j 的日志转到 SLF4J 上,然后再统一使用 Logback 进行日志输出
2.2 SpringBoot 日志使用
代码已经上传至 https://github.com/masteryourself-tutorial/tutorial-spring ,详见
tutorial-spring-boot-core/tutorial-spring-boot-log
工程
2.2.1 配置文件内容
1. application.properties
# 全局日志配置
logging.level.root=info
# 精确到包名
logging.level.pers.masteryourself.tutorial.spring.boot.log=debug
# 可以指定完整的路径, 如 D:/springboot.log, 也可以使用 ./ 表示当前路径
logging.file=./spring.log
# 在当前磁盘的根路径下创建 spring 文件夹和里面的 log 文件夹, 使用 spring.log 作为默认文件, 这两个配置无法配合使用
logging.path=/spring/log
# 在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
2.2.2 配置文件位置
-
Logback:
logback-spring.xml
,logback-spring.groovy
,logback.xml
,logback.groovy
-
Log4j2:
log4j2-spring.xml
,log4j2.xml
-
JUL:
logging.properties
-
如果是原生的 log 配置文件,那么会直接被日志框架识别
-
如果加上了 spring, 例如
logback-spring.xml
或者log4j2-spring.xml
, 那么日志框架不会直接加载日志的配置项, 而是由 SpringBoot 解析,可以使用 SpringBoot 的高级 Profile 功能
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<!-- 在 dev 环境下生效 -->
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
</springProfile>
<!-- 在非 dev 环境下生效 -->
<springProfile name="!dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
</springProfile>
</layout>
</appender>
2.3 切换日志框架为 log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
2.4 日志模板
1. logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为 true 时,配置文件如果发生改变,将会被重新加载,默认值为 true
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当 scan 为 true 时, 此属性生效。默认的时间间隔为 1 分钟
debug:当此属性设置为 true 时,将打印出 logback 内部日志信息,实时查看 logback 运行状态。默认值为 false
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 定义日志的根目录 -->
<property name="LOG_HOME" value="/app/log"/>
<!-- 定义日志文件名称 -->
<property name="appName" value="tutorial-spring"/>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--
日志输出格式:
%d 表示日期时间
%thread 表示线程名
%-5level:级别从左显示 5 个字符宽度
%logger{50} 表示 logger 名字最长 50 个字符,否则按照句点分割
%msg:日志消息
%n 是换行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</layout>
</appender>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日志文件的名称 -->
<file>${LOG_HOME}/${appName}.log</file>
<!--
当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
%i:当文件大小超过 maxFileSize 时,按照 i 进行文件滚动
-->
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--
可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动
且 maxHistory 是 365,则只保存最近 365 天的文件,删除之前的旧文件
-->
<MaxHistory>365</MaxHistory>
<!--
当日志文件超过 maxFileSize 指定的大小是,根据上面提到的 %i 进行日志文件滚动
注意此处配置 SizeBasedTriggeringPolicy 是无法实现按文件大小进行滚动的,必须配置 timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
</layout>
</appender>
<!--
logger 主要用于存放日志对象,也可以定义日志类型、级别
name:表示匹配的 logger 类型前缀,也就是包的前半部分
level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity:作用在于 children-logger 是否使用 rootLogger 配置的 appender 进行输出
false:表示只用当前 logger 的 appender-ref
true:表示当前 logger 的 appender-ref 和 rootLogger 的 appender-ref 都有效
-->
<!-- tutorial-spring logger -->
<logger name="pers.masteryourself.tutorial.spring" level="info"/>
<!-- Spring framework logger -->
<logger name="org.springframework" level="info" ref="stdout" additivity="false">
<appender-ref ref="stdout"/>
</logger>
<!--
root 与 logger 是父子关系,没有特别定义则默认为 root,任何一个类只会和一个 logger 对应
要么是定义的 logger,要么是 root,判断的关键在于找到这个 logger,然后判断这个 logger 的 appender 和 level
-->
<root level="info">
<appender-ref ref="stdout"/>
<appender-ref ref="appLogAppender"/>
</root>
</configuration>
2. log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- log4j2 自身日志级别 -->
<Configuration status="WARN">
<Properties>
<!-- 定义日志文件名称 -->
<Property name="APP_ID" value="tutorial-spring"/>
<!-- 定义日志的根目录 -->
<Property name="LOG_PATH" value="/app/log/${APP_ID}"/>
</Properties>
<Appenders>
<RollingFile name="RollingFile" fileName="${LOG_PATH}/app.log"
filePattern="${LOG_PATH}/app-%d{yyyyMMdd}-%i.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<Policies>
<!--以天为单位触发滚动操作,modulate 设置为 true,会以 00:00 作为起始时间,每天 00:00 进行一次滚动-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="1GB"/>
</Policies>
<!--每天最多保存 5G 日志,最多保存 5 天-->
<DefaultRolloverStrategy max="5">
<Delete basePath="${LOG_PATH}" maxDepth="1">
<IfFileName glob="app-*.log"/>
<IfLastModified age="7d"/>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<logger name="pers.masteryourself.tutorial.spring" level="warn"/>
<root level="INFO">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="Console"/>
</root>
</Loggers>
</Configuration>
3. log4j.properties
### set log levels ###
log4j.rootLogger=info , stdout , D , E
### 输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n
#### 输出到日志文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/log.log
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = DEBUG ## 输出DEBUG级别以上的日志
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#### 保存异常信息到单独文件 ###
#log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.E.File = logs/error.log ## 异常日志文件名
#log4j.appender.E.Append = true
#log4j.appender.E.Threshold = ERROR ## 只输出ERROR级别以上的日志!!!
#log4j.appender.E.layout = org.apache.log4j.PatternLayout
#log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n