核心是延迟日志参数计算与降低格式化开销:优先用{}占位符触发条件执行;重参数用supplier延迟求值;精简log_format,避免高成本字段;导出日志时用大缓冲+批量flush+stringbuilder预拼接。

核心是让字符串拼接“只在真要输出时才发生”,而不是每次调用日志方法就无条件执行。关键不在禁用日志,而在控制计算时机和降低格式化负担。
用占位符替代 + 拼接
这是最直接、最通用的优化手段。日志框架(如 SLF4J、Log4j2)看到 {} 占位符后,会先检查当前日志级别是否启用;若不满足(比如生产环境 DEBUG 关闭),整个参数表达式(如 user.getName()、new Date())根本不会执行。
- ✅ 推荐:
logger.debug("User {} login at {}", user.getName(), LocalDateTime.now()); - ❌ 避免:
logger.debug("User " + user.getName() + " login at " + LocalDateTime.now());—— 无论是否输出,拼接和对象构造都已发生
高开销参数用 Supplier 延迟求值
当某个参数本身就很重(比如序列化一个大订单、调用一次远程接口、遍历千条记录生成摘要),即使用了占位符,它仍会在日志调用时被立即求值。这时应把它包装成 Supplier,确保只在日志真正要写入时才触发。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- SLF4J 2.0+ / Log4j2 支持:
logger.debug("Order detail: {}", () -> toJson(order)); - 旧版本可用预检代替:
if (logger.isDebugEnabled()) { logger.debug("Order detail: {}", toJson(order)); }
精简 log_format,砍掉运行时高成本字段
日志格式里的某些字段看似只是“加个时间”或“打个行号”,实则每次都要调用函数、解析栈帧、做字符串格式化——在万级 QPS 下,它们就是 CPU 热点。
- 时间戳:用
%(created)f(毫秒浮点数)代替%(asctime)s(需调用strftime) - 方法名/行号:
%(funcName)s和%(lineno)d需遍历调用栈,排查问题时再临时开启即可 - 线程名:
%(threadName)s反射开销大,改用轻量%(thread)d(整数 ID) - 模块名:
%logger{20}截断包路径,避免每次截取+缓存运算
导出类日志慎用 BufferedWriter 小缓冲
如果是批量导出日志文本(如审计日志、离线分析),别依赖默认 8KB 缓冲。小缓冲会导致频繁系统调用,I/O 成瓶颈。
- 显式设置大缓冲区:如
new BufferedWriter(new FileWriter(file, true), 256 * 1024) - 批量写入后统一
flush():例如每写满 1000 行或累计 128KB 再刷盘 - 用
StringBuilder预拼接内容,减少单次write()调用次数和临时字符串对象
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










