【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 阻塞风险。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值