日志、指标、链路追踪、告警与 SLO
前言
可观测性的核心是让团队能够根据日志、指标和链路等外部信号解释系统状态,并在故障时形成证据链。本文说明三类信号的分工、告警与 SLO 的设计边界,以及如何避免高基数、无效告警和链路上下文丢失。
可观测性不是“多打日志”
可观测性让团队能根据外部信号理解系统内部状态,并在故障时建立证据链。日志回答“某一次请求发生了什么”,指标回答“系统整体趋势如何”,链路追踪回答“这次请求跨服务花在哪里”。三者应使用统一的 traceId、服务名、环境和版本关联。
| 信号 | 最适合回答的问题 | 常见陷阱 |
|---|---|---|
| Logs | 某订单为何失败、异常栈是什么 | 日志无结构、敏感信息泄露、无界量导致成本失控 |
| Metrics | P99 是否升高、错误率和队列是否饱和 | 用 userId/orderId 做 label 导致高基数 |
| Traces | 哪个服务/SQL/HTTP 调用耗时 | 全量采样成本高、异步链路丢失上下文 |
| Profiles | CPU/锁/分配热点在哪里 | 只在需要时低开销采样,避免长期高开销探针 |
指标、RED/USE 与四个黄金信号
面向 HTTP/RPC 服务可用 RED:Rate(请求速率)、Errors(错误)、Duration(延迟)。面向资源可用 USE:Utilization(利用率)、Saturation(饱和/排队)、Errors(错误)。常见黄金信号是延迟、流量、错误和饱和度。
指标必须同时看服务与下游:接口 P99 升高时,检查容器线程、业务线程池队列、数据库连接池等待、慢 SQL、HTTP 客户端连接池、Redis/MQ 延迟。只看 CPU 往往遗漏 IO 阻塞和排队。
Prometheus Label 应是有界低基数字段,如 service、endpoint、status、method、region。不能把 userId、orderId、完整 URL、异常消息、requestId 放入 Label;这些属于日志/Trace 属性。每个新指标发布前要评估组合数,例如 10 个接口 * 5 个状态 * 3 个地域可以接受,百万用户 ID 不可以。
Trace 与上下文传播
OpenTelemetry 常将一次请求表示为 Trace,由多个 Span 组成。入口服务创建或继承 trace context,HTTP/RPC/MQ 调用把上下文注入 Header,消费者再提取并创建子 Span。Span 属性记录方法、目标、状态、耗时等,错误 Span 记录异常类型和栈摘要,敏感参数需要脱敏。
异步线程池、CompletableFuture、消息队列是上下文最容易丢失的地方。应使用框架的 context propagation、任务装饰器或显式传递 Context,并在任务结束清理 MDC/ThreadLocal。只在 Controller 打 traceId 而不传递到下游,无法形成完整链路。
日志规范与安全
日志应结构化,至少带时间、level、service、environment、version、traceId、spanId、业务主键和事件名。订单创建成功、支付回调、状态机拒绝、补偿执行等应记录关键业务事件;不要在每个循环打印 DEBUG 造成日志风暴。
密码、Token、身份证、银行卡、手机号、完整请求 Body 不应直接记录。需要诊断时使用掩码、摘要或受权限保护的审计系统。异常日志使用 log.error("create order failed, orderId={}", orderId, ex) 保留堆栈;不要只记录 ex.getMessage()。
告警与 SLO
告警目标是让人采取行动。CPU 超过 80% 不是天然告警,应结合持续时间、服务影响和容量基线。对于用户接口,可定义 SLI:成功率和延迟;定义 SLO:例如 30 天内 99.9% 请求成功、99% 请求低于 300ms;错误预算耗尽时限制高风险发布并优先可靠性治理。
| 告警类型 | 示例 | 建议动作 |
|---|---|---|
| 症状告警 | 5 分钟错误率 > 2%、P99 超 SLO | 立即看影响范围、Trace 和最近变更 |
| 饱和告警 | 线程池队列持续增长、连接池等待 | 限流/降级,定位下游慢或容量不足 |
| 预测告警 | 磁盘按趋势 24 小时内耗尽 | 扩容、清理、调整保留策略 |
| 业务告警 | 支付成功率下降、积压超阈值 | 对账、重试、切换通道或人工介入 |
告警必须包含 runbook:影响服务、仪表盘、最近部署、关键查询、止血步骤和升级路径。没有处理动作的告警只会制造噪声。
Java 落地与故障排查
Spring Boot 可通过 Actuator/Micrometer 暴露 JVM、HTTP、线程池、连接池指标;OpenTelemetry Java Agent 或 SDK 自动/手动采集 Trace;日志通过 MDC 注入 traceId。自动埋点方便但不是免治理,仍应统一服务名、资源属性、采样和敏感字段过滤。
排查接口慢时按:仪表盘确认范围 -> Trace 找最长 Span -> 日志确认业务主键与异常 -> Profile/线程 dump 看 CPU、锁和阻塞 -> 下游指标验证根因。不要从单条日志直接断言数据库慢,也不要只因为一个慢 Trace 就调大线程池。
高频面试题
| 问题 | 回答要点 |
|---|---|
| Metrics、Logs、Traces 怎么配合? | 指标发现范围和趋势,Trace 定位一次请求的耗时链路,日志补充业务细节与异常。 |
| 为什么 Label 不能带 userId? | 产生高基数时间序列,消耗监控系统内存和索引;应放入日志/Trace。 |
| SLO 和告警关系? | SLO 定义用户可感知目标,告警围绕错误预算和服务影响,避免资源阈值噪声。 |
| Trace 如何跨异步/MQ 传递? | 注入和提取 W3C Trace Context 等上下文,使用任务装饰器/框架传播并清理线程本地状态。 |
| 日志为什么不能记完整 Token? | 日志是广泛流转的数据,泄露后可直接冒用身份,应脱敏或禁止记录。 |


