你是不是有时候或者常遇到这些场景:
- 用户反馈“页面卡住了”,你查日志翻到眼花也没找到问题在哪;
- 凌晨三点监控告警响了,CPU飙升,但日志里一片“岁月静好”;
- 一个请求到底经过了几个服务?哪个环节突然慢了?靠猜!
日志、指标、链路追踪这三大支柱,单独用就像“单科医生”,能解决局部问题,但面对复杂系统的“全身检查”,必须融合协同。
一、 三大支柱:各司其职,缺一不可
-
日志 (Logging):系统的“流水账”
- 是什么: 记录离散的事件、状态变化、错误堆栈。比如:“用户ID 123 在 2023-10-27 14:30:02 下单成功”,“数据库连接失败:ConnectionTimeoutException…”。
- 擅长: 事后详细排查,了解“当时发生了什么”、“为什么出错”。
- 局限: 数据量大,关联性弱,难以实时反映系统整体健康度。
-
指标 (Metrics):系统的“体检报告”
- 是什么: 可聚合的、随时间变化的数值。比如:当前QPS=1500、CPU使用率=75%、接口平均响应时间=120ms、错误率=0.5%。
- 擅长: 实时监控、告警、展示系统整体负载、性能趋势、资源消耗。
- 局限: 告诉你“系统发烧了”,但不一定知道“哪里发炎了”、“为什么发烧”。
-
链路追踪 (Tracing):请求的“全息影像”
- 是什么: 记录一个请求(如用户下单)从发起到结束,流经的所有服务、组件(网关、A服务、B服务、DB、缓存等)的调用路径、耗时、状态。
- 擅长: 理解请求的生命周期、定位性能瓶颈(慢在哪个服务/方法)、分析跨服务调用的故障根源。
- 局限: 采样影响开销,不记录每个请求内部详细日志。
二、 融合实践:1+1+1 > 3
关键:让数据关联起来! 核心是贯穿一个唯一的 Trace ID。
-
当指标告警(如错误率飙升):
- 立刻查看对应时间点、对应服务的链路追踪数据,找到出错频率高的调用路径。
- 聚焦到出错的具体链路,获取该链路的唯一 Trace ID。
- 用 Trace ID 去检索日志,精准定位该请求流经的所有服务的相关日志(尤其是错误堆栈),瞬间查明根本原因。
-
当用户报障(如订单失败):
- 获取用户提供的订单号/请求时间。
- 在日志系统中用订单号/时间范围找到关键日志,提取出该请求的 Trace ID。
- 用 Trace ID 在追踪系统中还原请求的完整路径和耗时,精确定位卡在哪个环节(是支付服务超时?还是库存服务锁冲突?)。
- 结合指标看当时该服务的负载(如支付服务当时的QPS、响应时间P99),判断是否是资源瓶颈导致。
三、 SpringBoot融合实战Demo
// 关键依赖 (pom.xml)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <!-- 暴露指标端点 -->
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId> <!-- Prometheus 指标 -->
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId> <!-- 链路追踪 -->
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId> <!-- 上报到Zipkin -->
</dependency>
// application.yml - 核心配置
spring:
application:
name: order-service # 服务名很重要!
sleuth:
sampler:
probability: 1.0 # 采样率 (生产环境可调低)
zipkin:
base-url: http://localhost:9411/ # Zipkin服务器地址
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
management:
endpoints:
web:
exposure:
include: prometheus, health, metrics # 暴露Prometheus指标端点
metrics:
tags:
application: ${spring.application.name} # 指标打上应用名标签
// 示例Controller - 日志 + 链路 + 指标自动集成
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class OrderController {
@GetMapping("/order/{orderId}")
public String getOrder(@PathVariable String orderId) {
// 1. 关键日志:自动包含 TraceID, SpanID!
log.info("查询订单开始, orderId: {}", orderId);
// 2. 模拟业务逻辑 (这里省略数据库操作)
// ... 你的业务代码,Sleuth会自动追踪
// 3. 模拟可能出错
if ("error".equals(orderId)) {
log.error("模拟订单查询异常!orderId: {}", orderId);
throw new RuntimeException("Order Not Found!");
}
log.info("查询订单成功, orderId: {}", orderId);
return "Order Details for " + orderId;
}
}
运行与观察:
- 启动应用 & Zipkin:
docker run -d -p 9411:9411 openzipkin/zipkin - 访问:
http://localhost:8080/order/123(成功) 和http://localhost:8080/order/error(失败) - 看日志: 控制台输出自动包含
[order-service, c3d8f7a9f6d73a12, c3d8f7a9f6d73a12]这样的格式。这就是[应用名, TraceID, SpanID]!同一个请求在不同服务中 TraceID 相同。 - 看链路追踪 (Zipkin): 访问
http://localhost:9411,搜索order-service,看到请求的完整链路、耗时、状态(成功/失败)。 - 看指标 (Prometheus): 访问
http://localhost:8080/actuator/prometheus,看到大量指标如http_server_requests_seconds_count{application="order-service", uri="/order/{orderId}", ...}统计了接口调用次数、耗时分布等。配置 Prometheus + Grafana 即可可视化。 - 故障排查: 在 Zipkin 中找到失败的链路 (
/order/error),点击查看详情。复制 TraceID,去日志系统里用这个 TraceID 搜索,瞬间过滤出这次失败请求相关的所有日志(可能跨越多个服务),看到具体的错误信息模拟订单查询异常!。
四、 构建你的可观测体系:关键点
- 标准化: 统一日志格式 (如JSON)、指标命名、追踪协议 (如OpenTelemetry)。
- 集中化: 日志 => ELK/ Loki,指标 => Prometheus + Grafana,追踪 => Jaeger/Zipkin。数据孤岛是融合的天敌。
- 贯穿 Trace ID: 确保所有组件都能传递和记录这个唯一ID,这是关联的纽带!
- 合理采样: 全量追踪开销大,按需采样(如错误请求全采,成功请求按比例)。
- 告警联动: 指标告警触发时,能自动关联展示相关链路的错误率和日志片段。
- 开发者赋能: 让开发者方便地在代码中打点(日志、自定义指标、追踪注解)。