这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 微服务排障依赖三大支柱:日志、指标和链路追踪。日志解释单次事件细节,指标发现趋势和告警,trace 把跨服务调用串成链路。
23.1 三大支柱
| 支柱 | 数据模型 | 典型问题 |
|---|---|---|
| Logging | 离散事件文本/JSON | 这次请求为什么失败 |
| Metrics | 时间序列数值 | 系统现在是否异常 |
| Tracing | span 树 | 延迟花在哪里 |
| Profiling | CPU/内存采样 | 方法级热点 |
不要只收集而不治理。观测数据越多,成本和噪声越高。
23.2 日志
结构化字段:
timestamp
level
service
instance
traceId
spanId
userId
path
status
cost
message
日志规范:
- 统一 UTF-8;
- 异常必须打印堆栈;
- 避免多行普通文本;
- 不打印密码和 token;
- 手机号、身份证脱敏;
- traceId 必须贯穿;
- 日志级别可动态调整。
JSON 输出可结合日志平台字段提取,减少二次解析成本。
23.3 Actuator
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置:
management:
endpoints:
web:
exposure:
include: health,info,prometheus,metrics
endpoint:
health:
show-details: never
常用端点:
| 端点 | 用途 |
|---|---|
/actuator/health |
健康状态 |
/actuator/metrics |
指标 |
/actuator/prometheus |
抓取 |
/actuator/env |
环境,敏感 |
/actuator/beans |
Bean,敏感 |
/actuator/configprops |
配置,敏感 |
/actuator/threaddump |
线程,敏感 |
生产默认只暴露最小集合。
23.4 Micrometer
计数器:
@Service
public class OrderMetrics {
private final Counter createTotal;
private final Timer createTimer;
public OrderMetrics(MeterRegistry registry) {
this.createTotal = Counter.builder("order_create_total")
.description("Order create count")
.register(registry);
this.createTimer = Timer.builder("order_create_seconds")
.description("Order create latency")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
}
}
标签使用低基数字段:
method
path template
status
service
outcome
不要把 userId、orderId、URL 原文作为 label。
23.5 Prometheus 与 Grafana
暴露端点:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
Prometheus 抓取:
scrape_configs:
- job_name: spring-boot
metrics_path: /actuator/prometheus
static_configs:
- targets: ["localhost:8080"]
核心指标:
| 指标 | 方向 |
|---|---|
| http_server_requests_seconds | 请求延迟 |
| jvm_memory_used_bytes | 内存 |
| jvm_gc_pause_seconds | GC |
| hikaricp_connections_pending | 数据库等待 |
| executor_queued | 线程池积压 |
| resilience4j_circuitbreaker_state | 熔断 |
| process_cpu_usage | CPU |
23.6 Micrometer Tracing
依赖:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
链路结构:
traceId = 4bf92f3577b34da6a3ce929d0e0e4736
Gateway span
-> Order Controller span
-> Order Service span
-> DB span
-> Inventory Client span
传播 Header:
traceparent: 00-traceId-spanId-flags
b3: traceId-spanId
x-trace-id
自定义 span:
@Service
public class PricingService {
private final Tracer tracer;
public BigDecimal calculate(Order order) {
Span span = tracer.nextSpan().name("pricing.calculate").start();
try (Tracer.SpanInScope scope = tracer.withSpan(span)) {
return doCalculate(order);
} finally {
span.end();
}
}
}
23.7 OpenTelemetry
OpenTelemetry 提供语言 SDK、Collector 和协议,适合统一日志、指标、trace。
App -> OTLP -> Collector -> Backend
Collector 能做:
- 协议转换;
- 采样;
- 过滤脱敏;
- 重试缓冲;
- 多后端分发。
采样策略:
| 策略 | 说明 |
|---|---|
| 全采样 | 成本高,排障方便 |
| 按比例采样 | 常见 |
| 尾部采样 | 根据结果保留慢/错误请求 |
| 强制保留 | 关键接口和 debug 请求 |
错误和慢请求应优先保留。
23.8 告警设计
告警模板:
名称:
严重级别:
触发条件:
持续时间:
影响:
排查入口:
负责人:
预案:
核心告警:
| 指标 | 条件 |
|---|---|
| availability | 5xx 或不可用 |
| latency P99 | 超目标 |
| error rate | 突增 |
| CPU throttling | 持续 |
| memory near limit | 高水位 |
| GC P99 | 超阈值 |
| DB pool pending | 大于 0 持续 |
| consumer lag | 持续增长 |
| circuit breaker | OPEN |
告警必须可执行,否则只适合 dashboard。
23.9 日志脱敏
错误:
log.info("login success, request={}", request);
正确:
log.info("login success, userId={}, phone={}", request.userId(), mask(request.phone()));
常见脱敏:
| 数据 | 策略 |
|---|---|
| 手机号 | 前 3 后 4 |
| 身份证 | 前 1 后 1 |
| 银行卡 | 前 4 后 4 |
| token | 只记前缀和长度 |
| 密码 | 完全不记录 |
对第三方对象输出 toString 前,要确认没有敏感字段。
本章小结
可观测性要同时建设日志、指标和 trace,并通过 traceId 将三者关联。日志要结构化并脱敏,指标要低基数,trace 要传播到数据库、消息和下游调用。告警应面向可执行动作,避免只堆 dashboard。
思考题
- 日志、指标、trace 分别解决什么问题?
- 为什么不能用 userId 做指标标签?
- 如何让错误请求更容易被采样保留?
- Actuator 哪些端点不应暴露?
- 如何设计一条可执行告警?