【Log4j2】logback‑spring.xml日志配置
【一】介绍
说明:SpringBoot 默认集成 logback,logback‑spring.xml 放置在 src/main/resources 下,SpringBoot 会优先加载该配置文件,区别于原生logback.xml,它支持 SpringBoot 的 、环境 profile 隔离等高级特性。
依赖:spring‑boot‑starter‑web 已经自动引入 logback‑classic,无需额外引入 log4j 依赖,SpringBoot2+ 默认舍弃 log4j1,log4j2 是另一套组件,本案例为 logback。
(2)完整项目文件结构
src/main/resources
├── logback-spring.xml # 主入口配置
├── logback
│ ├── console-appender.xml # 控制台输出配置
│ ├── file-appender.xml # 文件滚动输出配置
│ ├── elk-appender.xml # ELK logstash输出配置
│ └── base-logger.xml # logger级别基础配置
└── application.yml # spring.profiles.active 环境配置
(3)核心能力覆盖:
配置文件拆分、子文件引入
INFO、WARN、ERROR分不同的文件
多环境隔离:dev/test/prod
控制台彩色日志输出
文件滚动归档(按大小 + 时间)
ELK 输出(TCP SocketAppender + JSON 格式,对接 logstash)
日志级别控制、logger 根日志、包单独日志级别
日志异步输出、阈值过滤、错误日志单独输出、MDC 等细节
【二】配置逻辑
【1】主配置 logback‑spring.xml
主文件只做环境判断、引入拆分的子配置文件,不写大量重复代码。
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:是否监听配置文件变更,自动重载;scanPeriod:扫描间隔;debug:logback自身调试日志,线上关闭 -->
<configuration scan="true" scanPeriod="30 seconds" debug="false">
<!-- 【1】读取SpringBoot配置文件的属性,把application.yml参数引入logback -->
<!-- 从application.yml读取应用名称,变量名:APP_NAME -->
<springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="demo-app"/>
<!-- 读取日志存储路径,可在yml覆盖,默认 ./logs -->
<springProperty scope="context" name="LOG_PATH" source="logging.file.path" defaultValue="./logs"/>
<!-- 当前激活环境 dev / test / prod -->
<springProperty scope="context" name="ACTIVE_PROFILE" source="spring.profiles.active" defaultValue="dev"/>
<!-- 全局上下文变量,可直接 ${变量名} 使用 -->
<property name="LOG_PATTERN_CONSOLE" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<property name="LOG_PATTERN_FILE" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %X{traceId} - %msg%n"/>
<!-- 【2】按Spring profile环境隔离,logback专属标签 <springProfile>,只在对应环境生效 -->
<!-- dev开发环境:引入控制台、文件、elk;打印更多调试日志 -->
<springProfile name="dev">
<!-- 引入外部拆分的子配置文件 -->
<include resource="logback/console-appender.xml"/>
<include resource="logback/file-appender.xml"/>
<include resource="logback/elk-appender.xml"/>
</springProfile>
<!-- test测试环境:和dev类似,关闭控制台打印,只输出文件+elk -->
<springProfile name="test">
<include resource="logback/file-appender.xml"/>
<include resource="logback/elk-appender.xml"/>
</springProfile>
<!-- prod生产环境:关闭控制台输出,开启文件归档,elk输出,关闭debug日志 -->
<springProfile name="prod">
<include resource="logback/file-appender.xml"/>
<include resource="logback/elk-appender.xml"/>
</springProfile>
<!-- 【3】引入logger基础配置,控制各个包日志级别 -->
<include resource="logback/base-logger.xml"/>
</configuration>
关键标签解释
(1)
scan:true = 监控 xml 文件修改,修改后自动重载配置,开发环境打开,生产建议关闭。
scanPeriod:多久扫描一次配置文件。
debug:logback 框架自身的 debug 日志,生产务必 false,否则会输出大量 logback 内部日志干扰业务。
(2)
logback‑spring.xml 独有,原生 logback.xml 不支持。
scope=“context”:变量放到 logback 全局上下文,所有 appender 都可以读取。
source:对应 application.yml/application.properties 中的 key。
defaultValue:yml 没有配置时的默认值。
(3)
实现配置拆分,把控制台、文件、elk 拆成独立 xml,主配置按需引入,避免单个 xml 过于庞大。
(4)
SpringBoot logback 扩展标签,只有 spring.profiles.active 匹配才会执行标签内逻辑,实现环境隔离。支持逗号写多个环境。
【2】子配置 1:logback/console‑appender.xml 控制台输出
<?xml version="1.0" encoding="UTF-8"?>
<included>
<!-- 控制台输出appender,System.out -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 输出编码 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 开启控制台ANSI颜色,springboot提供颜色转换器 -->
<pattern>%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr([%thread]){faint} %clr(%-5level) %clr(%logger{40}){cyan} - %clr(%msg%n){green}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 将appender绑定到root,只有引入该文件才会启用控制台输出 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</included>
⚠️ 文件根标签必须是 ,不能写,include 引入的子文件专用。
(1)颜色语法 %clr(内容){颜色}
logback‑classic 支持,控制台彩色输出,文件日志不要用 % clr,会写入 ANSI 乱码
%clr(内容){颜色}
%clr(%‑5level){red}
颜色可选值:faint,red,green,yellow,blue,cyan,magenta
faint 浅灰色
red 红色,用于 ERROR 级别
green 绿色
yellow 黄色,WARN
cyan 青色
blue 蓝色
按级别自动变色语法:
%clr(%‑5level){WARN=yellow,ERROR=red,INFO=green,DEBUG=blue}
示例带级别区分颜色完整 pattern:
<pattern>
%clr(%d{yyyy‑MM‑dd HH:mm:ss.SSS}){faint}
%clr([%15.15thread]){faint}
%clr(%‑5level){%red}{WARN=yellow,INFO=green,DEBUG=blue}
%clr(%logger{40}){cyan}
‑ %msg%n
</pattern>
案例
<!-- 业务侧直接赋值完整pattern,不要再套 ${CONSOLE_LOG_PATTERN:-} -->
<property scope="context" name="CONSOLE_PATTERN"
value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){red} %clr(%-5level) %clr([%thread]){blue} %clr(%X){magenta} %clr(%logger{30}){green} %clr([%method,%line]){cyan} - %msg%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_PATTERN}</pattern>
<charset>${CONSOLE_LOG_CHARSET}</charset>
</encoder>
</appender>

