java中SLF4J 日志方法详解
SLF4J 日志方法详解:log.debug/info/warn/error 的正确用法
一、核心日志方法:5 大级别与典型场景
方法 | 级别 | 适用场景(生产环境) | 示例代码(@Slf4j) |
log.trace() | TRACE | 极细粒度调试(默认禁用,仅开发用) | log.trace("进入方法,参数:{}", params); |
log.debug() | DEBUG | 开发调试(生产建议关闭) | log.debug("用户ID={}查询开始", userId); |
log.info() | INFO | 业务关键流程(生产必开) | log.info("订单{}创建成功", orderId); |
log.warn() | WARN | 潜在风险(非错误,但需关注) | log.warn("库存不足:剩余{}件", stock); |
log.error() | ERROR | 不可恢复的错误(必须记录堆栈) | log.error("数据库连接失败", e); |
二、各方法深度用法与最佳实践
1. log.debug():开发调试专用
- 场景:记录方法入参、中间变量、SQL 语句等(生产环境建议关闭)。
- 参数化日志(避免性能损耗):
public User getUser(Long userId) { log.debug("查询用户,userId={}", userId); // 仅 DEBUG 开启时执行 User user = userRepository.findById(userId); log.debug("查询结果:{}", user); // 避免直接打印对象(可能触发复杂 toString) return user; } |
- 条件日志(复杂参数场景):
if (log.isDebugEnabled()) { log.debug("调试信息:{}", } |
2. log.info():业务流程记录(生产核心)
- 场景:用户操作、订单状态变更、任务完成等可观测性事件。
- 结构化日志(便于监控):
@PostMapping("/order") public Result createOrder(@RequestBody Order order) { String orderId = orderService.create(order); log.info( "订单创建成功", Map.of("userId", order.getUserId(), "amount", order.getAmount(), "orderId", orderId) ); // 配合 JSON 格式输出,便于检索 return Result.success(orderId); } |
3. log.warn():预警而非错误
- 场景:空指针防御、配置缺失、临时异常(如重试成功的请求)。
- 示例:
public List<Product> search(String keyword) { List<Product> result = productRepository.search(keyword); if (result.isEmpty()) { log.warn("关键词{}无搜索结果,可能数据缺失", keyword); // 非错误,提示潜在问题 } return result; } |
4. log.error():错误堆栈必记
- 场景:捕获异常时记录堆栈,包括业务异常和系统异常。
- 正确用法(携带上下文 + 堆栈):
@GetMapping("/user/{id}") public User getUser(@PathVariable Long id) { try { return userService.getById(id); } catch (UserNotFoundException e) { log.error("用户{}不存在", id, e); // 格式:消息 + 异常堆栈 throw e; // 重新抛出,保持异常链 } catch (Exception e) { log.error("用户查询失败,id={}", id, e); // 未知异常,必须记录堆栈 throw new } } |
5. log.trace():极细粒度调试
- 场景:框架源码调试、性能追踪(如 SQL 执行耗时)。
- 生产建议:默认禁用,通过配置临时开启(如 logback.xml 设为 TRACE)。
log.trace("SQL执行:{},耗时{}ms", sql, duration); // 仅开发/测试环境使用 |
三、通用注意事项:避免日志滥用
- 参数化日志,禁止字符串拼接
- 错误:log.debug("用户ID:" + userId + ",名称:" + username)(无论 DEBUG 级别是否开启,都会执行字符串拼接) 正确:log.debug("用户ID:{},名称:{}", userId, username)(仅当 DEBUG 开启时,才格式化参数)
- 异常堆栈必须记录
- 永远使用 log.error(msg, throwable) 重载方法,而非仅记录消息。 错误:log.error("文件读取失败:" + e.getMessage()) 正确:log.error("文件读取失败", e) (包含完整堆栈)
- 避免循环内高频日志
- 批量操作时,使用 DEBUG 级别并限制频率,或记录总数:
for (int i = 0; i < 1000; i++) { // 错误:1000 条 INFO 日志(生产环境磁盘爆炸) // 正确: if (i % 100 == 0 && log.isDebugEnabled()) { // 每 100 条 DEBUG 一次 log.debug("处理第{}条数据", i); } } log.info("批量处理完成,总计{}条", 1000); // 关键结果 INFO |
- 生产环境禁用 DEBUG/TRACE
- 通过配置文件(如 application.properties)强制设置:
logging.level.root=INFO logging.level.com.example=DEBUG # 仅特定包开启 DEBUG |
四、异常场景:日志方法的特殊用法
- 业务异常 vs 系统异常
- 业务异常(如用户不存在):用 WARN 记录上下文,不记录堆栈(因属预期)。
catch (UserNotFoundException e) { log.warn("用户{}不存在,请求被拒绝", e.getUserId()); // 无需堆栈 } |
- 系统异常(如数据库连接失败):用 ERROR 记录堆栈(需排查)。
catch (SQLException e) { log.error("数据库连接失败:{}", url, e); // 必须堆栈 } |
- MDC 上下文传递在分布式系统中,通过 MDC.put("traceId", id) 记录请求链路:
log.info("订单支付完成", MDC.getCopyOfContextMap()); // 输出包含 traceId 的日志 |
五、总结:日志方法选择四原则
- 级别匹配场景:DEBUG 开发,INFO 业务,WARN 预警,ERROR 故障。
- 参数化优先:永远使用占位符,避免性能损耗。
- 异常必带堆栈:log.error 必须传入异常对象,禁止仅记录消息。
- 生产最小化:禁用 DEBUG/TRACE,避免日志洪水。
合理使用日志方法,能让代码清晰可维护,同时为生产问题排查提供关键线索。结合 @Slf4j 注解和 Logback 配置,可在 Spring Boot 中高效实现日志的 “开发友好” 与 “生产健壮”。