SpringBoot项目logback同步配置和异步配置
logback简介
logback时一个开源的日志组件,springboot自带logback依赖。在项目中使用logback时只需关注配置即可。logback主要分为三个模块:
- logback-core,基础模块,其它两个模块的依赖
- logback-classic,slfj-api的实现
- logback-access,提供HTTP访问Servlet容器(如tomcat)日志的功能
同步日志配置
日志级别
ALL:最低等级的,用于打开所有日志记录
TRACE:很低的日志级别,一般不会使用
DEBUG:指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息
INFO:消息在粗粒度级别上突出强调应用程序的运行过程。打印一些感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志
WARN:表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给出的一些提示
ERROR:指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别
FATAL:指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别可以直接停止程序了
OFF:最高等级的,用于关闭所有日志记录。
配置文件命名
配置文件加载顺序
顺序 | 非SpringBoot项目 | SpringBoot项目 |
---|---|---|
1 | logback-test.xml | ogback-test.xml |
2 | logback.xml | logback.xml |
3 | application.yml | |
4 | logback-spring.xml |
如果配置文件命名为logback.xml,在配置文件中要使用SpringBoot环境中的变量时,就会获取不到。一般来说,建议使用logback-spring.xml。
配置文件结构
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan: 配置文件发生改变时是否重新载入。默认true
scanPeriod: 检测配置是否修改的时间间隔,scan为true时该配置才有效。默认60秒
debug: 是否打印logback内部日志信息。默认false
-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 自定义变量 使用变量方式:${AppName}-->
<property name="AppName" value="demo"/>
<property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %level [${APP_NAME:-},%X{traceId},%X{spanId},%X{parentId}] ${PID:- } --- [%thread] %-40.40class{39} %L: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx} \n"/>
<!-- 上下文名称 默认default -->
<contextName>default</contextName>
<!-- 定义日志追加器-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 过滤器 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<!-- 输出格式
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="CONSOLE"/>
</root>
<logger name="com.szkingdom" level="DEBUG">
<appender-ref ref="CONSOLE"/>
</logger>
</configuration>
appender
name:追加器名称
class:追加器所在路径。可以是logback提供的追加器,如ConsoleAppender、FileAppender
RollingFileAppender,也可以是自定义的追加器
-
ConsoleAppender
<property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %level [${APP_NAME:-},%X{traceId},%X{spanId},%X{parentId}] ${PID:- } --- [%thread] %-40.40class{39} %L: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx} \n"/> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>debug</level> </filter> <encoder> <Pattern>${LOG_PATTERN}</Pattern> <!-- 设置字符集 --> <charset>UTF-8</charset> </encoder> </appender>
-
RollingFileAppender
<property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %level [${APP_NAME:-},%X{traceId},%X{spanId},%X{parentId}] ${PID:- } --- [%thread] %-40.40class{39} %L: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx} \n"/> <property name="LOG_FILE" value="${BUILD_FOLDER:-logs}/${APP_NAME}"/> <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_FILE}/log_debug.log</file> <encoder> <pattern>${LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> <!-- 日志记录器的滚动策略--> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- rollover daily --> <fileNamePattern>${LOG_FILE}/debug/log-debug-%d{yyyy-MM-dd_HH-mm}.%i.log</fileNamePattern> <!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB --> <!--单个日志文件最大100M,到了这个值,就会再创建一个日志文件,日志文件的名字最后+1--> <maxFileSize>100MB</maxFileSize> <!--日志文件保留天数--> <maxHistory>30</maxHistory> <!--所有的日志文件最大20G,超过就会删除旧的日志--> <totalSizeCap>20GB</totalSizeCap> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>debug</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender>
filter
满足条件的日志才会被记录,logback默认已经提供多种过滤,也可以自己定义过滤器。详情见过滤器
encoder
通过pattern指定日志格式,charset指定字符集
%d表示时间
%thread表示线程名
%-5level 表示日志级别,允许以五个字符长度输出
%logger{50}表示具体的日志输出者,比如类名,括号内表示长度
%msg表示具体的日志消息,就是logger.info(“xxx”)中的xxx %n表示换行
root
通过appender-ref指定启用的appender。
root的属性level是对启动的所有appender的日志级别控制,实际输出的日志会取root和appender最大日志级别。
logger
包或类日志配置
name: 类路径或者包路径
level:日志级别
additivity:是否向上级传递日志,默认true
异步日志配置
logback使用AsyncAppender来异步记录日志,实际上只是将要打印、写入文件或写入数据库的日志保存到队列中,最终还是调用ConsoleAppender、RollingFileAppdender来处理日志。
为什么要异步输出日志
输出日志和业务逻辑在同一个线程,有日志输出时,必须等待日志出输出完成后才会执行业务逻辑的代码,在高并发的场景下,大量的日志输出操作会使线程卡顿在输出日志的过程中,降低程序性能。
配置
以异步输出滚动日志文件为例
<property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %level [${APP_NAME:-},%X{traceId},%X{spanId},%X{parentId}] ${PID:- } --- [%thread] %-40.40class{39} %L: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx} \n"/>
<property name="LOG_FILE" value="${BUILD_FOLDER:-logs}/${APP_NAME}"/>
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}/log_debug.log</file>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${LOG_FILE}/debug/log-debug-%d{yyyy-MM-dd_HH-mm}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<!--单个日志文件最大100M,到了这个值,就会再创建一个日志文件,日志文件的名字最后+1-->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>30</maxHistory>
<!--所有的日志文件最大20G,超过就会删除旧的日志-->
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name ="ASYNC_DEBUG_FILE" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>20</discardingThreshold>
<queueSize>256</queueSize>
<neverBlock>false</neverBlock>
<appender-ref ref ="DEBUG_FILE"/>
</appender>
- discardingThreshold,丢弃策略执行的阈值,如果队列剩余小于这个阈值且当前日志 level TRACE, DEBUG or INFO时,会丢弃这些日志。如果为0时,表示不会丢弃日志,队列满了会进行阻塞。默认20
- queueSize,队列大小,默认256
- neverBlock,永不阻塞,如果队列满了则直接返回
异步配置案例
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration scan="true">
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 应用名称 -->
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<!-- 日志编码 -->
<property name="ENCODING" value="UTF-8"/>
<!-- 日志文件路径 -->
<property name="LOG_PATH" value="${BUILD_FOLDER:-logs}/${APP_NAME}"/>
<!-- DEBUG日志文件归档路径 -->
<property name="DEBUG_LOG_PATH" value="${LOG_PATH}/debug/${APP_NAME}"/>
<!-- INFO日志文件归档路径 -->
<property name="INFO_LOG_PATH" value="${LOG_PATH}/info/${APP_NAME}"/>
<!-- WARN日志文件归档路径 -->
<property name="WARN_LOG_PATH" value="${LOG_PATH}/warn/${APP_NAME}"/>
<!-- ERROR日志文件归档路径 -->
<property name="ERROR_LOG_PATH" value="${LOG_PATH}/error/${APP_NAME}"/>
<!-- brave-tracer日志文件归档路径 -->
<property name="BRAVE_TRACER_LOG_PATH" value="${LOG_PATH}/brave-tracer/${APP_NAME}"/>
<property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %level [${APP_NAME:-},%X{traceId},%X{spanId},%X{parentId}] ${PID:- } --- [%thread] %-40.40class{39} %L: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx} \n"/>
<!-- 统一控制台输出日志 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender>
<!-- 统一DEBUG日志输出 -->
<appender name="debug_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}-debug.log</file>
<!-- 日志归档 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${DEBUG_LOG_PATH}-%d{yyyyMMdd}.log.%i</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<Pattern>${LOG_PATTERN}</Pattern>
<charset>${ENCODING}</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 统一INFO日志输出 -->
<appender name="info_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}-info.log</file>
<!-- 日志归档 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${INFO_LOG_PATH}-%d{yyyyMMdd}.log.%i</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<Pattern>${LOG_PATTERN}</Pattern>
<charset>${ENCODING}</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 统一WARN日志输出 -->
<appender name="warn_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}-warn.log</file>
<!-- 日志归档 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${WARN_LOG_PATH}-%d{yyyyMMdd}.log.%i</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<Pattern>${LOG_PATTERN}</Pattern>
<charset>${ENCODING}</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 统一ERROR日志输出 -->
<appender name="error_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}-error.log</file>
<!-- 日志归档 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${ERROR_LOG_PATH}-%d{yyyyMMdd}.log.%i</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<Pattern>${LOG_PATTERN}</Pattern>
<charset>${ENCODING}</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="brave_tracer_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}.trace.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${BRAVE_TRACER_LOG_PATH}-%d{yyyyMMdd}.log.%i</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<Pattern>%msg%n</Pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
<appender name ="async_debug_file" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>20</discardingThreshold>
<queueSize>256</queueSize>
<neverBlock>false</neverBlock>
<appender-ref ref ="debug_file"/>
</appender>
<appender name ="async_info_file" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>20</discardingThreshold>
<queueSize>256</queueSize>
<neverBlock>false</neverBlock>
<appender-ref ref ="info_file"/>
</appender>
<appender name ="async_warn_file" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>20</discardingThreshold>
<queueSize>256</queueSize>
<neverBlock>false</neverBlock>
<appender-ref ref ="warn_file"/>
</appender>
<appender name ="async_error_file" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>20</discardingThreshold>
<queueSize>256</queueSize>
<neverBlock>false</neverBlock>
<appender-ref ref ="error_file"/>
</appender>
<appender name ="async_brave_tracer_file" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>20</discardingThreshold>
<queueSize>256</queueSize>
<neverBlock>false</neverBlock>
<appender-ref ref ="brave_tracer_file"/>
</appender>
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="async_debug_file"/>
<appender-ref ref="async_info_file"/>
<appender-ref ref="async_warn_file"/>
<appender-ref ref="async_error_file"/>
</root>
<!--该logger会继承root的appender -->
<logger name="com.szkingdom" level="info"/>
<logger name="brave.Tracer" level="INFO" additivity="false">
<appender-ref ref="async_brave_tracer_file"/>
</logger>
</configuration>
性能对比
测试的日志框架
- logback1.2.9同步日志
- logback1.2.9异步日志
- logback1.3.7异步日志
- logback1.3.7异步日志
- log4j2.18.0同步日志
- log4j2.18.0异步日志
测试环境
JDK: azul-1.8.0_372
Operating System: Windows 10 家庭中文版 64-bit (10.0, Build 19045) (19041.vb_release.191206-1406)
Language: Chinese (Simplified) (Regional Setting: Chinese (Simplified))
System Manufacturer: ASUSTeK COMPUTER INC.
System Model: X542UQR
BIOS: X542UQR.309 (type: UEFI)
Processor: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (8 CPUs), ~2.0GHz
Memory: 16384MB RAM
Available OS Memory: 16270MB RAM
Page File: 21427MB used, 8659MB available
测试工具
jmh1.09
参数:-f 1 -tu ms -wi 2 -i 5 -t {1,2,4,16,32,64}
测试结果
Threads | logback1.2.9 Async | logback1.2.9 Sync | logback1.3.7 Async | logback1.3.7 Sync | log4j2.18.0 Async | log4j2.18.0 Sync | Unit |
---|---|---|---|---|---|---|---|
1 | 357.728 | 389.144 | 370.710 | 360.961 | 233.370 | 390.871 | ops/ms |
2 | 347.908 | 393.659 | 373.666 | 352.226 | 257.216 | 397.496 | ops/ms |
4 | 241.653 | 375.129 | 371.660 | 338.377 | 254.090 | 406.206 | ops/ms |
8 | 151.541 | 379.731 | 370.519 | 356.223 | 253.066 | 401.591 | ops/ms |
16 | 124.349 | 332.613 | 370.471 | 366.478 | 250.208 | 401.789 | ops/ms |
32 | 118.019 | 310.043 | 370.132 | 360.580 | 248.227 | 359.671 | ops/ms |
64 | 111.478 | 330.585 | 368.876 | 359.243 | 249.226 | 365.272 | ops/ms |