有天凌晨告警:订单服务报错率突增。我登录 Kibana 翻日志,几千行文本里找那一条异常,正则写到手抖,花了 20 分钟才定位。痛定思痛,把日志从"人看"改成"机器看"——结构化输出,这套改造后来的排障时间从 20 分钟降到 40 秒。
为什么要从文本日志切到结构化
文本日志是给人扫的,机器分析要正则硬抠,字段一变就全废。结构化日志(JSON / ECS)把每行变成带字段的键值对,对接 Loki、Elasticsearch 直接按字段聚合。Spring Boot 3.4 引入的结构化日志让这事儿零依赖:不用再引 Logstash encoder 那一套。
结构化日志输出配置
在 application.yml 里指定格式即可:
logging:
structureded:
format:
console: elasticsearch # 可选 ecs、logstash
level:
root: info
com.acme.order: debug
输出从这样:
2024-05-22 13:59:01.123 INFO [order-svc,abc123] c.a.o.Service - 下单成功 orderId=8891
变成这样:
{"@timestamp":"2024-05-22T13:59:01.123Z","level":"INFO","service":"order-svc","traceId":"abc123","logger":"c.a.o.Service","message":"下单成功","orderId":"8891"}
字段稳定、可索引,按 orderId 一搜就出。
OTLP 集成:日志直接进可观测后端
光输出 JSON 不够,要能传走。我们接了 OpenTelemetry 协议(OTLP),日志、指标、链路统一出口:
otel:
logs:
exporter: otlp
exporter:
otlp:
endpoint: http://otel-collector:4317
protocol: grpc
配合 Micrometer,指标也走 OTLP。以前 Prometheus 拉 + 日志另外推,两套管道;现在统一 OTLP 推给 Collector,再由它分流到 Prometheus(指标)、Loki(日志)、Tempo(链路)。部署复杂度降了一截。
一个收益对比
| 场景 | 文本日志+正则 | 结构化+字段检索 |
|---|---|---|
| 按 orderId 查一条日志 | 约 20 分钟(翻页+正则) | 40 秒 |
| 统计每分钟错误率 | 几乎做不到 | 1 条聚合查询 |
| 字段变更兼容性 | 正则全废 | 新增字段自动索引 |
Actuator 增强:把内部状态亮出来
Spring Boot 3.4 的 Actuator 在 /actuator 下多了些端点,我们重点用三个:
/actuator/metrics:直接看 JVM、HTTP 请求耗时分布,不用额外埋点;/actuator/threaddump:虚拟线程时代线程数暴增,这里能看到平台线程与虚拟线程比例;/actuator/loggers:运行时动态调日志级别,线上排查时把某个包调到 debug,完事调回,不用重启。
我们把 /actuator/loggers 接了内部运维平台,点一下按钮就能给某个服务开 debug 日志,救过不止一次火。
踩坑
- 性能:JSON 序列化比文本慢一截,高 QPS 服务开启后 CPU 涨了约 8%。我们用异步 Appender 削峰,控制在 3% 内;
- 敏感字段:
message里别带手机号、 token,上线前加了脱敏转换器; - 磁盘:结构化日志体积大 30%,日志轮转周期从 7 天缩到 3 天,配合远端存储。
小结
结构化日志 + OTLP 把"排障靠肉眼"升级为"排障靠查询"。Spring Boot 3.4 把它做成开箱即用,配置成本极低。Actuator 的动态日志级别和指标端点,是线上救火的利器。代价是少许 CPU 和存储,换来的是故障定位时间的数量级下降——这笔账怎么算都值。别等半夜被告警叫醒才想起该结构化。