SLF4J
SLF4J(Simple Logging Facade for Java)是 Java 日志的门面抽象层,本身不提供日志实现,而是统一了日志调用 API,底层实现可替换为 Logback、Log4j2 等。
Spring Boot 默认使用 SLF4J + Logback 组合,引入 spring-boot-starter 自动生效。日志配置详见 Spring Boot 日志。
核心设计
你的代码(只调用 SLF4J API)
↓
SLF4J 桥接层(slf4j-api)
↓ 运行时自动绑定
具体实现(Logback / Log4j2 / java.util.logging)
代码只依赖 SLF4J 接口,底层实现可随时替换,不影响业务代码。
依赖配置
Spring Boot(自动包含,无需手动添加)
<!-- spring-boot-starter 已包含 SLF4J + Logback -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>切换为 Log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>非 Spring 项目(手动引入)
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>基本使用
创建 Logger
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
}Lombok @Slf4j(推荐)
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class UserService {
public void createUser(String name) {
log.info("Creating user: {}", name);
}
}日志级别
从低到高:TRACE < DEBUG < INFO < WARN < ERROR
设置级别后,低于该级别的日志不输出(设为 INFO 则 TRACE/DEBUG 不输出)。
log.trace("最细粒度,调试循环内部逻辑");
log.debug("调试信息,开发环境输出,生产环境关闭");
log.info("关键业务节点:用户登录、订单创建");
log.warn("潜在问题:配置缺失、重试发生");
log.error("需要处理的错误,通常附带异常");| 级别 | 典型场景 |
|---|---|
| TRACE | 方法进出、循环迭代,生产不开启 |
| DEBUG | SQL 语句、参数值、中间计算结果 |
| INFO | 服务启动/停止、用户操作、定时任务 |
| WARN | 降级逻辑触发、依赖服务慢、配置不完整 |
| ERROR | 未捕获异常、数据库操作失败、外部接口故障 |
参数化日志(重要)
使用 {} 占位符,避免字符串拼接——只有日志真正输出时才格式化:
// 错误:无论级别是否输出,都执行拼接
log.debug("User: " + user.getName() + ", Age: " + user.getAge());
// 正确:懒求值,DEBUG 关闭时不执行拼接
log.debug("User: {}, Age: {}", user.getName(), user.getAge());记录异常时,异常对象放最后一个参数(不加 {}),SLF4J 自动输出完整堆栈:
try {
userRepository.save(user);
} catch (Exception e) {
log.error("Failed to save user: {}", user.getId(), e);
}MDC(Mapped Diagnostic Context)
MDC 是线程局部的 Map,用于在日志中附加请求级上下文(traceId、userId)。在过滤器中设置一次,整个请求链路的日志自动携带。
// 请求进入时设置
MDC.put("traceId", UUID.randomUUID().toString());
MDC.put("userId", String.valueOf(currentUserId));
// 业务代码正常打日志,无需手动传递
log.info("Processing order: {}", orderId);
// 输出:[traceId=abc-123] [userId=42] Processing order: 1001
// 请求结束时清理(防止线程池复用时数据泄露)
MDC.clear();在 logback.xml 中通过 %X{key} 引用 MDC 变量:
<pattern>%d{HH:mm:ss} [%X{traceId}] [%X{userId}] %-5level %logger - %msg%n</pattern>通过 OncePerRequestFilter 统一管理 MDC:
@Component
public class MdcFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws ServletException, IOException {
String traceId = Optional.ofNullable(req.getHeader("X-Trace-Id"))
.orElse(UUID.randomUUID().toString());
MDC.put("traceId", traceId);
try {
chain.doFilter(req, res);
} finally {
MDC.clear();
}
}
}日志桥接(Bridging)
第三方库可能使用 log4j 1.x、commons-logging 等框架,SLF4J 提供桥接器将其调用重定向到 SLF4J 统一处理:
<!-- 将 log4j 1.x 桥接到 SLF4J -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<!-- 将 commons-logging 桥接到 SLF4J -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<!-- 将 java.util.logging 桥接到 SLF4J -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>桥接时需同时排除被桥接框架的原始依赖,避免循环绑定导致 StackOverflow。
SLF4J vs 其他日志框架
| 框架 | 类型 | 说明 |
|---|---|---|
| SLF4J | 门面(API) | 只定义接口,代码中推荐依赖此层 |
| Logback | 实现 | Spring Boot 默认,SLF4J 原生支持 |
| Log4j2 | 实现 | 性能优秀,支持异步日志,适合高吞吐场景 |
| Log4j 1.x | 实现 | 已停止维护,存在安全漏洞,不应使用 |
| java.util.logging | 实现 | JDK 内置,功能弱,一般不用 |
相关链接
- Spring Boot 日志 — Logback 配置、日志级别、文件输出、滚动策略
- 链路追踪 — 结合 MDC 实现分布式 traceId 传递