赞
踩
在Java生态中,日志框架有很多,常见的有:jul(JDK提供)、log4j、logback等,随着各种日志框架的出现,在实际项目中使用方式、暴露的日志api、依赖等问题都需要程序员考虑和选择。面对中场景SLF4J
出现了即简单日志门面,自身不记录日志,只作为各个日志框架的门面存在。SLF4J常用的日志实现:log4j、logback,一般有slf4j+log4j(log4j2)、slf4j+logback两种日志组合。
在springboot中,默认使用logback作为日志实现,并对logback进行了默认的配置,我们在项目开发和系统运行中难免会遇到,对logback进行定制和高并发场景日志性能优化。本文主要在springboot环境下对logback的使用、配置、优化进行讲解。
logback 由log4j创始人Ceki Gülcü设计的,相比于其他日志框架,性能更好、占用空间更小。主要包含三个模块:
logback-core:logback的核心基础模块,为其他模块提供支撑
logback-classic:实现了slf4j API,看作log4j的增强,提供了常用的encoder、filter、pattern、AsyncAppender等
Logback-access:可以与 Servlet 容器(如 Jetty 或 Tomcat)集成,以提供丰富而强大的 HTTP 访问日志功能。
springboot版本:
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
logback版本:1.2.11
jdk版本:17.0.2
开始之前先给出,个人开发项目过程中使用的配置方式和细节,暂时没做dev、test、pre、pro环境区分。
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- scan: logback配置文件一旦发生变化,logback支持重新加载文件 scanPeriod: 60s加载一次 debug: 应用启动时在控制台上打印出 context上下文的加载情况 --> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <!-- spring上下文的属性配置 --> <springProperty scope="context" name="APP_NAME" source="spring.application.name"/> <springProperty scope="context" name="APP_PORT" source="server.port"/> <!-- 彩色日志依赖的渲染类 --> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/> <!-- 异常日志渲染类 --> <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/> <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/> <!-- 彩色日志格式 时间: %d{yyyy-MM-dd HH:mm:ss.SSS} 应用名称: ${APP_NAME:-APP} 日志级别: %5p 进程ID: ${PID:- } 线程名称: %15.15t 触发日志类方法: %-40.40logger{39} 代码行号: %-4line 日志内容: %m 换行符: %n 异常: %wEx --> <property name="CONSOLE_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} [${APP_NAME:-APP} %clr(${APP_PORT:-}){blue}] %clr(${PID:- }){magenta} %clr(%5p) %clr([%15.15t]){faint} %clr(%-40.40logger{39} %-4line){cyan} %clr(:){faint} %m%n%wEx"/> <!-- 文件日志格式 --> <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [${APP_NAME:-APP}] %5p ${PID:- } [%15.15t] %-40.40logger{39} %-4line : %m%n%wEx"/> <!-- 定义日志文件存储位置和文件名 --> <property name="LOG_FILE" value="logs/log"/> <!-- 定义日志文件归档格式: log.2023-06-07.0.gz log.2023-06-07.1.gz log.2023-06-07.2.gz --> <property name="ROLLINGPOLICY_FILE_NAME_PATTERN" value="${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz"/> <!-- 单个日志文件大小 --> <property name="ROLLINGPOLICY_MAX_FILE_SIZE" value="20MB"/> <!-- 归档文件占用磁盘总大小,超过后会根据cleanHistoryOnStart实行,决定是否删除 --> <property name="ROLLINGPOLICY_TOTAL_SIZE_CAP" value="1GB"/> <!-- 超过最大磁盘限制后是否删除归档文件 --> <property name="ROLLINGPOLICY_CLEAN_HISTORY_ON_START" value="true"/> <!-- 保留的历史归档日志文件个数 --> <property name="ROLLINGPOLICY_MAX_HISTORY" value="10"/> <!-- 控制台输出 --> <appender name="CONSOLE_OUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${CONSOLE_LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- 文件输出: RollingFileAppender 滚动日志输出--> <appender name="FILE_OUT" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${FILE_LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> <file>${LOG_FILE}</file> <!-- 滚动日志策略: SizeAndTimeBasedRollingPolicy 根据文件大小和时间进行分割归档--> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${ROLLINGPOLICY_FILE_NAME_PATTERN}</fileNamePattern> <cleanHistoryOnStart>${ROLLINGPOLICY_CLEAN_HISTORY_ON_START}</cleanHistoryOnStart> <maxFileSize>${ROLLINGPOLICY_MAX_FILE_SIZE}</maxFileSize> <totalSizeCap>${ROLLINGPOLICY_TOTAL_SIZE_CAP}</totalSizeCap> <maxHistory>${ROLLINGPOLICY_MAX_HISTORY}</maxHistory> </rollingPolicy> </appender> <!-- AsyncAppender 在于高并发下,加如日志队列缓存,减少写磁盘日志的IO次数。可以根据实际情况决定是否使用 --> <appender name="ASYNC_CONSOLE_APPENDER" class="ch.qos.logback.classic.AsyncAppender"> <!-- 如果设置20,表示队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志,设置0则不会丢弃 --> <discardingThreshold>0</discardingThreshold> <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> <queueSize>1024</queueSize> <!-- 队列满了不阻塞调用者--> <neverBlock>false</neverBlock> <!-- 是否记录调用者额外的信息(比如记录后,可以打印行号)会造成性能损耗,默认false只记录线程名字和mdc信息 --> <includeCallerData>true</includeCallerData> <!-- 实际输出日志appender,最多只能添加一个 --> <appender-ref ref ="CONSOLE_OUT"/> </appender> <appender name="ASYNC_FILE_APPENDER" class="ch.qos.logback.classic.AsyncAppender"> <!-- 如果设置20,表示队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志,设置0则不会丢弃 --> <discardingThreshold>0</discardingThreshold> <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> <queueSize>1024</queueSize> <!-- 队列满了不阻塞调用者--> <neverBlock>false</neverBlock> <!-- 实际输出日志appender,最多只能添加一个 --> <appender-ref ref ="FILE_OUT"/> </appender> <!-- 默认的日志输出级别 --> <root level="INFO"> <!--选择合适的日志记录器,可以是多个,会同时写入。一般是一个console,一个file,可以选择异步和同步--> <appender-ref ref="ASYNC_CONSOLE_APPENDER"/> <appender-ref ref="ASYNC_FILE_APPENDER"/> </root> </configuration>
springboot默认加载classpath下 logback-spring.xml
,可以通过properties配置指定路径:
logging.config=classpath:log/logback-spring.xml
如果想区分环境可以通过不同的环境的application-xx.properties指定不同的日志文件路径。
根节点:configuration,作为顶级标签, 可以用来配置一些lockback的全局属性,常见的属性如下:
property 标签可以定义一些属性变量,供其他配置地方通过${xxx}
使用。
springProperty 标签可以获取spring上下文里的属性。
定义转换器,可以用来对日志进行特殊的转换,比如,SpringBoot后启动项目控制台会带有彩色日志样式,是因为使用了org.springframework.boot.logging.logback.ColorConverter
颜色转换器,会把日志用AnsiOutput
进行输出。
Appender是logback中的最核心组件之一,承担日志时间的写入任务,包括:控制台、日志文件、网络输出等。借用一张官方的继承体系图:
途中可以看出常用的appender有三个:
System.out
和 System.err
<appender name="CONSOLE_OUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.log</file>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="FILE_OUT" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${FILE_LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> <file>${LOG_FILE}</file> <!-- 滚动日志策略: SizeAndTimeBasedRollingPolicy 根据文件大小和时间进行分割归档--> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- 指定滚动文件名称的生成规则 --> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern> <!-- 超过最大磁盘限制后是否删除归档文件 --> <cleanHistoryOnStart>true</cleanHistoryOnStart> <!-- 单个日志文件大小 --> <maxFileSize>20MB</maxFileSize> <!-- 归档文件占用磁盘总大小,超过后会根据cleanHistoryOnStart实行,决定是否删除 --> <totalSizeCap>1GB</totalSizeCap> <!-- 保留的历史归档日志文件个数 --> <maxHistory>10</maxHistory> </rollingPolicy> </appender>
<appender name="ASYNC_FILE_APPENDER" class="ch.qos.logback.classic.AsyncAppender">
<!-- 如果设置20,表示队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志,设置0则不会丢弃 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>1024</queueSize>
<!-- 队列满了不阻塞调用者-->
<neverBlock>false</neverBlock>
<!-- 实际输出日志appender,最多只能添加一个 -->
<appender-ref ref ="FILE_OUT"/>
</appender>
Encoder在0.9.19中引入,作用负责将日志事件转换为字节数组,并将字节数组输出到输出流中(具体的appender输出流)。在此之前的版本主要使用Layout
,Layout只能将日志事件转换为String。Layout还无法控制何时写出日志,因此Layout无法对日志事件进行批量控制,相比之下,Encoder不仅可以完全控制写出的字节的格式,而且还可以控制何时(以及是否)写出这些字节内容。
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID:- } [%15.15t] %-40.40logger{39} %-4line : %m%n%wEx</pattern>
<charset>UTF-8</charset>
</encoder>
layout根据pattern规则,将日志事件转换为具体的日志字符串,我们配置的各种日志打印格式,都是由layout解析翻译成字符串。由于官方规则太多,我们以一个具体的例介绍一些常用的规则符号:
时间: %d{yyyy-MM-dd HH:mm:ss.SSS}
应用名称: ${APP_NAME:-APP}
日志级别: %5p
进程ID: ${PID:- }
线程名称: %15.15t
触发日志类方法: %-40.40logger{39}
代码行号: %-4line
日志内容: %m
换行符: %n
异常: %wEx
%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} [${APP_NAME:-APP} %clr(${APP_PORT:-}){blue}] %clr(${PID:- }){magenta} %clr(%5p) %clr([%15.15t]){faint} %clr(%-40.40logger{39} %-4line){cyan} %clr(:){faint} %m%n%wEx
详细规则:Chapter 6: Layouts (qos.ch)
filter 日志过滤器,本人实际用的不是太多,logback 的内置 filter 有一下几种:
级别过滤器 ch.qos.logback.classic.filter.LevelFilter。对指定级别的日志进行具体的操作
阀值过滤器 ch.qos.logback.classic.filter.ThresholdFilter。
表达式过滤器 ch.qos.logback.core.filter.EvaluatorFilter。
Groovy的表达式 ch.qos.logback.classic.boolex.GEventEvaluator。
Java的表达式ch.qos.logback.classic.boolex.JaninoEventEvaluator。使用 Janino 解析java script。
包含标记 ch.qos.logback.classic.boolex.OnMarkerEvaluator
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。