目录
一、logback介绍
1.logback定义
2.logback启动加载文件
3.pom依赖
3.日志级别
二、配置文件详解
1.configuration
2.property
3.springProperty
4.logger
5.root
6.appender
7.AsyncAppender
三.Spring Boot配置
完整logback-spring.xml配置文件
一、logback介绍
1.logback定义
Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch。
logback的结构主要由三个模块组成:
- logback-core:其它两个模块的基础模块。
- logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging。
- logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能。
SLF4J 是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。大概意思是指你只需要按统一的方式写记录日志的代码,而无需关心日志是通过哪个日志系统,以什么风格输出的,因为它们取决于部署项目时绑定的日志系统。
SLF4J最常用的日志实现框架是:log4j、logback。一般有slf4j+log4j、slf4j+log4j2、slf4j+logback三种日志组合。
本文选取logback做具体介绍
2.logback启动加载文件
在工程resources目录下建立logback.xml
- logback首先会试着查找logback.groovy文件;
- 当没有找到时,继续试着查找logback-test.xml文件;
- 当没有找到时,继续试着查找logback.xml文件;
- 如果仍然没有找到,则使用默认配置(打印到控制台)。
注:Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml),命名为logback-spring.xml的日志配置文件
3.pom依赖
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--引入以上依赖,会自动引入以下jar
logback-classic.x.x.x.jar
logback-core.x.x.x.jar
slf4j-api-x.x.x.jar
-->
注:在实际开发中我们不需要直接添加该依赖,spring-boot-starter其中包含了spring-boot-starter-logging,该依赖内容就是 Spring Boot 默认的日志框架就是logback。而spring-boot-starter-web包含了spring-boot-starter,可通过引入spring-boot-starter-web后的项目JAR依赖关系很快得出这个结论。
3.日志级别
日志级别用来控制日志信息的输出,从高到低分为共分为七个等级:
- off 最高等级,用于关闭所有日志记录。
- fatal 指出每个严重的错误事件将会导致应用程序的退出。
- error 指出虽然发生错误事件,但仍然不影响系统的继续运行。
- warm 表明会出现潜在的错误情形。
- info 一般和在粗粒度级别上,强调应用程序的运行全程。
- debug 一般用于细粒度级别上,对调试应用程序非常有帮助。
- all 最低等级,用于打开所有日志记录。
Log4j建议只使用四个级别,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG,优先级高的将被打印出来。(logback通用),通过定义级别,可以作为应用程序中相应级别的日志信息的开关。
二、配置文件详解
1.configuration
- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 其他配置省略-->
</configuration>
2.property
property有两个属性name和value:name变量的名称,value变量的值。
<!-- logback项目名称 -->
<property name="appName" value="demoServer"></property>
<!-- 日志级别 DEBUGER INFO WARN ERROR -->
<property name="logLevel" value="INFO"></property>
<!-- 日志路径-->
<property name="logPath" value="/data/project/logs"></property>
<!-- 最大保存时间 60天-->
<property name="maxHistory" value="60"/>
<!-- 异步缓冲队列的深度,该值会影响性能.默认值为256 -->
<property name="queueSize" value="512"></property>
<!-- lOGGER PATTERN 根据个人喜好选择匹配 -->
<property name="logPattern" value="[ %-5level] [%date{yyyy-MM-dd HH:mm:ss.SSS}] %logger{36} [%line] [%thread]- %msg%n"></property>
3.springProperty
springProperty要配合配置文件来用。
<springProperty scope="context" name="spring.application.name" source="spring.application.name"/>
<springProperty scope="context" name="spring.profiles.active" source="spring.profiles.active"/>
也就是在配置文件中有对应配置。
spring:
application:
name: demoServer
profiles:
active: dev
4.logger
logger节点,可选节点,作用是指明具体的包或类的日志输出级别,以及要使用的<appender>(可以把<appender>理解为一个日志模板)。
- name:必写属性,指定具体包或类,被指定的包或类中的日志输出将遵从该logger规定配置。
- level:非必写属性,指定日志输出级别,该级别将覆盖root配置的输出级别。
- addtivity:非必写属性,是否向上级loger传递打印信息,默认是true。
- appender-ref:引用的appender,引用后将实现appender中定义的行为。一个logger可以有多个引用,互不影响。
<logger name="com.github.demo" level value="INFO" >
<!-- 引用的appender,类似于spring的ref -->
<appender-ref ref="ASYNC_LOG_INFO" />
<appender-ref ref="STDOUT" />
</logger>
不过一般需要特殊检测的基础服务实际使用中additivity常常设置为false。如下:
<logger name="com.alibaba.nacos.client" level="${log.level.nacos}" additivity="false">
<appender-ref ref="ASYNC_THIRD_PARTY_APPENDER"/>
</logger>
<logger name="com.alibaba.nacos.client.config" level="${log.level.nacos}" additivity="false">
<appender-ref ref="ASYNC_THIRD_PARTY_APPENDER"/>
</logger>
<logger name="com.alibaba.nacos.client.naming" level="${log.level.nacos}" additivity="false">
<appender-ref ref="ASYNC_THIRD_PARTY_APPENDER"/>
</logger>
5.root
root节点,必选节点,用来指定最基础的日志输出级别并指定<appender>,可以理解为根logger。
<!-- 在定义后引用APPENDER -->
<root level="${logLevel}">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ASYNC_LOG_DEBUG"/>
<appender-ref ref="ASYNC_LOG_INFO"/>
<appender-ref ref="ASYNC_LOG_WARN"/>
<appender-ref ref="ASYNC_LOG_ERROR"/>
</root>
6.appender
appender节点是非常关键的一个节点,它有两个必要属性name和class,name指定appender名称,class指定appender的全限定名。
另一个属性encoder:负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。
appender有三种类型:
- ConsoleAppender(控制台日志)。
- FileAppender(文件日志)。
- RollingFileAppender(滚动文件日志)。
最常用的滚动策略,根据时间再根据文件大小来滚动生成文件
<!-- INFO 级别的日志记录 -->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/info/${appName}_info.log</file>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 归档的日志文件的路径,例如今天是2022-11-06日志,当前写的日志文件路径为file节点指定,
可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
而2022-11-06的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<fileNamePattern>${logPath}/info/${appName}_info-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>128MB</maxFileSize>
<maxHistory>${maxHistory}</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${logPattern}</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>
把日志输出到控制台
<!-- lOGGER PATTERN 根据个人喜好选择匹配 -->
<property name="logPattern" value="[ %-5level] [%date{yyyy-MM-dd HH:mm:ss.SSS}] %logger{36} [%line] [%thread]- %msg%n"></property>
<!-- %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n -->
<!-- %d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg %n -->
<!-- [ %-5level] [%date{yyyy-MM-dd HH:mm:ss.SSS}] %logger{96} [%line] [%thread]- %msg%n -->
<!-- 动态日志级别 -->
<jmxConfigurator/>
<!-- 控制台的标准输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<pattern>${logPattern}</pattern>
</encoder>
</appender>
pattern定义了日志的输出格式解析:
- %date:表示日期
- %thread:表示线程名
- %-5level:表示级别从左显示 5 个字符宽度
- %logger{36}:表示 Logger 名字最长 36 个字符
- %msg:表示日志消息
- %n:换行符
7.AsyncAppender
<!-- ASYNC_LOG_INFO -->
<appender name="ASYNC_LOG_INFO" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>${queueSize}</queueSize>
<!-- 设置该属性 logback 会使用 ArrayBlockingQueue 的非阻塞方法 offer 代替 put, 防止在队列满时阻塞业务线程 -->
<neverBlock>true</neverBlock>
<appender-ref ref="FILE_INFO"/>
</appender>
- queueSize:控制阻塞队列大小,使用的ArrayBlockingQueue阻塞队列,默认容量256:内存中最多保存256条日志
- discardingThreshold:丢弃日志的阈值,为防止队列满后发生阻塞。默认队列剩余容量 < 队列长度的20%,就会丢弃TRACE、DEBUG和INFO级日志
- neverBlock:控制队列满时,加入的数据是否直接丢弃,不会阻塞等待,默认是false
- 队列满时:offer不阻塞,而put会阻塞
- neverBlock为true时,使用offer
注意:
queueSize 过大
可能导致OOM
queueSize 较小
默认值256就已经算很小了,且discardingThreshold设置为大于0(或为默认值),队列剩余容量少于discardingThreshold的配置就会丢弃<=INFO日志。这里的坑点有两个:
因为discardingThreshold,所以设置queueSize时容易踩坑。
neverBlock 默认false
意味总可能会出现阻塞。
- 若discardingThreshold = 0,那么队列满时再有日志写入就会阻塞。
- 若discardingThreshold != 0,也只丢弃≤INFO级日志,出现大量错误日志时,还是会阻塞。
queueSize、discardingThreshold和neverBlock三参密不可分,务必按业务需求设置:
- 若优先绝对性能,设置neverBlock = true,永不阻塞。
- 若优先绝不丢数据,设置discardingThreshold = 0,即使≤INFO级日志也不会丢。但最好把queueSize设置大一点,毕竟默认的queueSize显然太小,太容易阻塞。
- 若兼顾,可丢弃不重要日志,把queueSize设置大点,再设置合理的discardingThreshold
三.Spring Boot配置
Spring Boot项目的配置文件。
logging: file: path: '@logging.file.path@' name: ${logging.file.path}/${spring.application.name}/root.log config: classpath:logback-spring.xml
spring-boot-starter-web默认引入logback
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
完整logback-spring.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 分级别异步文件日志输出配置 -->
<!-- 级别从高到低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志 -->
<!-- scan 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 -->
<!-- scanPeriod 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- logback项目名称 -->
<property name="appName" value="demoServer"></property>
<!-- 日志级别 DEBUGER INFO WARN ERROR -->
<property name="logLevel" value="INFO"></property>
<!-- 日志路径-->
<property name="logPath" value="/data/project/logs"></property>
<!-- 最大保存时间 60天-->
<property name="maxHistory" value="60"/>
<!-- 异步缓冲队列的深度,该值会影响性能.默认值为256 -->
<property name="queueSize" value="512"></property>
<!-- lOGGER PATTERN 根据个人喜好选择匹配 -->
<property name="logPattern" value="[ %-5level] [%date{yyyy-MM-dd HH:mm:ss.SSS}] %logger{36} [%line] [%thread]- %msg%n"></property>
<!-- %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n -->
<!-- %d{yyyy-MM-dd HH:mm:ss} %-4relative [%thread] %-5level %logger{35} - %msg %n -->
<!-- [ %-5level] [%date{yyyy-MM-dd HH:mm:ss.SSS}] %logger{96} [%line] [%thread]- %msg%n -->
<!-- 动态日志级别 -->
<jmxConfigurator/>
<!-- 控制台的标准输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<pattern>${logPattern}</pattern>
</encoder>
</appender>
<!-- DUBUG 日志记录 -->
<appender name="FILE_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/debug/${appName}_debug.log</file>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 归档的日志文件的路径,例如今天是2022-11-06日志,当前写的日志文件路径为file节点指定,
可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
而2022-11-06的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<fileNamePattern>${logPath}/debug/${appName}_debug-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>128MB</maxFileSize>
<maxHistory>${maxHistory}</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${logPattern}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- INFO 级别的日志记录 -->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/info/${appName}_info.log</file>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 归档的日志文件的路径,例如今天是2022-11-06日志,当前写的日志文件路径为file节点指定,
可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
而2022-11-06的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<fileNamePattern>${logPath}/info/${appName}_info-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>128MB</maxFileSize>
<maxHistory>${maxHistory}</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${logPattern}</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>
<!-- WARN 级别的日志记录 -->
<appender name="FILE_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/warn/${appName}_warn.log</file>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 归档的日志文件的路径,例如今天是2022-11-06日志,当前写的日志文件路径为file节点指定,
可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
而2022-11-06的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<fileNamePattern>${logPath}/warn/${appName}_warn-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>128MB</maxFileSize>
<maxHistory>${maxHistory}</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${logPattern}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- Error 级别的日志记录 -->
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/error/${appName}_error.log</file>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 归档的日志文件的路径,例如今天是2022-11-06日志,当前写的日志文件路径为file节点指定,
可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
而2022-11-06的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
<fileNamePattern>${logPath}/error/${appName}_error-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>128MB</maxFileSize>
<maxHistory>${maxHistory}</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${logPattern}</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>
<!-- ASYNC_LOG_DEBUG -->
<appender name="ASYNC_LOG_DEBUG" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>${queueSize}</queueSize>
<!-- 设置该属性 logback 会使用 ArrayBlockingQueue 的非阻塞方法 offer 代替 put, 防止在队列满时阻塞业务线程 -->
<neverBlock>true</neverBlock>
<appender-ref ref="FILE_DEBUG"/>
</appender>
<!-- ASYNC_LOG_INFO -->
<appender name="ASYNC_LOG_INFO" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>${queueSize}</queueSize>
<!-- 设置该属性 logback 会使用 ArrayBlockingQueue 的非阻塞方法 offer 代替 put, 防止在队列满时阻塞业务线程 -->
<neverBlock>true</neverBlock>
<appender-ref ref="FILE_INFO"/>
</appender>
<!-- ASYNC_LOG_WARN -->
<appender name="ASYNC_LOG_WARN" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>${queueSize}</queueSize>
<!-- 设置该属性 logback 会使用 ArrayBlockingQueue 的非阻塞方法 offer 代替 put, 防止在队列满时阻塞业务线程 -->
<neverBlock>true</neverBlock>
<appender-ref ref="FILE_WARN"/>
</appender>
<!--ASYNC_LOG_ERROR -->
<appender name="ASYNC_LOG_ERROR" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>${queueSize}</queueSize>
<!-- 设置该属性 logback 会使用 ArrayBlockingQueue 的非阻塞方法 offer 代替 put, 防止在队列满时阻塞业务线程 -->
<neverBlock>true</neverBlock>
<appender-ref ref="FILE_ERROR"/>
</appender>
<logger name="com.github.demo" level value="DEBUG" additivity="false">
<!-- 引用的appender,类似于spring的ref -->
<appender-ref ref="STDOUT" />
</logger>
<!-- 在定义后引用APPENDER -->
<root level="${logLevel}">
<appender-ref ref="ASYNC_LOG_DEBUG"/>
<appender-ref ref="ASYNC_LOG_INFO"/>
<appender-ref ref="ASYNC_LOG_WARN"/>
<appender-ref ref="ASYNC_LOG_ERROR"/>
</root>
</configuration>