(2)Pattern 格式占位说明
%d{yyyy‑MM‑dd HH:mm:ss.SSS}:日期时间毫秒
[%thread]:输出线程名
%-5level:日志级别,占 5 字符左对齐 INFO WARN ERROR
%logger{40}:打印类名,最多 40 字符,长类名会缩写
%X{traceId}:MDC 链路追踪 ID,skywalking/sleuth 会注入,没有则为空
%msg%n:业务日志消息 + 换行
(3)Logback PatternLayout 占位符大全
语法
%[修饰符]转换词{参数}
修饰符:-左对齐,数字代表最小 / 最大宽度;例:%-5level,占 5 字符左对齐
{}:给转换器传参数
修饰语法(宽度、对齐、截断)
%[最小宽度].[最大宽度]转换符
%-:左对齐,不加就是右对齐
数字:最小宽度,不足补空格
.数字:最大长度,超过就截断
%-5level // 左对齐,最少5字符
%10thread // 右对齐,最少10字符
%.40logger // 最多40字符,超过截断
%15.15thread // 固定15字符,超长截断
时间日期相关
<!--示例 -->
<pattern>%d{yyyy‑MM‑dd HH:mm:ss.SSS}</pattern>

线程相关
(1)%t / %thread:输出线程名称 http‑nio‑8080‑exec‑1
(2)%15.15thread:固定 15 字符,过长截断
示例:[%thread] → [http‑nio‑8080‑exec‑1]
日志级别
%p / %level 日志级别 TRACE,DEBUG,INFO,WARN,ERROR INFO
%-5level 左对齐,占 5 个字符 INFO WARN
级别顺序:TRACE < DEBUG < INFO < WARN < ERROR
日志来源:Logger 名称(类名)

