slf4j
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSLF4J - Quick Reference
SLF4J - 快速参考
When to Use This Skill
何时使用此技能
- Standard logging API for Java
- Integration with Logback, Log4j2
- Java logging best practices
Deep Knowledge: Usewith technology:mcp__documentation__fetch_docsfor comprehensive documentation.slf4j
- Java 标准日志 API
- 与 Logback、Log4j2 集成
- Java 日志最佳实践
深度知识:调用并指定技术栈为mcp__documentation__fetch_docs可获取完整文档。slf4j
Essential Patterns
核心使用范式
Logger Declaration
声明 Logger
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
// Or with Lombok
// @Slf4j on class
}java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
// 配合 Lombok 可以简化为在类上标注 @Slf4j
}Logging Levels
日志级别
java
log.trace("Detailed debug info: {}", details);
log.debug("Debug info for development");
log.info("Normal operation: user {} logged in", userId);
log.warn("Potential problem: {} retries remaining", retries);
log.error("Error occurred: {}", message, exception);java
log.trace("Detailed debug info: {}", details);
log.debug("Debug info for development");
log.info("Normal operation: user {} logged in", userId);
log.warn("Potential problem: {} retries remaining", retries);
log.error("Error occurred: {}", message, exception);Parameterized Logging
参数化日志
java
// Good - lazy evaluation
log.debug("Processing order {} for user {}", orderId, userId);
// Avoid - always evaluates
log.debug("Processing order " + orderId + " for user " + userId);
// Multiple parameters
log.info("User {} performed {} on resource {}", userId, action, resourceId);java
// 推荐写法 - 懒加载计算
log.debug("Processing order {} for user {}", orderId, userId);
// 不推荐写法 - 会立即执行字符串拼接
log.debug("Processing order " + orderId + " for user " + userId);
// 多参数示例
log.info("User {} performed {} on resource {}", userId, action, resourceId);Exception Logging
异常日志记录
java
try {
processOrder(order);
} catch (OrderException e) {
// Exception is always last parameter
log.error("Failed to process order {}: {}", order.getId(), e.getMessage(), e);
}java
try {
processOrder(order);
} catch (OrderException e) {
// 异常对象必须放在最后一个参数位置
log.error("Failed to process order {}: {}", order.getId(), e.getMessage(), e);
}MDC (Mapped Diagnostic Context)
MDC(映射诊断上下文)
java
import org.slf4j.MDC;
// Set context
MDC.put("requestId", requestId);
MDC.put("userId", userId);
try {
// All logs in this scope will include requestId and userId
log.info("Processing request");
} finally {
MDC.clear();
}java
import org.slf4j.MDC;
// 设置上下文信息
MDC.put("requestId", requestId);
MDC.put("userId", userId);
try {
// 该作用域内的所有日志都会自动携带 requestId 和 userId
log.info("Processing request");
} finally {
MDC.clear();
}Conditional Logging
条件日志
java
if (log.isDebugEnabled()) {
log.debug("Expensive computation result: {}", expensiveMethod());
}
// Better with lambdas (SLF4J 2.0+)
log.atDebug().log(() -> "Result: " + expensiveMethod());java
if (log.isDebugEnabled()) {
log.debug("Expensive computation result: {}", expensiveMethod());
}
// SLF4J 2.0+ 更优雅的 Lambda 写法
log.atDebug().log(() -> "Result: " + expensiveMethod());Fluent API (SLF4J 2.0+)
流式 API(SLF4J 2.0+)
java
log.atInfo()
.addKeyValue("orderId", orderId)
.addKeyValue("amount", amount)
.log("Order processed successfully");java
log.atInfo()
.addKeyValue("orderId", orderId)
.addKeyValue("amount", amount)
.log("Order processed successfully");When NOT to Use This Skill
何时不使用此技能
- Logging framework configuration: Use or Log4j2 skills for XML/config
logback - Performance tuning: Configuration-level optimization is in implementation skills
- Transport/appender setup: That's implementation-specific (Logback/Log4j2)
- Non-Java projects: Use language-appropriate logging APIs
- Direct implementation usage: Always code to SLF4J API, not Logback/Log4j2 directly
- 日志框架配置:Logback/Log4j2 的 XML/配置文件修改请使用对应的专项技能
- 性能调优:配置层面的优化属于日志实现框架的能力范畴
- 传输/追加器配置:该部分是 Logback/Log4j2 等实现框架的独有功能
- 非 Java 项目:请使用对应语言适配的日志 API
- 直接调用实现类:编码时应当始终面向 SLF4J API 开发,不要直接依赖 Logback/Log4j2 的实现类
Anti-Patterns
反模式
| Anti-Pattern | Why It's Bad | Solution |
|---|---|---|
| String concatenation in logs | Always evaluated, performance hit | Use parameterized logging: |
| Logging without level check for expensive ops | Wastes CPU even when disabled | Use |
| Catching exceptions without logging | Silent failures, hard to debug | Always log with |
| Not using MDC for request context | Loses correlation across logs | Use |
| Using wrong log level | Too much noise or missing issues | Follow conventions: ERROR=requires action, WARN=potential issue |
| Logging sensitive data | Security/compliance violation | Mask PII, passwords, tokens before logging |
| 反模式 | 弊端 | 解决方案 |
|---|---|---|
| 日志中使用字符串拼接 | 会立即执行拼接逻辑,带来性能损耗 | 使用参数化日志: |
| 高开销日志操作未加级别判断 | 即使对应日志级别关闭也会浪费 CPU 资源 | 调用高开销方法前先判断 |
| 捕获异常但不记录日志 | 导致故障静默发生,难以排查 | 始终使用 |
| 未使用 MDC 记录请求上下文 | 丢失日志间的关联关系,排查问题时无法串链路 | 搭配 try-finally 使用 |
| 日志级别使用错误 | 要么日志冗余太多,要么关键信息丢失 | 遵循级别约定:ERROR=需要人工介入处理,WARN=潜在异常风险 |
| 记录敏感数据 | 违反安全/合规要求 | 日志输出前对个人身份信息、密码、令牌等敏感内容做掩码处理 |
Quick Troubleshooting
快速故障排查
| Issue | Cause | Solution |
|---|---|---|
| NoClassDefFoundError: StaticLoggerBinder | No SLF4J implementation on classpath | Add Logback or Log4j2 dependency |
| Multiple bindings warning | Multiple SLF4J implementations | Keep only one: Logback OR Log4j2 |
| Logs not appearing | Wrong log level | Check implementation config (logback.xml) |
| Parameters not replaced | Wrong placeholder syntax | Use |
| MDC values empty | MDC cleared or wrong thread | Ensure MDC.put() before logging, clear in finally |
| Exception stack trace missing | Exception not last parameter | Put exception as last param: |
| 问题 | 原因 | 解决方案 |
|---|---|---|
| NoClassDefFoundError: StaticLoggerBinder | 类路径下没有 SLF4J 实现依赖 | 添加 Logback 或 Log4j2 依赖 |
| 多实现绑定警告 | 类路径下存在多个 SLF4J 实现依赖 | 仅保留一个实现:Logback 或 Log4j2 |
| 日志没有输出 | 日志级别配置错误 | 检查日志实现的配置文件(如 logback.xml) |
| 参数占位符没有被替换 | 占位符语法错误 | 使用 |
| MDC 值为空 | MDC 被提前清空或者日志打印在其他线程 | 确保打日志前已经执行 |
| 异常堆栈信息缺失 | 异常对象没有放在参数最后一位 | 将异常对象作为最后一个参数传入: |