日志系统设计:从 System.out.println 到结构化日志
Logging System Design: From System.out.println to Structured Logging
| David | 2026-08-30T09:43:11
之前项目的日志一团糟,出了问题根本没法查。花了一天时间重新设计了日志方案,从格式规范到集中化收集。
Our project's logging was a mess - impossible to debug with. Redesigned the entire logging approach in one day, from format standards to centralized collection.
之前接手一个项目,打开日志文件一看傻眼了:有用 System.out.println 的,有用 log.info 但不带任何上下文的,有把整个对象 toString 打进去的。出了问题想查日志,根本无从下手。 日志规范 首先定了一套日志规范: 什么时候打日志 必须打:外部调用(HTTP、RPC、MQ)的入参出参、异常 catch 块、关键业务节点(下单、支付、退款) 不要打:循环内部、getter/setter、纯计算逻辑 日志级别怎么选 ERROR:需要立即处理的异常,比如数据库连接失败、支付回调异常 WARN:不影响主流程但需要关注的,比如降级、重试、参数校验失败 INFO:正常业务流转记录,比如"用户 xxx 下单成功,订单号 yyy" DEBUG:开发调试用,生产环境不开 结构化日志 用 JSON 格式输出日志,方便后续 ELK 解析: // logback-spring.xml <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"> <providers> <timestamp/> <logLevel/> <threadName/> <loggerName/> <message/> <mdc/> <stackTrace/> </providers> </encoder> MDC 透传 traceId 在请求入口生成 traceId 放入 MDC,整条链路的日志都能通过这个 ID 关联: @Component public class TraceFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { String traceId = UUID.randomUUID().toString().replace("-", ""); MDC.put("traceId", traceId); try { chain.doFilter(request, response); } finally { MDC.clear(); } } } 效果 改完之后排查问题的效率提升了一个量级。之前一个线上 bug 可能要查一两个小时的日志,现在用 traceId 一搜就能看到完整的调用链路。
Inherited a project with chaotic logging - mix of System.out.println, context-free log.info, and toString dumps. Redesigned the entire approach. Standards Defined when to log (external calls, exceptions, key business events) and log level guidelines (ERROR for immediate action, WARN for degradation, INFO for business flow). Structured Logging JSON format output with logstash-logback-encoder, MDC-based traceId propagation for request chain correlation. Result: Bug investigation time dropped from 1-2 hours to minutes with traceId-based log searching.