Logback 日志框架完整技术文档
目录
一、Logback 简介
1.1 什么是 Logback?
Logback 是由 Log4j 创始人 Ceki Gülcü 设计的另一个开源日志组件,是 Log4j 的继任者。
核心优势:
- ⚡ 性能更优(比 Log4j 快约 10 倍)
- 🔄 自动重载配置文件
- 📦 更小的内存占用
- 🎯 原生支持 SLF4J API
- 🛡️ 谨慎的模式(遇到错误会降级而非崩溃)
- 📝 完善的日志归档和压缩
1.2 与其他日志框架的关系
┌─────────────────────────────────────┐
│ 应用程序代码 │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ SLF4J API(日志门面) │
└─────────────────────────────────────┘
│
┌────────┴────────┐
▼ ▼
┌────────┐ ┌──────────┐
│ Logback│ │ Log4j2 │
└────────┘ └──────────┘1.3 Maven 依赖
<dependencies>
<!-- SLF4J API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<!-- Logback Classic(包含 Core) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
</dependency>
<!-- 可选:Logstash Encoder(JSON 格式日志) -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
</dependencies>1.4 配置文件加载顺序
Logback 按以下顺序查找配置文件:
logback-test.xml(Classpath)logback.groovy(Classpath)logback.xml(Classpath)- 如果都没找到,使用
BasicConfigurator(控制台输出)
指定配置文件:
java -Dlogback.configurationFile=/path/to/config.xml -jar app.jar二、核心组件
Logback 由三个核心模块组成:
2.1 Logger(日志记录器)
负责捕获日志信息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserService {
// 获取 Logger 实例
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public void login(String username) {
logger.debug("用户 {} 开始登录", username);
logger.info("用户 {} 登录成功", username);
logger.warn("用户 {} 密码即将过期", username);
logger.error("用户 {} 登录失败", username);
}
}2.2 Appender(输出目的地)
决定日志输出到哪里(控制台、文件、数据库等)。
2.3 Layout/Encoder(格式化器)
决定日志的输出格式。
架构图
Logger (决定是否记录)
│
├─ Filter (过滤器)
│
├─ Appender (输出到哪里)
│ │
│ ├─ Encoder/Layout (格式化)
│ │
│ └─ Filter (Appender 级别过滤)
│
└─ 继承父 Logger 的 Appender (可配置)三、Appender 详解
Appender 是日志输出的目的地,Logback 提供了多种 Appender。
3.1 ConsoleAppender(控制台输出)
类名: ch.qos.logback.core.ConsoleAppender
功能: 将日志输出到控制台(System.out 或 System.err)
配置示例:
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 输出目标:System.out(默认)或 System.err -->
<target>System.out</target>
<!-- 编码器:定义输出格式 -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 过滤器:只输出 INFO 及以上级别 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>适用场景:
- ✅ 开发环境调试
- ✅ Docker 容器(日志收集通过标准输出)
- ✅ Kubernetes Pod(kubectl logs)
- ❌ 生产环境(除非使用容器)
3.2 FileAppender(文件输出)
类名: ch.qos.logback.core.FileAppender
功能: 将日志写入指定文件
配置示例:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- 日志文件路径 -->
<file>logs/application.log</file>
<!-- 是否追加到文件末尾(false 会覆盖) -->
<append>true</append>
<!-- 立即刷新(true 安全但慢,false 快但可能丢日志) -->
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>缺点:
- ❌ 文件会无限增长
- ❌ 没有自动归档和清理
- ❌ 生产环境不推荐
3.3 RollingFileAppender(滚动文件输出)⭐
类名: ch.qos.logback.core.rolling.RollingFileAppender
功能: 将日志写入文件,并支持自动滚动(按时间、大小等)
核心配置:
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 当前活动日志文件 -->
<file>logs/application.log</file>
<!-- 滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 归档文件命名模式 -->
<fileNamePattern>logs/application.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<!-- 单个文件最大大小 -->
<maxFileSize>100MB</maxFileSize>
<!-- 保留的归档文件数量(天数) -->
<maxHistory>30</maxHistory>
<!-- 所有归档文件总大小上限 -->
<totalSizeCap>10GB</totalSizeCap>
<!-- 是否在启动时清理过期文件 -->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>3.3.1 滚动策略详解
A. TimeBasedRollingPolicy(按时间滚动)
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天滚动 -->
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 按小时滚动 -->
<!-- <fileNamePattern>logs/app.%d{yyyy-MM-dd-HH}.log</fileNamePattern> -->
<!-- 按月滚动 -->
<!-- <fileNamePattern>logs/app.%d{yyyy-MM}.log</fileNamePattern> -->
<maxHistory>30</maxHistory>
</rollingPolicy>B. SizeBasedRollingPolicy(按大小滚动)
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>logs/app.%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>C. SizeAndTimeBasedRollingPolicy(按时间+大小)⭐ 推荐
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- %d 按天,%i 是同一天内的序号 -->
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>生成的文件示例:
logs/
├── application.log # 当前活动文件
├── application.2025-12-07.0.log.gz
├── application.2025-12-07.1.log.gz
├── application.2025-12-07.2.log.gz
├── application.2025-12-06.0.log.gz
└── application.2025-12-05.0.log.gz3.4 AsyncAppender(异步输出)
类名: ch.qos.logback.classic.AsyncAppender
功能: 异步写入日志,提升性能(日志事件先放入队列,后台线程处理)
配置示例:
<configuration>
<!-- 实际的输出 Appender -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d %p %c - %m%n</pattern>
</encoder>
</appender>
<!-- 异步包装器 -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 引用实际的 Appender -->
<appender-ref ref="FILE"/>
<!-- 队列大小(默认 256) -->
<queueSize>512</queueSize>
<!-- 丢弃阈值:队列剩余容量小于此值时丢弃 TRACE/DEBUG/INFO -->
<!-- 设为 0 表示永不丢弃 -->
<discardingThreshold>0</discardingThreshold>
<!-- 队列满时是否阻塞(false 会丢弃日志) -->
<neverBlock>false</neverBlock>
<!-- 是否提取调用者信息(会影响性能) -->
<includeCallerData>false</includeCallerData>
<!-- 最大刷新时间(毫秒) -->
<maxFlushTime>1000</maxFlushTime>
</appender>
<root level="INFO">
<appender-ref ref="ASYNC_FILE"/>
</root>
</configuration>性能对比:
| 配置 | QPS | 延迟 |
|---|---|---|
| 同步文件 Appender | ~5000 | 高 |
| 异步文件 Appender | ~50000 | 低 |
注意事项:
- ⚠️ 应用关闭时可能丢失队列中的日志(设置合理的
maxFlushTime) - ⚠️
includeCallerData=true会显著影响性能 - ✅ 生产环境强烈推荐
3.5 其他常用 Appender
SMTPAppender(邮件通知)
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>smtp.gmail.com</smtpHost>
<smtpPort>587</smtpPort>
<STARTTLS>true</STARTTLS>
<username>your-email@gmail.com</username>
<password>your-password</password>
<to>admin@company.com</to>
<from>app@company.com</from>
<subject>应用错误: %logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
<!-- 只发送 ERROR 级别 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>SyslogAppender(发送到 Syslog 服务器)
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<facility>USER</facility>
<suffixPattern>[%thread] %logger %msg</suffixPattern>
</appender>DBAppender(数据库)
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>com.mysql.cj.jdbc.Driver</driverClass>
<url>jdbc:mysql://localhost:3306/logs</url>
<user>root</user>
<password>password</password>
</connectionSource>
</appender>四、Layout 和 Encoder
4.1 Encoder vs Layout
| 特性 | Encoder | Layout |
|---|---|---|
| 输出格式 | 字节流(byte[]) | 字符串(String) |
| 功能 | 更强大(可压缩、加密) | 只格式化 |
| 推荐程度 | ✅ 推荐 | ⚠️ 旧版 |
4.2 PatternLayoutEncoder(最常用)
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
<immediateFlush>true</immediateFlush>
</encoder>4.3 LogstashEncoder(JSON 格式)
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<!-- 添加自定义字段 -->
<customFields>{"app":"my-app","env":"prod"}</customFields>
<!-- 包含 MDC -->
<includeMdc>true</includeMdc>
<!-- 包含上下文信息 -->
<includeContext>true</includeContext>
<!-- 包含调用者信息 -->
<includeCallerData>false</includeCallerData>
<!-- 堆栈哈希 -->
<stackTracePattern>
<omitCommonFrames>true</omitCommonFrames>
</stackTracePattern>
</encoder>输出示例:
{
"@timestamp": "2025-12-08T14:30:25.123+08:00",
"message": "用户登录成功",
"logger_name": "com.Glowxq.UserService",
"thread_name": "http-nio-8080-exec-1",
"level": "INFO",
"app": "my-app",
"env": "prod",
"mdc": {
"traceId": "abc123",
"userId": "user_12345"
}
}五、Filter 过滤器
Filter 用于精确控制哪些日志事件被记录。
5.1 Filter 的位置
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/app.log</file>
<!-- Appender 级别的 Filter -->
<filter class="...">
...
</filter>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<!-- Logger 级别的 Filter -->
<turboFilter class="...">
...
</turboFilter>
</configuration>5.2 Filter 决策
Filter 有三种返回值:
| 返回值 | 含义 |
|---|---|
ACCEPT | 接受日志,不再询问后续 Filter |
DENY | 拒绝日志,不再询问后续 Filter |
NEUTRAL | 中立,继续询问下一个 Filter |
5.3 LevelFilter(精确级别过滤)
配置示例:
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 匹配的级别 -->
<level>ERROR</level>
<!-- 匹配时的决策 -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的决策 -->
<onMismatch>DENY</onMismatch>
</filter>使用场景: 单独收集 ERROR 日志
<!-- ERROR 专用文件 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/error.log</file>
<!-- 只记录 ERROR 级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>90</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d %p %c - %m%n%ex</pattern>
</encoder>
</appender>5.4 ThresholdFilter(阈值过滤)
配置示例:
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 只记录 WARN 及以上级别 -->
<level>WARN</level>
</filter>行为:
- 高于或等于阈值:返回
NEUTRAL - 低于阈值:返回
DENY
5.5 EvaluatorFilter(条件过滤)
基于表达式的强大过滤器。
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<!-- 只记录包含 "重要" 关键字的日志 -->
<expression>message.contains("重要")</expression>
</evaluator>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>更复杂的示例(过滤敏感信息):
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
<expression>
return message.contains("password") ||
message.contains("token") ||
message.contains("secret");
</expression>
</evaluator>
<onMatch>DENY</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter>5.6 自定义 Filter
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
public class BusinessFilter extends Filter<ILoggingEvent> {
private String keyword;
@Override
public FilterReply decide(ILoggingEvent event) {
if (event.getMessage().contains(keyword)) {
return FilterReply.ACCEPT;
}
return FilterReply.DENY;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
}XML 配置:
<filter class="com.example.BusinessFilter">
<keyword>订单</keyword>
</filter>六、Logger 配置
6.1 Logger 层级
Logger 有层级关系,类似 Java 包结构:
root
├─ com
│ └─ Glowxq
│ └─ base
│ ├─ service
│ │ └─ UserService
│ └─ controller
│ └─ UserController
└─ org
└─ springframework6.2 Logger 继承
子 Logger 会继承父 Logger 的配置(Level 和 Appender)。
<configuration>
<!-- 根 Logger -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
<!-- com.Glowxq 包:DEBUG 级别 -->
<logger name="com.Glowxq" level="DEBUG"/>
<!-- com.Glowxq.base.service 包:TRACE 级别,不继承父 Appender -->
<logger name="com.Glowxq.base.service" level="TRACE" additivity="false">
<appender-ref ref="FILE"/>
</logger>
</configuration>继承示例:
| Logger 名称 | 有效级别 | Appender |
|---|---|---|
root | INFO | CONSOLE |
com.Glowxq | DEBUG | CONSOLE(继承) |
com.Glowxq.base.service | TRACE | FILE(不继承) |
com.Glowxq.base.controller | DEBUG | CONSOLE(继承自 com.Glowxq) |
6.3 additivity 属性
作用: 控制日志事件是否向上传播到父 Logger。
<!-- additivity="true"(默认):日志会传播到父 Logger -->
<logger name="com.Glowxq.service" level="DEBUG" additivity="true">
<appender-ref ref="SERVICE_FILE"/>
</logger>
<!-- additivity="false":日志不会传播到父 Logger -->
<logger name="com.Glowxq.controller" level="DEBUG" additivity="false">
<appender-ref ref="CONTROLLER_FILE"/>
</logger>效果对比:
Logger logger = LoggerFactory.getLogger("com.Glowxq.service.UserService");
logger.info("test");additivity="true" 时:
- 输出到
SERVICE_FILE - 也输出到父 Logger 的 Appender(如 CONSOLE)
additivity="false" 时:
- 只输出到
SERVICE_FILE - 不输出到父 Logger
6.4 关闭第三方库的日志
<!-- 完全关闭 -->
<logger name="org.apache" level="OFF"/>
<logger name="org.springframework" level="OFF"/>
<!-- 只输出错误 -->
<logger name="com.zaxxer.hikari" level="ERROR"/>
<logger name="io.netty" level="ERROR"/>
<!-- 调试 SQL -->
<logger name="org.hibernate.SQL" level="DEBUG"/>
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>七、MDC 上下文
7.1 什么是 MDC?
MDC(Mapped Diagnostic Context) 是线程级别的键值对存储,用于在日志中添加上下文信息。
7.2 基本使用
import org.slf4j.MDC;
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
public void createOrder(String orderId, String userId) {
// 设置 MDC
MDC.put("orderId", orderId);
MDC.put("userId", userId);
try {
logger.info("开始创建订单");
// ... 业务逻辑 ...
logger.info("订单创建成功");
} finally {
// 清理 MDC(避免内存泄漏)
MDC.clear();
}
}
}配置:
<pattern>%d [%X{orderId}] [%X{userId}] %p - %m%n</pattern>输出:
2025-12-08 14:30:25.123 [order_123] [user_456] INFO - 开始创建订单
2025-12-08 14:30:25.456 [order_123] [user_456] INFO - 订单创建成功7.3 Web 应用中的 MDC
Filter 实现:
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
public class MDCFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
try {
// 生成 TraceId
String traceId = UUID.randomUUID().toString().replace("-", "");
MDC.put("traceId", traceId);
// 从请求头获取 UserId
String userId = httpRequest.getHeader("X-User-Id");
if (userId != null) {
MDC.put("userId", userId);
}
// 记录请求信息
MDC.put("requestUri", httpRequest.getRequestURI());
MDC.put("method", httpRequest.getMethod());
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}Spring Boot 配置:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MDCFilter> mdcFilter() {
FilterRegistrationBean<MDCFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new MDCFilter());
registration.addUrlPatterns("/*");
registration.setOrder(1);
return registration;
}
}7.4 多线程环境中的 MDC
问题: MDC 是 ThreadLocal,子线程不会自动继承父线程的 MDC。
解决方案:
import java.util.Map;
import java.util.concurrent.Callable;
public class MDCCallable<T> implements Callable<T> {
private final Callable<T> callable;
private final Map<String, String> contextMap;
public MDCCallable(Callable<T> callable) {
this.callable = callable;
// 保存当前线程的 MDC
this.contextMap = MDC.getCopyOfContextMap();
}
@Override
public T call() throws Exception {
// 恢复 MDC
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
try {
return callable.call();
} finally {
MDC.clear();
}
}
}线程池配置:
@Configuration
public class AsyncConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
// 设置 MDC 装饰器
executor.setTaskDecorator(runnable -> {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
runnable.run();
} finally {
MDC.clear();
}
};
});
executor.initialize();
return executor;
}
}八、高级特性
8.1 条件配置
根据不同环境使用不同配置:
<configuration>
<!-- 从 Spring Boot 读取环境变量 -->
<springProperty scope="context" name="app.env" source="spring.profiles.active"/>
<!-- 开发环境:控制台 + DEBUG -->
<if condition='property("app.env").equals("dev")'>
<then>
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
</then>
</if>
<!-- 生产环境:文件 + INFO -->
<if condition='property("app.env").equals("prod")'>
<then>
<root level="INFO">
<appender-ref ref="ROLLING_FILE"/>
</root>
</then>
</if>
</configuration>8.2 变量定义
<configuration>
<!-- 定义变量 -->
<property name="LOG_HOME" value="/var/logs/myapp"/>
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 使用变量 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${LOG_HOME}/application.log</file>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 从系统属性读取 -->
<property name="LOG_LEVEL" value="${log.level:-INFO}"/>
<root level="${LOG_LEVEL}">
<appender-ref ref="FILE"/>
</root>
</configuration>启动参数:
java -Dlog.level=DEBUG -jar app.jar8.3 包含外部配置
<configuration>
<!-- 包含其他配置文件 -->
<include resource="logback-appenders.xml"/>
<include resource="logback-loggers.xml"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>8.4 自动扫描配置变更
<configuration scan="true" scanPeriod="30 seconds">
<!-- 配置文件变更后 30 秒自动重载 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
...
</appender>
</configuration>8.5 JMX 管理
<configuration>
<!-- 启用 JMX -->
<jmxConfigurator/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
...
</appender>
</configuration>使用 JConsole 连接后可以动态修改:
- Logger 级别
- Appender 状态
- 查看日志统计
九、性能优化
9.1 使用异步 Appender
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE"/>
<queueSize>512</queueSize>
<discardingThreshold>0</discardingThreshold>
<neverBlock>false</neverBlock>
</appender>9.2 避免使用位置信息
❌ 避免:
<pattern>%C.%M:%L - %m%n</pattern>✅ 推荐:
<pattern>%logger{36} - %m%n</pattern>9.3 使用参数化日志
❌ 避免字符串拼接:
logger.debug("User " + userId + " login at " + new Date());✅ 推荐参数化:
logger.debug("User {} login at {}", userId, new Date());9.4 条件日志
// 避免不必要的对象创建
if (logger.isDebugEnabled()) {
logger.debug("Complex message: {}", createComplexObject());
}9.5 合理的日志级别
<!-- 生产环境 -->
<root level="INFO"/>
<!-- 关键业务 DEBUG -->
<logger name="com.Glowxq.core" level="DEBUG"/>
<!-- 第三方库只记录错误 -->
<logger name="org.apache" level="ERROR"/>
<logger name="org.springframework" level="WARN"/>9.6 限制日志大小
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy>十、最佳实践
10.1 生产环境完整配置模板
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">
<!-- ========== 变量定义 ========== -->
<property name="APP_NAME" value="Glowxq-base"/>
<property name="LOG_HOME" value="${LOG_PATH:-./logs}"/>
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId:-}] %5p [%t] %-40.40logger{39} : %m%n"/>
<!-- ========== 控制台输出 ========== -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%cyan(%d{HH:mm:ss.SSS}) %highlight(%-5level) %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- ========== INFO 日志文件 ========== -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${APP_NAME}-info.log</file>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${APP_NAME}-info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- ========== ERROR 日志文件 ========== -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${APP_NAME}-error.log</file>
<encoder>
<pattern>${LOG_PATTERN}%ex{full}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${APP_NAME}-error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>90</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- ========== 异步包装 ========== -->
<appender name="ASYNC_INFO" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="INFO_FILE"/>
<queueSize>512</queueSize>
<discardingThreshold>0</discardingThreshold>
</appender>
<appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="ERROR_FILE"/>
<queueSize>256</queueSize>
<discardingThreshold>0</discardingThreshold>
</appender>
<!-- ========== Logger 配置 ========== -->
<logger name="com.Glowxq" level="DEBUG"/>
<!-- 关闭第三方库的日志 -->
<logger name="org.apache" level="WARN"/>
<logger name="org.springframework" level="WARN"/>
<logger name="com.zaxxer.hikari" level="WARN"/>
<logger name="io.netty" level="WARN"/>
<!-- ========== Root Logger ========== -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_INFO"/>
<appender-ref ref="ASYNC_ERROR"/>
</root>
</configuration>10.2 微服务分布式追踪
<configuration>
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId:-N/A}] [%X{spanId:-N/A}] [%X{userId:-}] %5p [%t] %logger{36} : %m%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>Filter 实现:
@Component
public class TraceFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
// 从请求头获取或生成 TraceId
String traceId = httpRequest.getHeader("X-Trace-Id");
if (traceId == null) {
traceId = UUID.randomUUID().toString().replace("-", "");
}
MDC.put("traceId", traceId);
// SpanId
String spanId = UUID.randomUUID().toString().substring(0, 8);
MDC.put("spanId", spanId);
// 传递 TraceId 到下游服务
((HttpServletResponse) response).setHeader("X-Trace-Id", traceId);
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}10.3 日志脱敏
<conversionRule conversionWord="mask"
converterClass="com.Glowxq.log.MaskingConverter"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d %p %c - %mask(%m)%n</pattern>
</encoder>
</appender>Converter 实现:
public class MaskingConverter extends ClassicConverter {
private static final Pattern PHONE_PATTERN = Pattern.compile("(\\d{3})\\d{4}(\\d{4})");
private static final Pattern ID_CARD_PATTERN = Pattern.compile("(\\d{6})\\d{8}(\\d{4})");
@Override
public String convert(ILoggingEvent event) {
String message = event.getFormattedMessage();
// 手机号脱敏
message = PHONE_PATTERN.matcher(message).replaceAll("$1****$2");
// 身份证脱敏
message = ID_CARD_PATTERN.matcher(message).replaceAll("$1********$2");
return message;
}
}十一、故障排查
11.1 开启 Logback 调试
<configuration debug="true">
<!-- 会打印配置解析过程 -->
</configuration>或者启动参数:
java -Dlogback.debug=true -jar app.jar11.2 常见问题
问题1:日志不输出
原因:
- Logger 级别设置过高
- Filter 拒绝了日志
- Appender 配置错误
排查:
<configuration debug="true">
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
...
</configuration>问题2:日志重复输出
原因: additivity="true" 导致日志传播到父 Logger
解决:
<logger name="com.Glowxq" level="DEBUG" additivity="false">
<appender-ref ref="FILE"/>
</logger>问题3:日志文件不滚动
检查:
fileNamePattern是否包含时间/索引占位符- 文件路径是否有写权限
- 查看 Logback 内部状态
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);问题4:中文乱码
解决:
<encoder>
<pattern>%msg%n</pattern>
<charset>UTF-8</charset>
</encoder>问题5:性能问题
检查:
- 是否使用了
%C、%F、%L、%M - 是否使用异步 Appender
- 日志级别是否合理
11.3 监控日志系统
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.Appender;
import org.slf4j.LoggerFactory;
@Component
public class LogbackMonitor {
@Scheduled(fixedRate = 60000)
public void checkLogback() {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
// 检查 Appender 状态
context.getLoggerList().forEach(logger -> {
Iterator<Appender> it = logger.iteratorForAppenders();
while (it.hasNext()) {
Appender appender = it.next();
if (!appender.isStarted()) {
// 告警:Appender 未启动
System.err.println("Appender " + appender.getName() + " is not started!");
}
}
});
}
}附录
A. 配置文件 Schema
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback
https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">
...
</configuration>B. Logback vs Log4j2
| 特性 | Logback | Log4j2 |
|---|---|---|
| 性能 | 高 | 极高 |
| 异步 | AsyncAppender | 原生异步 |
| 无锁 | 否 | 是 |
| Lambda | 不支持 | 支持 |
| 配置 | XML | XML/JSON/YAML |
| 社区 | 成熟 | 活跃 |
C. 参考资源
- 官方文档: https://logback.qos.ch/
- GitHub: https://github.com/qos-ch/logback
- SLF4J: https://www.slf4j.org/
- Stack Overflow: https://stackoverflow.com/questions/tagged/logback
文档版本: 1.0
适用版本: Logback 1.2.x - 1.5.x
最后更新: 2025-12-08
作者: Glowxq 技术团队