一、核心概念
1.1 为什么需要日志?
日志是应用程序运行时的“黑匣子”,用于:
- 调试:定位问题、追踪执行流程。
- 监控:分析系统行为、性能瓶颈。
- 审计:记录关键操作(如登录、支付)。
- 告警:发现异常并通知运维。
1.2 Spring Boot 默认日志框架:Logback
- Spring Boot 默认集成 SLF4J + Logback。
- SLF4J(Simple Logging Facade for Java):日志门面,提供统一 API。
- Logback:SLF4J 的原生实现,性能高、功能强大。
1.3 可选日志框架:Log4j2
- Log4j2:Apache 开发,性能优于 Logback(尤其在异步日志场景)。
- 支持更丰富的插件和功能(如自动重新加载配置)。
- 需要排除默认的 Logback 依赖。
1.4 日志级别(Level)
日志按严重程度分级,级别从高到低:
级别 | 说明 | 使用场景 |
---|---|---|
OFF |
关闭所有日志 | 通常不用 |
ERROR |
错误,程序无法继续运行 | 异常捕获、系统故障 |
WARN |
警告,潜在问题 | 业务逻辑异常、降级处理 |
INFO |
信息,关键业务流程 | 启动信息、重要操作 |
DEBUG |
调试,详细执行信息 | 开发调试、问题排查 |
TRACE |
追踪,比 DEBUG 更详细 | 深度调试 |
规则:如果当前日志级别设置为
INFO
,则DEBUG
和TRACE
日志不会输出。
二、详细操作步骤(适合快速实践)
步骤 1:项目准备
创建标准 Spring Boot 项目,确保包含 spring-boot-starter-web
(它依赖 spring-boot-starter-logging
)。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 其他依赖 -->
</dependencies>
步骤 2:使用默认 Logback(无需额外依赖)
Spring Boot 已内置 Logback,可直接使用。
2.1 在代码中使用日志
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
@RestController
public class UserController {
// 获取日志记录器(通常以类名为参数)
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {
logger.debug("Fetching user with id: {}", id); // 使用占位符 {}
if (id <= 0) {
logger.warn("Invalid user id: {}", id);
return "Invalid ID";
}
try {
// 模拟业务逻辑
String user = findUserById(id);
logger.info("User fetched successfully: {}", user);
return user;
} catch (Exception e) {
logger.error("Failed to fetch user with id: {}", id, e); // 记录异常堆栈
return "Error";
}
}
private String findUserById(Long id) {
if (id == 1L) return "Alice";
throw new RuntimeException("User not found");
}
}
2.2 配置日志级别(通过 application.yml
)
# application.yml
logging:
level:
root: INFO # 根日志级别
com.example: DEBUG # 指定包的日志级别
org.springframework: WARN # 降低 Spring 框架日志级别
org.hibernate: ERROR # 仅记录 Hibernate 错误
# 文件输出配置
file:
name: logs/app.log # 日志文件路径(相对或绝对)
# max-size: 10MB # 单个文件最大大小(需 logback-spring.xml 配置)
# max-history: 7 # 保留天数
# 控制台输出格式
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} - %5p %c{1}:%L - %m%n"
# %d: 日期, %5p: 级别(5字符宽), %c{1}: 类名(简写), %L: 行号, %m: 消息, %n: 换行
2.3 使用 logback-spring.xml
进行高级配置
在 src/main/resources/
下创建 logback-spring.xml
(推荐使用 -spring
后缀以支持 Spring 扩展)。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义变量 -->
<property name="LOG_PATH" value="logs"/>
<property name="APP_NAME" value="myapp"/>
<!-- 彩色控制台输出(Spring Boot 提供) -->
<springProfile name="dev,test">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
</pattern>
</encoder>
</appender>
</springProfile>
<!-- 生产环境控制台输出(无颜色) -->
<springProfile name="prod">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
</springProfile>
<!-- 按天滚动的文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APP_NAME}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天生成一个文件,保留30天 -->
<fileNamePattern>${LOG_PATH}/archive/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{ISO8601} [%thread] %-5level %logger{50} - %msg%n%ex{10}</pattern>
</encoder>
</appender>
<!-- 异常日志单独输出(可选) -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APP_NAME}-error.log</file>
<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>${LOG_PATH}/archive/${APP_NAME}-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{ISO8601} [%thread] %-5level %logger{50} - %msg%n%ex</pattern>
</encoder>
</appender>
<!-- 根日志器 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
<!-- 特定包的日志器 -->
<logger name="com.example.service" level="DEBUG" additivity="false">
<appender-ref ref="FILE"/>
<!-- 不继承根日志器的 appender -->
</logger>
</configuration>
步骤 3:切换到 Log4j2
3.1 排除默认日志依赖并引入 Log4j2
<dependencies>
<!-- 排除 spring-boot-starter-web 中的 logging starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入 Log4j2 starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</dependencies>
3.2 创建 log4j2-spring.xml
配置文件
在 src/main/resources/
下创建 log4j2-spring.xml
。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Properties>
<Property name="LOG_PATH">logs</Property>
<Property name="APP_NAME">myapp</Property>
</Properties>
<Appenders>
<!-- 控制台输出 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- 按天滚动的文件输出 -->
<RollingFile name="FileAppender" fileName="${LOG_PATH}/${APP_NAME}.log"
filePattern="${LOG_PATH}/archive/${APP_NAME}-%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<Pattern>%d{ISO8601} [%thread] %-5level %logger{50} - %msg%n%ex{10}</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!-- 错误日志单独输出 -->
<RollingFile name="ErrorFileAppender" fileName="${LOG_PATH}/${APP_NAME}-error.log"
filePattern="${LOG_PATH}/archive/${APP_NAME}-error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout>
<Pattern>%d{ISO8601} [%thread] %-5level %logger{50} - %msg%n%ex</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
</Appenders>
<Loggers>
<!-- 根日志器 -->
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
<AppenderRef ref="ErrorFileAppender"/>
</Root>
<!-- 特定包 -->
<Logger name="com.example.service" level="debug" additivity="false">
<AppenderRef ref="FileAppender"/>
</Logger>
</Loggers>
</Configuration>
3.3 在 application.yml
中配置(可选)
# application.yml - Log4j2 配置(部分可通过 YAML 设置)
logging:
config: classpath:log4j2-spring.xml # 显式指定配置文件
level:
root: INFO
com.example: DEBUG
三、常见错误与解决方案
错误现象 | 原因分析 | 解决方案 |
---|---|---|
No appenders could be found for logger |
配置文件未找到或命名错误 | 检查 logback-spring.xml 或 log4j2-spring.xml 是否在 resources 目录 |
日志级别不生效 | 配置未加载或级别设置错误 | 检查 application.yml 或 XML 配置中的 level 设置 |
文件未生成 | 路径权限问题或路径错误 | 确认日志目录有写权限;使用绝对路径测试 |
异步日志不生效 | 未正确配置异步 Appender | Logback:使用 AsyncAppender ;Log4j2:确保 AsyncLogger 或 AsyncAppender 配置正确 |
ClassNotFoundException: ch.qos.logback.classic.Logger |
依赖冲突 | 确保已排除 spring-boot-starter-logging 当使用 Log4j2 时 |
四、注意事项
- 配置文件命名:
- Logback:
logback-spring.xml
(推荐)或logback.xml
。 - Log4j2:
log4j2-spring.xml
(推荐)或log4j2.xml
。 - 使用
-spring
后缀可支持<springProfile>
等 Spring 扩展。
- Logback:
- 日志级别设置:
- 生产环境避免
DEBUG
/TRACE
,防止日志爆炸。 - 关键业务包可单独设置级别。
- 生产环境避免
- 性能影响:
- 字符串拼接:使用
logger.debug("Value: " + expensiveMethod());
会始终执行expensiveMethod()
。 - 正确方式:
logger.debug("Value: {}", expensiveMethod());
(仅在需要时计算)。
- 字符串拼接:使用
- 异常日志:
- 记录异常时,传入
Throwable
对象以输出完整堆栈:logger.error("Message", exception);
。
- 记录异常时,传入
- 多环境配置:
- 使用
<springProfile name="dev">
在 XML 中区分环境。
- 使用
五、使用技巧
5.1 动态修改日志级别(生产环境神器)
使用 Spring Boot Actuator:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# application.yml
management:
endpoints:
web:
exposure:
include: loggers # 暴露 loggers 端点
查看日志级别:
GET http://localhost:8080/actuator/loggers GET http://localhost:8080/actuator/loggers/com.example.service
修改日志级别:
POST http://localhost:8080/actuator/loggers/com.example.service Content-Type: application/json { "configuredLevel": "DEBUG" }
5.2 使用 MDC(Mapped Diagnostic Context)
在日志中添加上下文信息(如请求 ID、用户 ID)。
// 在拦截器或 Filter 中
import org.slf4j.MDC;
MDC.put("userId", "123");
MDC.put("requestId", UUID.randomUUID().toString());
// 在日志 pattern 中使用
// Logback pattern: %X{userId} %X{requestId}
// 输出: [userId=123] [requestId=abc-123] ...
// 清理
MDC.clear();
5.3 异步日志(提升性能)
Logback 异步配置
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE"/>
<queueSize>1000</queueSize>
<discardingThreshold>0</discardingThreshold> <!-- 0 表示不丢弃 ERROR 日志 -->
</appender>
<root level="INFO">
<appender-ref ref="ASYNC_FILE"/>
</root>
Log4j2 异步配置(推荐)
- 全局异步:在
log4j2.component.properties
中设置Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
。 - 混合模式:部分 Logger 异步。
5.4 日志归档与清理
- 配置
maxHistory
和totalSizeCap
(Logback)或DefaultRolloverStrategy
(Log4j2)自动清理旧日志。 - 使用外部脚本(如 Linux
logrotate
)进行更复杂的归档策略。
六、最佳实践
- 统一日志格式:
- 团队内约定日志格式,便于解析和分析。
- 关键信息结构化:
- 使用 JSON 格式输出日志,便于 ELK 等系统解析。
<PatternLayout pattern='{"time":"%d{ISO8601}","level":"%p","logger":"%c","message":"%m","exception":"%ex"}%n'/>
- 避免日志泄露敏感信息:
- 不要记录密码、密钥、完整身份证号等。
- 合理使用日志级别:
INFO
记录关键业务;DEBUG
用于开发;ERROR
必须包含可行动信息。
- 监控日志文件大小:
- 防止磁盘被占满。
- 使用集中式日志系统:
- 将日志发送到 ELK(Elasticsearch, Logstash, Kibana)或 Splunk。
七、性能优化
优化点 | Logback | Log4j2 |
---|---|---|
同步日志 | 性能良好 | 性能优秀 |
异步日志 | AsyncAppender ,性能提升明显 |
AsyncLogger ,性能极佳(LMAX Disruptor) |
字符串拼接 | 使用 {} 占位符避免不必要的字符串创建 |
同左 |
I/O 性能 | 合理设置 maxFileSize 和滚动策略 |
同左,Log4j2 在高并发下 I/O 更稳定 |
总体建议 | 对于一般应用足够 | 对于高吞吐、低延迟场景优先选择 |
结论:对于大多数应用,Logback 性能已足够。若追求极致性能或需要 Log4j2 特有功能,可切换。
总结
Spring Boot 日志管理是应用可观测性的基石。
- 默认选择:Logback +
logback-spring.xml
,简单高效。 - 高性能选择:Log4j2,在高并发场景下表现更优。
- 关键技巧:使用占位符、MDC、异步日志、Actuator 动态调级。
- 最佳实践:结构化日志、避免敏感信息、集中管理。
快速上手建议:
- 使用默认 Logback。
- 配置
logback-spring.xml
实现文件滚动。 - 通过
application.yml
设置日志级别。 - 引入 Actuator 实现生产环境动态调级。
本文基于 Spring Boot 3.x 编写。