%logger{30}:总长度超过 30,包名缩写,保留类名。
com.aaa.bbbccc.service.TestService → 缩写为 c.a.b.service.TestService
输出方法、行号(调试用,性能差,生产谨慎)
⚠️ 获取堆栈信息,性能损耗大,生产环境尽量不要开启
| 占位符 | 说明 |
|---|---|
| %M / %method | 输出执行日志的方法名 |
| %L / %line | 输出代码行号 |
| %F / %file | 输出 java 文件名 |
| %C / %class | 输出调用类全名,性能开销很高 |
示例:%M:%L → queryUser:42
日志消息、异常堆栈
(1)%m / %msg / %message:业务输出日志消息
(2)%n:平台换行符(推荐,不要手动写\n)
(3)%ex / %exception / %throwable:打印异常堆栈;如果没有异常,什么都不输出
(4)%ex{short}:简短异常,只打印第一行异常信息,不输出完整栈
如果 pattern没有写 % ex,logback 依然会自动打印异常堆栈。
MDC(非常重要,链路追踪 traceId、spanId)
MDC 存上下文变量,SkyWalking/Sleuth/TLog 把 traceId 放到 MDC。
(1)%X{key}:取 MDC 中 key 对应的值,没有则为空字符串
(2)%X:打印 MDC 全部 key=value
%X{traceId} //链路id
%X{spanId}
常用 pattern 片段:%X{traceId}
SpringBoot ANSI 颜色占位 %clr
【3】子配置 2:logback/file‑appender.xml 文件滚动日志
实现:按天滚动,同时限制单个文件最大大小,历史日志压缩,保留指定天数,区分 info 普通日志和 error 错误日志。
<?xml version="1.0" encoding="UTF-8"?>
<included>
<!-- 【1】INFO 全部业务日志,滚动文件 -->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 当前正在写的日志文件 -->
<file>${LOG_PATH}/${APP_NAME}.log</file>
<!-- 滚动策略:时间+大小双策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 归档文件名,%d日期,%i大小分割序号 -->
<fileNamePattern>${LOG_PATH}/history/${APP_NAME}-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<!-- 单文件到达该大小,生成新分片 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 最多保留30天历史日志 -->
<maxHistory>30</maxHistory>
<!-- 所有归档日志总上限,防止磁盘占满,单位KB MB GB -->
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<!-- 过滤器:只输出INFO及以上,排除ERROR,ERROR交给另外appender -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMismatch>ACCEPT</onMismatch>
<onMatch>DENY</onMatch>
</filter>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${LOG_PATTERN_FILE}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 【2】ERROR单独日志文件,只收集ERROR级别日志,方便排查问题 -->
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APP_NAME}-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/history/${APP_NAME}-error-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy>
<!-- 只匹配ERROR级别,其余全部丢弃 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>${LOG_PATTERN_FILE}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 异步输出appender,提升性能,磁盘IO不阻塞业务线程 -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 绑定上面两个文件输出源 -->
<appender-ref ref="FILE_INFO"/>
<appender-ref ref="FILE_ERROR"/>
<!-- 队列容量,日志堆积上限 -->
<queueSize>512</queueSize>
<!-- 队列满了,true丢弃日志;false阻塞业务线程,生产建议true,防止日志打满拖垮业务 -->
<neverBlock>true</neverBlock>
<!-- 是否把异常也加入异步队列 -->
<includeCallerData>false</includeCallerData>
</appender>
<!-- 将异步文件输出绑定root -->
<root level="INFO">
<appender-ref ref="ASYNC_FILE"/>
</root>
</included>
关键配置说明
(1)RollingFileAppender:滚动文件输出,日志文件到达条件后归档。
(2)fileNamePattern:.gz 代表自动压缩历史日志,节省磁盘。%i是同一个日期下,文件超过 maxFileSize 后的分片序号。
(3)maxHistory:保留多少天日志,旧日志自动删除。
(4)totalSizeCap:所有归档日志总大小上限,防止磁盘爆盘,优先级高于 maxHistory。
(5)LevelFilter级别过滤器:
ACCEPT:接受这条日志
DENY:直接丢弃,不往下传递
NEUTRAL:交给下一个 filter 继续判断
(6)AsyncAppender异步日志:
生产强烈建议开启,日志写磁盘是 IO 操作,同步会阻塞业务线程;异步使用内存队列缓存日志。
queueSize内存队列大小,日志瞬间爆发超过队列,neverBlock=true会丢弃日志,适合高并发业务。
includeCallerData:获取调用栈信息,开启会损耗性能,线上关闭。
【4】子配置 3:logback/elk‑appender.xml 对接 ELK(Logstash)
两种方案:
(1)TCP SocketAppender:logback 直接把日志发送给 logstash tcp 端口,生产常用;
(2)输出 JSON 文件,filebeat 采集磁盘文件(更稳定,推荐线上)。
依赖:需要引入 logstash‑logback‑encoder,输出 JSON 格式日志。
<!-- pom.xml引入 -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash‑logback‑encoder</artifactId>
<version>7.4</version>
</dependency>
logback/elk‑appender.xml
<?xml version="1.0" encoding="UTF-8"?>
<included>
<!-- 方式1:TCP直接发送日志到Logstash -->
<appender name="LOGSTASH_TCP" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!-- logstash地址端口,多个logstash写多个 <destination> 做负载 -->
<destination>127.0.0.1:5000</destination>
<!-- 重连策略,断开自动重试 -->
<reconnectionDelay>5000</reconnectionDelay>
<!-- 写入缓冲区大小 -->
<writeBufferSize>1048576</writeBufferSize>
<!-- JSON编码器 -->
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<!-- 自定义字段,写入es索引,区分服务名、环境 -->
<customFields>{"app":"${APP_NAME}","profile":"${ACTIVE_PROFILE}"}</customFields>
<!-- MDC字段全部输出,traceId等链路信息进入JSON -->
<includeMdcKeyName>traceId</includeMdcKeyName>
<includeMdcKeyName>spanId</includeMdcKeyName>
<!-- 输出堆栈异常栈 -->
<throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
<maxDepthPerThrowable>30</maxDepthPerThrowable>
<shortenedClassNameLength>20</shortenedClassNameLength>
</throwableConverter>
</encoder>
</appender>
<!-- 异步包装,网络IO不阻塞业务线程!重要!网络波动会耗时,必须异步 -->
<appender name="ASYNC_LOGSTASH" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="LOGSTASH_TCP"/>
<queueSize>1024</queueSize>
<neverBlock>true</neverBlock>
<includeCallerData>false</includeCallerData>
</appender>
<!-- 将elk输出绑定root -->
<root level="INFO">
<appender-ref ref="ASYNC_LOGSTASH"/>
</root>
</included>
⚠️ TCP 方式风险:logstash 宕机,队列满后会丢弃日志(neverBlock=true),如果业务不允许丢日志,优先选择输出 JSON 日志文件,filebeat 采集磁盘文件上传 logstash。
Filebeat 采集方案(推荐生产)
不使用 TCP,直接输出 JSON 格式到磁盘,filebeat 读文件。
<appender name="FILE_JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APP_NAME}.json.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/history/${APP_NAME}-%d{yyyy‑MM‑dd}.json.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"app":"${APP_NAME}","profile":"${ACTIVE_PROFILE}"}</customFields>
<includeMdcKeyName>traceId</includeMdcKeyName>
</encoder>
</appender>
【5】子配置 4:logback/base‑logger.xml 日志级别控制
控制全局 root 级别,指定包的日志级别,屏蔽第三方框架多余日志。
<?xml version="1.0" encoding="UTF-8"?>
<included>
<!-- 单独logger:指定包路径,设置日志级别,additivity是否向上传递给root appender -->
<!-- mybatis打印sql,开发环境打开,生产关闭 -->
<logger name="com.mapper" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_FILE"/>
</logger>
<!-- 屏蔽spring框架大量debug日志 -->
<logger name="org.springframework" level="WARN"/>
<logger name="org.apache" level="WARN"/>
<logger name="com.alibaba.druid" level="WARN"/>
<!-- root根日志,所有日志最终的出口,优先级最低 -->
<root level="INFO">
<!-- 这里不写appender-ref,交给各个环境子文件追加appender-ref,避免重复绑定 -->
</root>
</included>
logger 标签关键参数
(1):针对指定包 / 类设置日志级别。
(2)level:TRACE < DEBUG < INFO < WARN < ERROR < OFF
(3)additivity=“true/false”:是否向上传递日志到 root 根 logger。
additivity=true:日志输出当前 logger 的 appender,同时再交给 root 的 appender,会造成日志重复打印。
additivity=false:只走当前 logger 绑定的 appender,不再向上传递,避免重复输出。
root:所有日志的顶层根节点,所有没有匹配的 logger 的日志,全部交给 root。
【6】application.yml 配套配置
spring:
application:
name: logback‑demo
profiles:
active: dev # 修改此处切换 dev/test/prod
logging:
file:
path: ./logs
【三】案例分析
【1】文件清单
logback‑spring.xml:主入口配置
logback/base‑skw.xml:顶层聚合引入文件
logback/defaults.xml:自定义转换器、全局变量、第三方包日志级别
logback/appender‑def.xml:Skywalking 占位 NOPAppender
logback/appender‑grpc‑skw.xml:Skywalking GRPC 日志上报 Appender
logback/appender‑console.xml:控制台输出 Appender
logback/appender‑file.xml:文件滚动输出,分 DEBUG/INFO/WARN/ERROR 独立文件 + 异步包装
整体架构:主文件按 profile 环境隔离,通过多层 include 拆分配置;Skywalking 做兼容占位;文件按日志级别拆分独立日志文件;全部文件输出套异步 Appender;支持外部 application.yml 覆盖日志 pattern、路径等参数。
【2】整体执行加载流程
logback‑spring.xml(主)
↓ include
base‑skw.xml(聚合入口,<included>标签)
├─ include defaults.xml 【转换器、全局property、第三方包日志抑制】
├─ include appender‑def.xml 【SKYWALKING NOP占位Appender】
├─ include appender‑grpc‑skw.xml 【真正Skywalking GRPC上报Appender】
├─ include appender‑console.xml 【控制台CONSOLE appender定义】
└─ include appender‑file.xml 【多级别文件+异步FILE_XXX_ASYNC appender定义】
logback‑spring.xml 内部 <springProfile> 按环境,把上面定义好的各个appender‑ref绑定到root标签。
⚠️关键顺序:appender‑grpc‑skw.xml必须在appender‑console.xml/appender‑file.xml之前引入。注释说明:Skywalking 要重写 PATTERN 变量,必须在 console/file appender 加载前执行,否则 pattern 变量不会被覆盖。
appender‑def.xml定义SKYWALKING的NOPAppender空实现(空 appender,不输出任何日志),作为占位;当引入appender‑grpc‑skw.xml,重名 name=“SKYWALKING” 会覆盖前面 NOPAppender。
设计目的:没有 Skywalking 探针时,使用 NOP 空实现,不会报错;有探针环境,加载 grpc 真实上报 appender。
【3】文件解析
(1)logback‑spring.xml【主配置】
<configuration scan="true" scanPeriod="30 seconds" debug="false">
<include resource="logback/base‑skw.xml"/>
<!-- local本地开发环境 -->
<springProfile name="local">
<root level="DEBUG">
<appender‑ref ref="CONSOLE"/>
<appender‑ref ref="FILE_ASYNC"/>
<appender‑ref ref="SKYWALKING"/>
<appender‑ref ref="DEBUG_FILE_ASYNC"/>
<appender‑ref ref="INFO_FILE_ASYNC"/>
<appender‑ref ref="WARN_FILE_ASYNC"/>
<appender‑ref ref="ERROR_FILE_ASYNC"/>
</root>
</springProfile>
<!-- dev sit环境 -->
<springProfile name="dev,sit">
<root level="DEBUG">
<appender‑ref ref="SKYWALKING"/>
<appender‑ref ref="FILE_ASYNC"/>
<appender‑ref ref="DEBUG_FILE_ASYNC"/>
<appender‑ref ref="INFO_FILE_ASYNC"/>
<appender‑ref ref="WARN_FILE_ASYNC"/>
<appender‑ref ref="ERROR_FILE_ASYNC"/>
</root>
</springProfile>
<!-- uat uat1生产预发环境 -->
<springProfile name="uat,uat1">
<root level="DEBUG">
<appender‑ref ref="SKYWALKING"/>
<appender‑ref ref="FILE_ASYNC"/>
<appender‑ref ref="DEBUG_FILE_ASYNC"/>
<appender‑ref ref="INFO_FILE_ASYNC"/>
<appender‑ref ref="WARN_FILE_ASYNC"/>
<appender‑ref ref="ERROR_FILE_ASYNC"/>
</root>
</springProfile>
</configuration>
(1)只做两件事:引入聚合配置文件;不同环境给 root 绑定不同的 appender 引用。
(2)local环境:开启CONSOLE控制台输出;dev/sit/uat 环境没有绑定 CONSOLE,关闭控制台打印。
(3)root 统一level=“DEBUG”,所有日志输出 DEBUG 级别;可以通过 defaults.xml 里面单独 logger 降低第三方包级别。
(4)appender 全部预先在被 include 的子 xml 中定义,主配置只做引用绑定,职责分离。
注意:springProfile是 logback‑spring.xml 专属标签,原生 logback.xml 不支持。
(2)base‑skw.xml【聚合层,】
<included>
<include resource="logback/defaults.xml"/>
<include resource="logback/appender‑def.xml"/>
<!-- SKYWALKING重写PATTERN,必须在console/file appender之前! -->
<include resource="logback/appender‑grpc‑skw.xml"/>
<include resource="logback/appender‑console.xml"/>
<include resource="logback/appender‑file.xml"/>
</included>
(1)全部子文件在这里统一引入,logback‑spring.xml只需要一行 include 即可,简化主文件。
(2)加载顺序非常关键:
defaults.xml:定义CONSOLE_PATTERN、FILE_PATTERN基础变量
appender‑def.xml:NOP 占位SKYWALKINGappender
appender‑grpc‑skw.xml:覆盖SKW_PATTERN变量,并且覆盖 name=“SKYWALKING” 的 appender 定义
之后才加载 console、file,读取已经被 skywalking 处理后的 pattern 变量
如果顺序颠倒:先加载 console/file,再加载 grpc‑skw,pattern 已经被读取使用,后续修改 property 不会生效。
(3)defaults.xml【全局默认配置】
<included>
<!-- 注册自定义转换规则 -->
<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"/>
<!-- 自定义转换器 %ip 输出本机IP,业务自定义实现LogBackIpConverter -->
<conversionRule conversionWord="ip" converterClass="com.hzyt.core.log.logback.LogBackIpConverter"/>
<!-- 从Spring环境读取yml/properties配置 -->
<springProperty scope="context" name="SPRING_APP_NAME" source="spring.application.name"/>
<springProperty scope="context" name="SERVER_PORT" source="server.port"/>
<springProperty scope="context" name="SKYWALKING_PATTERN" source="logging.pattern.skywalking"/>
<!-- 定义全局Pattern变量,支持yml外部覆盖,${变量:-默认值} 冒号‑代表默认值 -->
<property scope="context" name="CONSOLE_PATTERN"
value="${CONSOLE_LOG_PATTERN:‑%d{yyyy‑MM‑dd HH:mm:ss.SSS} %‑5level[%thread] %logger{60}#%method: %msg%n}"/>
<property scope="context" name="FILE_PATTERN"
value="${FILE_LOG_PATTERN:‑%d{yyyy‑MM‑dd HH:mm:ss.SSS}|%‑5level|${SPRING_APP_NAME}@%ip:${SERVER_PORT}|[%thread] %logger{60}#%method: %msg%n}"/>
<!-- 抑制第三方组件多余日志,把一些框架日志设置为WARN/ERROR,减少噪音 -->
<logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
<logger name="org.springframework" level="INFO"/>
</included>
关键点
(1)conversionRule 注册自定义 pattern 占位符
%clr:SpringBoot 提供的 ANSI 颜色转换器
%ip:项目自定义转换器,日志中打印本机服务器 IP,在 FILE_PATTERN 中使用%ip
(2)${xxx:-default}语法:如果外部环境变量 /yml 中没有配置 xxx,则使用后面默认值。
业务可以在 application.yml 配置:
logging:
pattern:
console: xxx
file: xxx
skywalking: xxx
直接覆盖 logback 内部 pattern,不用修改 xml。
(3)FILE_PATTERN 中内置:应用名、本机 ip、服务端口,日志文件每条日志都能看到这台机器信息,分布式排查很方便。
(4)大量第三方包 logger,用于压制框架冗余 DEBUG 日志。
(4)appender‑def.xml【Skywalking NOP 占位 Appender】
<included>
<appender name="SKYWALKING" class="ch.qos.logback.core.helpers.NOPAppender"/>
</included>
(1)NOPAppender:No‑Operation,空 appender,接收日志,什么都不做,不输出、不上报。
(2)场景:没有挂载 Skywalking java agent 探针的时候,appender‑grpc‑skw.xml不会被 agent 增强加载,此时使用这个 NOP 占位。
(3)主配置 root 依然引用ref=“SKYWALKING”,不会报找不到 appender 异常,实现探针有无的兼容性。
当 Skywalking Agent 启动,会加载appender‑grpc‑skw.xml,同名 name=“SKYWALKING” 直接覆盖 NOPAppender,替换为真实 GRPC 上报逻辑。
(5)appender‑grpc‑skw.xml【Skywalking GRPC 日志上报】
<included>
<property scope="context" name="SKW_PATTERN"
value="${SKYWALKING_PATTERN:‑[%thread] %logger{60}#%method: %msg%n}"/>
<appender name="SKYWALKING" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${SKW_PATTERN}</pattern>
</layout>
</encoder>
</appender>
</included>
(1)TraceIdPatternLogbackLayout:Skywalking 提供的 layout,自动识别 MDC 的%tid(traceId),pattern 中可以直接写%tid,不需要手动写%X{tid}。
(2)GRPCLogClientAppender:通过 GRPC 协议,直接把日志发送到 Skywalking‑OAP 服务,在 Skywalking UI 展示日志。
(3)覆盖name="SKYWALKING"appender,替换掉前面 NOP 空实现。
⚠️注意:这个 appender 本身没有套 AsyncAppender,GRPC 网络 IO 会阻塞业务线程,生产建议外层包 AsyncAppender。
(6)appender‑console.xml【控制台输出】
<included>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_PATTERN}</pattern>
<charset>${CONSOLE_LOG_CHARSET}</charset>
</encoder>
</appender>
</included>
(1)读取全局变量CONSOLE_PATTERN,支持 yml 覆盖。
(2)只有 local 环境 root 才引用 CONSOLE,dev/sit/uat 环境不绑定,服务器环境关闭控制台输出,减少 stdout 性能损耗。
(7)appender‑file.xml【文件输出】
设计特点:按日志级别拆分独立文件:
app‑name.log(全部日志)、xxx.debug.log、xxx.info.log、xxx.warn.log、xxx.error.log;每一个文件都包装一层AsyncAppender。
<included>
<!-- springProperty读取yml配置,日志路径、文件名 -->
<springProperty scope="context" name="SPRING_APP_NAME" source="spring.application.name"/>
<property scope="context" name="FILE_PATH" value="${user.home}/logs/${SPRING_APP_NAME}"/>
<!-- 各个日志文件名称、归档pattern模板 -->
<property name="FILE_NAME" value="${LOG_FILE_NAME:-${SPRING_APP_NAME}.log}"/>
<property name="DEBUG_FILE_NAME" value="${LOG_FILE_NAME:-${SPRING_APP_NAME}.debug.log}"/>
<property name="INFO_FILE_NAME" value="${LOG_FILE_NAME:-${SPRING_APP_NAME}.info.log}"/>
<property name="WARN_FILE_NAME" value="${LOG_FILE_NAME:-${SPRING_APP_NAME}.warn.log}"/>
<property name="ERROR_FILE_NAME" value="${LOG_FILE_NAME:-${SPRING_APP_NAME}.error.log}"/>
<!-- 滚动策略全局参数,可yml覆盖 -->
<property name="MAX_HISTORY" value="${LOGBACK_ROLLINGPOLICY.MAX_HISTORY:-100}"/>
<property name="MAX_FILE_SIZE" value="${LOGBACK_ROLLINGPOLICY.MAX_FILE_SIZE:-100MB}"/>
<property name="TOTAL_SIZE_CAP" value="${LOGBACK_ROLLINGPOLICY.TOTAL_SIZE_CAP:-10GB}"/>
<!--1. FILE:全部日志,无级别过滤 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${FILE_PATH}/${FILE_NAME}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${FILE_PATH}/${FILE_NAME_PATTERN}</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>${MAX_HISTORY}</maxHistory>
<totalSizeCap>${TOTAL_SIZE_CAP}</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_PATTERN}</pattern>
<charset>${FILE_LOG_CHARSET}</charset>
</encoder>
</appender>
<!-- 包装异步 -->
<appender name="FILE_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>512</queueSize>
<appender‑ref ref="FILE"/>
</appender>
<!--2. DEBUG_FILE,ThresholdFilter过滤,>=DEBUG -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
...
</appender>
<appender name="DEBUG_FILE_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender‑ref ref="DEBUG_FILE"/>
</appender>
<!--3. INFO_FILE,>=INFO -->
<!--4. WARN_FILE,>=WARN -->
<!--5. ERROR_FILE,>=ERROR -->
</included>
设计逻辑
(1)每个日志级别单独文件:
debug.log:只存 DEBUG 及以上
info.log:只存 INFO 及以上
warn.log:只存 WARN 及以上
error.log:只存 ERROR 级别日志
app.log:全部日志完整备份
优点:排查问题看 error.log 直接看错误,不用翻海量 info 日志;缺点:磁盘 IO 压力变大,同一条日志会写入多个文件,磁盘占用成倍增加。
例如一条 ERROR 日志,会同时写入:app.log + debug.log + info.log + warn.log + error.log,一共 5 份。
(2)每个RollingFileAppender都套独立AsyncAppender,磁盘 IO 异步,不阻塞业务线程。
(3)滚动参数全部抽成 property,支持 application.yml 外部覆盖,不用修改 xml:
# application.yml示例,修改日志保留策略
logback:
rollingpolicy:
max‑history: 60
max‑file‑size: 200MB
total‑size‑cap: 20GB
(4)日志路径默认 u s e r . h o m e / l o g s / {user.home}/logs/ user.home/logs/{spring.application.name},输出到用户家目录 logs 下。
【4】整套配置的优点
高度解耦分层:主配置、全局变量、appender 定义、skywalking 逻辑拆分,职责清晰。
Skywalking 探针兼容:有无 agent 都不会报错,NOP 占位 + 同名覆盖方案。
多环境隔离:springProfile区分 local/dev/sit/uat,本地打开控制台,服务器关闭控制台。
外部可配置:pattern、日志路径、滚动策略,全部支持 application.yml 覆盖,不用改 xml。
日志分级存储:DEBUG/INFO/WARN/ERROR 独立文件,方便运维排查故障。
自定义转换器:日志打印本机 IP、服务名、端口,分布式环境快速定位机器。
文件输出全部异步,降低 IO 阻塞风险。

410

被折叠的 条评论
为什么被折叠?



