可观测
可观测性是指通过系统输出的日志、链路、指标,在不修改系统的情况下推断出内部状态。Metrics 告诉你「有问题」,Traces 告诉你「问题在哪条链路」,Logs 告诉你「具体发生了什么」。
微服务越多,可观测性越重要。没有可观测性,发现问题靠运气,定位问题靠猜。服务治理 负责发现问题,可观测性负责定位问题。
适用场景
- 分布式系统故障定位:通过 Traces 追踪请求链路,快速定位故障节点
- 性能瓶颈分析:通过 Metrics 监控 QPS、延迟、资源使用率
- 生产环境问题排查:通过 Logs 结合 TraceId 还原问题现场
- 容量规划:基于历史指标数据预测资源需求
- SLA/SLO 监控:定义服务水平目标并设置告警阈值
三大支柱
| 支柱 | 定义 | 工具 |
|---|---|---|
| Metrics | 数值型定量测量(QPS、CPU) | Prometheus, Micrometer |
| Logs | 带时间戳的离散事件记录 | ELK, Loki |
| Traces | 单次请求的完整调用路径 | Jaeger, Zipkin |
Metrics
Metrics 是系统运行状态的量化指标,用于直观反映系统健康状况。
四种指标类型
| 类型 | 说明 | 示例 |
|---|---|---|
| Counter | 单调递增 | 请求总数、错误总数 |
| Gauge | 任意增减的瞬时值 | 内存使用量 |
| Histogram | 统计分布,支持分位数 | 延迟分布(P99) |
| Summary | 客户端计算分位数 | 响应时间 P50/P95 |
RED / USE 方法论
指标不能乱采,两套业界标准方法论指导「采什么」:
RED(面向请求,服务视角)
| 指标 | 含义 | 示例 |
|---|---|---|
| Rate | 请求速率 | QPS、RPS |
| Errors | 错误率 | 5xx 比例、异常数 |
| Duration | 延迟分布 | P50/P95/P99 响应时间 |
USE(面向资源,基础设施视角)
| 指标 | 含义 | 示例 |
|---|---|---|
| Utilization | 利用率 | CPU、内存使用率 |
| Saturation | 饱和程度 | 线程池队列长度、DB 连接池占用 |
| Errors | 错误数 | 磁盘 I/O 错误、网络丢包 |
先回答「用户请求是否健康」(RED),再看「资源是否耗尽」(USE),两者组合即可覆盖大多数故障场景。
Prometheus + Grafana
Prometheus 使用 Pull 模式主动拉取指标,与 Kubernetes 原生集成,自动发现服务。
scrape_configs:
- job_name: 'spring-boot-app'
static_configs:
- targets: ['localhost:8080']
metrics_path: '/actuator/prometheus'Logs
Logs 记录系统运行时的离散事件,是排查问题的重要依据。
结构化日志
传统日志难以机器解析,推荐结构化格式(JSON),且必须携带 TraceId 用于关联链路追踪。
{
"timestamp": "2026-04-14T10:00:00Z",
"level": "ERROR",
"traceId": "abc123def456",
"service": "order-service",
"message": "下单失败",
"error": "库存不足"
}日志收集架构
日志框架选择
应用中不可直接使用日志系统(Log4j、Logback)中的 API,应依赖使用日志框架门面(SLF4J)。推荐使用 SLF4J:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);日志级别使用
| 级别 | 使用场景 |
|---|---|
| ERROR | 系统逻辑出错、异常、重要错误 |
| WARN | 用户输入参数错误、刚上线时的业务行为信息 |
| INFO | 运行信息、状态变化 |
| DEBUG | 开发环境调试,生产环境禁止输出 |
| TRACE | 详细追踪信息 |
日志规范
- 所有日志文件至少保存 15 天(有些异常以"周"为频次发生)
- 根据国家法律,网络安全相关记录留存不少于六个月
- 字符串变量拼接使用占位符方式:
logger.debug("id: {}", id) - trace/debug/info 级别必须进行开关判断
- 避免重复打印日志,设置
additivity=false - 生产环境禁止
System.out和e.printStackTrace() - 异常日志包含现场信息和堆栈信息:
logger.error("params:{}", params, e)
日志配置示例
查看完整 logback 配置
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/home/admin/app/logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/home/admin/app/logs/app.log.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.example" level="INFO" additivity="false">
<appender-ref ref="FILE"/>
</logger>
<root level="WARN">
<appender-ref ref="FILE"/>
</root>
</configuration>日志安全
日志是敏感信息泄露的高发渠道:禁止打印密码、Token、身份证号等敏感字段;对外输出日志需按规范脱敏(如手机号只保留前 3 后 4 位)。
Traces
Traces 记录单个请求在分布式系统中的完整调用路径,用于定位问题链路。
TraceId / SpanId 机制
每个 Span 记录:服务名、开始时间、持续时间、状态、自定义标签。
Jaeger vs Zipkin
| 维度 | Zipkin | Jaeger |
|---|---|---|
| 来源 | Uber(CNCF 毕业) | |
| 存储 | 内存、MySQL、Cassandra | Elasticsearch、Cassandra |
| 适用 | 轻量,中小项目 | 大规模系统 |
Spring Boot 集成
management:
tracing:
sampling:
probability: 1.0 # 生产建议 0.1TraceId 自动透传(HTTP Header、MQ 消息头),日志自动携带。
OpenTelemetry
OpenTelemetry(OTel)是 CNCF 托管的统一可观测性规范,由 OpenTracing + OpenCensus 合并而来,核心目标是避免厂商锁定,用一套 API 对接任意后端。
推荐新项目使用 OTel,与 云原生 中的 Kubernetes 生态深度集成。
告警原则
| 原则 | 说明 |
|---|---|
| SLO 驱动 | 基于用户体验指标设置告警 |
| 减少噪音 | 告警必须有行动意义 |
| 分级处理 | P0(立即响应)、P1(1小时内)、P2(工作时间) |
实践路径
| 阶段 | 方案 |
|---|---|
| 基础 | Prometheus + Grafana(指标)+ ELK(日志) |
| 进阶 | 加入 Jaeger(链路追踪),日志携带 TraceId |
| 云原生 | OTel + Grafana 全家桶 |
常见问题
Q: Metrics、Logs、Traces 应该先上哪个?
A: 先上 Metrics(Prometheus + Grafana),成本最低、见效最快。能发现「有问题」后,再上 Traces 定位「问题在哪」,最后补 Logs 看「具体细节」。
Q: 采样率应该设置多少?
A: 开发环境 100%(全量采集),生产环境建议 10%(probability: 0.1)。对核心链路可动态调整为 100%。
Q: Jaeger 和 Zipkin 怎么选?
A: 中小项目用 Zipkin(轻量、部署简单),大规模系统用 Jaeger(CNCF 毕业、存储扩展性好)。新项目建议直接用 OTel + Jaeger。
Q: 日志太多怎么控制成本?
A: 1) 结构化日志按级别过滤,生产环境只收集 WARN 以上;2) 使用 Loki 替代 ELK(成本低 10x);3) 设置日志保留策略(如 7 天)。
Q: OpenTelemetry 和直接用 Jaeger SDK 有什么区别?
A: OTel 是标准规范,一套 SDK 可对接多个后端(Jaeger、Prometheus、Loki),避免厂商锁定。直接用 Jaeger SDK 只能对接 Jaeger,迁移成本高。
故障排查:Prometheus 抓不到指标
- 确认应用暴露了
/actuator/prometheus端点 - 检查防火墙是否放行 Prometheus 的抓取端口
- 确认
scrape_configs中的targets地址正确
故障排查:日志不携带 TraceId
- 确认引入了 Sleuth 或 OTel 依赖
- 检查日志格式是否包含
%X{traceId}(Logback)或对应占位符 - 确认 TraceId 透传中间件(MQ、HTTP Header)配置正确