SLF4J推荐使用{}占位符输出日志,避免字符串拼接开销且仅在日志级别启用时解析参数;异常必须作为最后一个参数传入才能打印堆栈;参数为null时输出“null”不抛NPE;耗时操作应通过Supplier延迟执行。

Java 项目中推荐使用 SLF4J 作为日志门面,配合占位符({})方式输出日志,既简洁又高效——它避免了字符串拼接的开销,也规避了未启用日志级别时的冗余计算。
用 SLF4J 的占位符语法写日志
SLF4J 支持 {} 占位符,底层会按需格式化,只有当前日志级别开启时才真正解析参数:
- 正确写法:
logger.debug("User {} logged in from IP {}", username, ipAddr); - 错误写法:
logger.debug("User " + username + " logged in from IP " + ipAddr);(提前拼接,浪费性能) - 支持多个占位符,顺序一一对应;若参数为
null,会输出字符串"null",不会报错
处理异常时保留堆栈信息
记录异常不能只靠占位符,必须把 Throwable 作为最后一个参数传入,SLF4J 才能自动打印完整堆栈:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- ✅ 正确:
logger.error("Failed to process order {}", orderId, ex); - ❌ 错误:
logger.error("Failed to process order " + orderId + ", " + ex);(丢失堆栈、且提前拼接) - 注意:异常对象必须放在参数列表末尾,且不能包裹在占位符里
避免隐式 toString() 带来的性能隐患
有些对象重写了 toString(),但内部可能触发数据库查询或复杂计算。SLF4J 占位符虽延迟执行,但只要参数被传入,其构造/获取过程仍会发生:
立即学习“Java免费学习笔记(深入)”;
- 风险示例:
logger.debug("Order detail: {}", order.getDetail());(getDetail()可能很重) - 安全做法:用 lambda 包裹耗时逻辑(SLF4J 2.0+ 支持,或搭配 Log4j2 / Logback 的
logger.debug("...", () -> heavyOperation())) - 或手动加日志级别判断:
if (logger.isDebugEnabled()) { logger.debug("...", heavyOperation()); }
确保绑定正确的日志实现
SLF4J 是门面,需搭配具体实现(如 Logback、Log4j2)。Maven 中只引入一个 binding,避免冲突:
- 推荐 Logback(原生支持 SLF4J,无需额外适配器):
ch.qos.logback:logback-classic - 若用 Log4j2,需引入
org.apache.logging.log4j:log4j-slf4j-impl(注意版本匹配) - 检查 classpath 下是否混入多个
slf4j-xxx.jar,否则启动时会警告“SLF4J multiple bindings”

















