Logback 是 Java 生态系统中使用最广泛的日志框架之一,作为 Log4j 的继任者和 SLF4J 的原生实现,它以其卓越的性能、灵活的配置和丰富的功能成为现代 Java 应用的首选日志解决方案。
Logback 简介
什么是 Logback
Logback 由 Log4j 创始人 Ceki Gülcü 开发,设计上分为三个核心模块:
| 模块 | 作用 |
|---|
| logback-core | 基础框架,为其他两个模块提供支持 |
| logback-classic | SLF4J 的原生实现,包含核心日志功能 |
| logback-access | 与 Servlet 容器集成,提供 HTTP 访问日志功能 |
为什么选 Logback
1 2 3 4 5 6 7 8 9 10 11
| 性能优势: - 初始化速度比 Log4j 快约 10 倍 - 内存占用更少 - 异步 Appender 吞吐量更高 - 自动重载配置无需重启应用
功能优势: - 原生支持 SLF4J - 条件化配置(if/then/else) - 自动压缩归档日志 - 更强大的过滤器
|
核心架构
三大核心组件
1 2 3 4 5 6 7 8 9 10 11
| Logger logger = LoggerFactory.getLogger(MyClass.class); logger.info("用户 {} 登录成功", username);
|
日志级别层级
1 2 3 4 5
| ERROR > WARN > INFO > DEBUG > TRACE > ALL ^ | | | +---------------------------------------+ (继承关系)
|
级别设置规则:只输出级别 >= 设定级别的日志
1 2 3 4
| logger.debug("这行不会输出"); logger.info("这行会输出"); logger.error("这行会输出");
|
配置详解
XML 配置(最常用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| <?xml version="1.0" encoding="UTF-8"?> <configuration> <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/> <property name="LOG_DIR" value="logs"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${LOG_PATTERN}</pattern> <charset>UTF-8</charset> </encoder> </appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/application.log</file> <append>true</append> <encoder> <pattern>${LOG_PATTERN}</pattern> </encoder> </appender>
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_DIR}/app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_DIR}/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <maxHistory>30</maxHistory> <totalSizeCap>3GB</totalSizeCap> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>100MB</maxFileSize> </triggeringPolicy> <encoder> <pattern>${LOG_PATTERN}</pattern> </encoder> </appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <queueSize>512</queueSize> <discardingThreshold>0</discardingThreshold> <neverBlock>false</neverBlock> <appender-ref ref="ROLLING_FILE"/> </appender>
<logger name="com.example.dao" level="DEBUG"/> <logger name="org.springframework" level="WARN"/> <logger name="org.hibernate.SQL" level="DEBUG"/>
<root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="ASYNC"/> </root> </configuration>
|
配置自动重载
1 2 3 4
| <configuration scan="true" scanPeriod="60 seconds"> ... </configuration>
|
条件化配置(多环境支持)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <configuration> <if condition='property("env").contains("dev")'> <then> <property name="LOG_LEVEL" value="DEBUG"/> </then> <else> <property name="LOG_LEVEL" value="INFO"/> </else> </if> <root level="${LOG_LEVEL}"> <appender-ref ref="CONSOLE"/> </root> </configuration>
|
日志格式详解
常用 Pattern 符号
| 符号 | 含义 | 示例输出 |
|---|
%d{pattern} | 日期时间 | %d{yyyy-MM-dd HH:mm:ss} → 2024-01-15 14:30:25 |
%level / %le / %p | 日志级别 | INFO, DEBUG, ERROR |
%logger{len} | Logger 名(可缩写) | %logger{36} → c.e.m.MyService |
%msg / %m | 日志消息 | 实际日志内容 |
%thread / %t | 线程名 | main, pool-1-thread-2 |
%class | 调用类名(慢,不推荐生产用) | com.example.MyClass |
%method / %M | 调用方法名(慢) | doSomething |
%line / %L | 行号(慢) | 42 |
%file / %F | 文件名(慢) | MyClass.java |
%ex / %exception | 异常堆栈 | 完整的 stack trace |
%n | 换行符 | 平台相关 |
%X{key} | MDC 变量 | 见下文 MDC 章节 |
%marker | Marker 标记 | 特殊标记,用于过滤 |
颜色输出(IDE/终端友好)
1 2 3 4 5
| <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger{36}) - %msg%n</pattern> </encoder> </appender>
|
颜色说明:
%highlight() - 根据级别自动着色(ERROR=红,WARN=黄,INFO=绿,DEBUG=蓝)%cyan() / %magenta() / %yellow() 等 - 指定颜色
代码中使用
基础用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import org.slf4j.Logger; import org.slf4j.LoggerFactory;
public class UserService { private static final Logger logger = LoggerFactory.getLogger(UserService.class); public void login(String username, String password) { logger.info("用户登录请求"); logger.info("用户 {} 尝试登录", username); logger.debug("用户 {} 从 IP {} 登录,时间 {}", username, clientIp, LocalDateTime.now()); if (logger.isDebugEnabled()) { logger.debug("详细对象信息: {}", expensiveToString()); } } }
|
异常日志
1 2 3 4 5 6 7 8 9 10 11 12
| try { riskyOperation(); } catch (BusinessException e) { logger.error("业务处理失败,订单号: {}", orderNo, e); } catch (Exception e) { logger.error("系统异常", e); }
|
使用 Markers 进行高级过滤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import org.slf4j.Marker; import org.slf4j.MarkerFactory;
public class AuditService { private static final Marker AUDIT_MARKER = MarkerFactory.getMarker("AUDIT"); private static final Marker SECURITY_MARKER = MarkerFactory.getMarker("SECURITY"); static { SECURITY_MARKER.add(AUDIT_MARKER); } public void deleteUser(Long userId) { logger.info(SECURITY_MARKER, "删除用户 {}, 操作人 {}", userId, currentUser); } }
|
配合配置过滤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <appender name="AUDIT_FILE" class="ch.qos.logback.core.FileAppender"> <file>logs/audit.log</file> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator class="ch.qos.logback.classic.boo.JaninoEventEvaluator"> <expression> marker != null && marker.contains("AUDIT") </expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern> </encoder> </appender>
|
MDC(Mapped Diagnostic Context)
MDC 是 Logback 的杀手锏功能,用于在日志中添加上下文信息(如用户ID、请求ID、TraceID 等)。
基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import org.slf4j.MDC;
public class OrderController { private static final Logger logger = LoggerFactory.getLogger(OrderController.class); public void createOrder(CreateOrderRequest request) { MDC.put("userId", request.getUserId()); MDC.put("requestId", UUID.randomUUID().toString()); MDC.put("traceId", request.getTraceId()); try { logger.info("开始创建订单"); orderService.create(request); logger.info("订单创建成功,订单号: {}", orderNo); } finally { MDC.clear(); } } }
|
配合 Pattern
1
| <pattern>%d{HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{userId}] %-5level %logger - %msg%n</pattern>
|
输出示例:
14:30:25.123 [http-nio-8080-exec-1] [abc-123-xyz] [user_9527] INFO c.e.s.OrderService - 开始创建订单
14:30:25.145 [http-nio-8080-exec-1] [abc-123-xyz] [user_9527] DEBUG c.e.s.OrderService - 参数验证通过
14:30:25.200 [http-nio-8080-exec-1] [abc-123-xyz] [user_9527] INFO c.e.s.OrderService - 订单创建成功,订单号: ORDER_20240115_001
Spring Boot 集成(自动填充 MDC)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Component public class MdcInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String traceId = request.getHeader("X-Trace-Id"); if (traceId == null) { traceId = UUID.randomUUID().toString().replace("-", ""); } MDC.put("traceId", traceId); MDC.put("clientIp", getClientIp(request)); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { MDC.clear(); } }
|
高级特性
日志分离(不同业务模块写不同文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <appender name="ORDER_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/order/order.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/order/order.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern> </encoder> </appender>
<appender name="PAY_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/pay/pay.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/pay/pay.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>90</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern> </encoder> </appender>
<logger name="com.example.order" level="INFO" additivity="false"> <appender-ref ref="ORDER_LOG"/> <appender-ref ref="CONSOLE"/> </logger>
<logger name="com.example.pay" level="INFO" additivity="false"> <appender-ref ref="PAY_LOG"/> <appender-ref ref="CONSOLE"/> </logger>
|
注意 additivity="false" 防止日志重复输出到 root。
SMTP 告警(错误邮件通知)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>smtp.company.com</smtpHost> <smtpPort>587</smtpPort> <username>alert@company.com</username> <password>password</password> <to>ops@company.com</to> <from>alert@company.com</from> <subject>%logger{20} - %m</subject> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss} %level %logger - %msg%n%ex</pattern> </layout> </appender>
|
日志脱敏(自定义 Encoder)
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class MaskingPatternEncoder extends PatternLayoutEncoder { private Pattern pattern = Pattern.compile("(phone|idCard|password)=([^&]*)"); private String replacement = "$1=***"; @Override public byte[] encode(ILoggingEvent event) { String formatted = layout.doLayout(event); String masked = pattern.matcher(formatted).replaceAll(replacement); return masked.getBytes(StandardCharsets.UTF_8); } }
|
Spring Boot 集成
依赖
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
|
application.yml 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| logging: level: root: INFO com.example: DEBUG org.springframework.web: WARN pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" file: name: logs/application.log logback: rollingpolicy: max-file-size: 100MB max-history: 30
|
多环境配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| logging: level: root: DEBUG com.example: TRACE
logging: level: root: WARN com.example: INFO pattern: console: "%d{HH:mm:ss} [%X{traceId}] %-5level - %msg%n"
|
性能优化建议
DO(应该做)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| logger.debug("User {} logged in from {}", user, ip);
if (logger.isDebugEnabled()) { logger.debug("Result: {}", heavyComputation()); }
|
DON’T(不要做)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
for (Item item : items) { logger.debug("Processing item: {}", item); }
logger.info("User password: {}", password);
|
常见问题排查
日志不输出
检查清单:
logback.xml 是否在 src/main/resources 目录- 文件名是否正确(区分大小写)
- 日志级别是否设置正确
- 包路径是否匹配
<logger name="...">
日志重复
原因:Logger 的 additivity 默认为 true,日志会向上传播给父 Logger。
1 2 3 4
| <logger name="com.example.dao" level="DEBUG" additivity="false"> <appender-ref ref="DAO_LOG"/> </logger>
|
中文乱码
1 2 3 4
| <encoder> <pattern>...</pattern> <charset>UTF-8</charset> </encoder>
|
动态调整日志级别(运行时)
1 2 3
| LoggerContext ctx = (LoggerContext) LoggerFactory.getILoggerFactory(); ctx.getLogger("com.example").setLevel(Level.DEBUG);
|
总结
| 场景 | 推荐方案 |
|---|
| 本地开发 | ConsoleAppender + 彩色输出 |
| 测试环境 | 文件 + Console,DEBUG 级别 |
| 生产环境 | RollingFileAppender + AsyncAppender,INFO/WARN 级别 |
| 分布式追踪 | MDC 传递 traceId/requestId |
| 审计日志 | Marker + 单独 Appender |
| 告警通知 | SMTPAppender 过滤 ERROR |
Logback 的优势在于其简洁的设计、高性能和灵活性。掌握它的核心概念(Logger、Appender、Layout/Encoder)和 MDC 特性,你就能构建出既易用又强大的日志系统。
参考资源: