一、引言
"线上服务出现延迟,你需要花多久找到问题根源?"
对于现代微服务架构来说,可观测性(Observability)已经不再是可选的加分项,而是必须具备的核心能力。一个完善的可观测性体系包含三大支柱:
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 可观测性三件套 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Logs │ │ Traces │ │ Metrics │ │
│ │ (日志) │ │ (链路追踪) │ │ (指标) │ │
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ │
│ │ 发生了什么 │ │ 如何发生的 │ │ 发生得频繁吗 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ 三大支柱缺一不可,共同构建完整的可观测体系 │
│ │
└─────────────────────────────────────────────────────────────────┘
今天,我将带你从零开始搭建一套完整的可观测性系统,涵盖日志、链路追踪和指标三个维度。
二、技术选型
2.1 架构全景图
┌──────────────────────────────────────────────────────────────────────────┐
│ │
│ 可观测性架构全景图 │
│ │
│ ┌──────────────┐ │
│ │ 订单服务 │ │
│ │ (Spring Boot)│ │
│ └──────┬───────┘ │
│ │ │
│ ├── Logback (JSON 日志) ──→ Promtail ──→ Loki ──→ Grafana │
│ │ │
│ ├── OpenTelemetry Agent ──→ Jaeger ──→ Grafana │
│ │ │
│ └── Micrometer ──→ Prometheus ──→ Grafana │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐│
│ │ Grafana ││
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ ││
│ │ │ 日志查询 │ │ 链路拓扑 │ │ 指标仪表盘 │ │ 统一告警管理 │ ││
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ ││
│ └─────────────────────────────────────────────────────────────────────┘│
│ │
└──────────────────────────────────────────────────────────────────────────┘
2.2 组件说明
| 组件 | 作用 | 选型理由 |
|---|---|---|
| Logback | 日志采集 | Spring Boot 默认日志框架,成熟稳定 |
| Promtail | 日志收集 | Loki 官方推荐,轻量级,支持 Kubernetes |
| Loki | 日志存储 | 日志聚合系统,与 Grafana 深度集成 |
| OpenTelemetry Agent | 链路追踪 | 开源标准,支持多语言,零代码侵入 |
| Jaeger | 链路存储 | CNCF 毕业项目,功能完善,可视化优秀 |
| Micrometer | 指标采集 | Spring Boot 默认指标库,支持多种输出 |
| Prometheus | 指标存储 | 云原生监控标准,强大的查询语言 |
| Grafana | 可视化 | 统一仪表盘,支持多数据源 |
三、日志系统:Logback → Promtail → Loki → Grafana
3.1 架构设计
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 日志系统架构 │
│ │
│ 应用层 收集层 存储层 展示层 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Logback │──│ Promtail │──│ Loki │──│ Grafana │ │
│ │ (JSON) │ │ (tail) │ │ (存储) │ │ (查询) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ 日志格式:结构化 JSON → 采集:文件 tail → 存储:索引存储 → 查询:LQL │
│ │
└─────────────────────────────────────────────────────────────────┘
3.2 Logback 配置
logback-spring.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_PATH" value="./logs"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.logstash.logback.layout.LogstashJsonLayout">
<includeContextName>false</includeContextName>
<customFields>{"app_name":"order-service","environment":"dev"}</customFields>
</layout>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/order-service.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/order-service.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.logstash.logback.layout.LogstashJsonLayout">
<includeContextName>false</includeContextName>
<customFields>{"app_name":"order-service","environment":"dev"}</customFields>
</layout>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
日志输出示例:
{
"@timestamp": "2026-07-26T10:30:00.123+08:00",
"@version": "1",
"app_name": "order-service",
"environment": "dev",
"message": "Order created successfully",
"logger_name": "com.example.order.service.OrderService",
"thread_name": "http-nio-8080-exec-1",
"level": "INFO",
"level_value": 20000,
"traceId": "abc123",
"spanId": "def456",
"orderId": "ORD-20260726-0001",
"userId": "user-12345"
}
3.3 Promtail 配置
promtail-config.yaml:
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: order-service
static_configs:
- targets:
- localhost
labels:
job: order-service
__path__: /logs/order-service*.log
pipeline_stages:
- json:
expressions:
timestamp: "@timestamp"
level: level
message: message
logger: logger_name
traceId: traceId
spanId: spanId
orderId: orderId
userId: userId
appName: app_name
- labels:
level:
appName:
orderId:
userId:
- timestamp:
source: timestamp
format: RFC3339
3.4 Loki 查询示例
# 查询最近 1 小时的错误日志
{job="order-service", level="ERROR"} |= "order" | tail 100
# 查询指定 traceId 的所有日志
{job="order-service"} | json | traceId="abc123"
# 查询订单创建失败的日志
{job="order-service"} |= "Order creation failed"
# 统计各级别日志数量
sum(count_over_time({job="order-service"} | json | level="ERROR" [1h]))
四、链路追踪:OpenTelemetry Agent → Jaeger
4.1 架构设计
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 链路追踪系统架构 │
│ │
│ 应用层 采集层 展示层 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Order Service │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ CreateOrder │──│ Inventory │──│ Payment │ │ │
│ │ │ Controller │ │ Service │ │ Client │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ └── OpenTelemetry Agent │
│ │ │
│ ┌────┴────┐ │
│ │ Jaeger │ │
│ │ (存储) │ │
│ └────┬────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ Grafana │ │
│ │ (可视化)│ │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4.2 OpenTelemetry 配置
otel-config.yaml:
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch:
exporters:
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
prometheus:
endpoint: "0.0.0.0:9464"
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [jaeger]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus]
4.3 启动应用(带 OpenTelemetry Agent)
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=order-service \
-Dotel.traces.exporter=jaeger \
-Dotel.exporter.jaeger.endpoint=http://jaeger:14250 \
-Dotel.metrics.exporter=prometheus \
-Dotel.logs.exporter=none \
-jar order-service.jar
4.4 手动添加 Span(可选)
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
@Service
public class OrderService {
private final Tracer tracer = GlobalOpenTelemetry.getTracer("order-service");
public Order createOrder(OrderRequest request) {
Span span = tracer.spanBuilder("createOrder").startSpan();
try (Scope scope = span.makeCurrent()) {
span.setAttribute("order.request.amount", request.getAmount());
span.setAttribute("order.request.userId", request.getUserId());
inventoryService.reserveStock(request.getItems());
Span paymentSpan = tracer.spanBuilder("processPayment").startSpan();
try (Scope paymentScope = paymentSpan.makeCurrent()) {
paymentService.process(request);
} finally {
paymentSpan.end();
}
return orderRepository.save(order);
} finally {
span.end();
}
}
}
4.5 Jaeger 查看链路
# 访问 Jaeger UI
# http://localhost:16686
# 搜索条件
Service: order-service
Operation: createOrder
Time: Last 15 minutes
五、指标系统:Micrometer → Prometheus → Grafana
5.1 架构设计
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 指标系统架构 │
│ │
│ 应用层 存储层 展示层 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Micrometer│──────│Prometheus│──────│ Grafana │ │
│ │(采集) │ │(存储) │ │(仪表盘) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ 指标类型:Counter / Gauge / Timer / Histogram / Summary │
│ │
└─────────────────────────────────────────────────────────────────┘
5.2 Micrometer 配置
pom.xml 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
application.yml 配置:
management:
endpoints:
web:
exposure:
include: prometheus,health,metrics
metrics:
export:
prometheus:
enabled: true
tags:
application: order-service
5.3 自定义指标
import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.Metrics;
import org.springframework.stereotype.Service;
@Service
public class OrderMetricsService {
private final Counter orderCreatedCounter = Counter.builder("order.created.total")
.description("Total number of orders created")
.tag("status", "success")
.register(Metrics.globalRegistry);
private final Counter orderFailedCounter = Counter.builder("order.created.total")
.description("Total number of orders created")
.tag("status", "failed")
.register(Metrics.globalRegistry);
public void recordOrderCreated(boolean success) {
if (success) {
orderCreatedCounter.increment();
} else {
orderFailedCounter.increment();
}
}
@Timed(value = "order.processing.time", description = "Order processing time")
public void processOrder() {
// 业务逻辑
}
}
5.4 Prometheus 配置
prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'order-service'
static_configs:
- targets: ['app:8080']
metrics_path: '/actuator/prometheus'
scrape_interval: 5s
5.5 PromQL 查询示例
# 查询订单创建总数
sum(order_created_total)
# 查询订单创建成功率
sum(order_created_total{status="success"}) / sum(order_created_total)
# 查询 P99 请求延迟
histogram_quantile(0.99, rate(http_server_requests_seconds_bucket[5m]))
# 查询活跃连接数
jvm_threads_live_threads
# 查询内存使用率
1 - (jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"})
# 查询请求 QPS
rate(http_server_requests_seconds_count[1m])
六、实战:订单服务完整可观测案例
6.1 服务架构
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 订单服务架构 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ API网关 │───▶│ 订单服务 │───▶│ 库存服务 │ │
│ │ Gateway │ │ OrderService│ │ Inventory │ │
│ └─────────────┘ └──────┬──────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 支付服务 │ │
│ │ Payment │ │
│ └─────────────┘ │
│ │
│ 请求链路:用户下单 → 创建订单 → 扣减库存 → 发起支付 → 支付回调 │
│ │
└─────────────────────────────────────────────────────────────────┘
6.2 核心代码
OrderController:
@RestController
@RequestMapping("/api/orders")
@Slf4j
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private OrderMetricsService metricsService;
@PostMapping
public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) {
log.info("Creating order for user: {}", request.getUserId());
try {
Order order = orderService.createOrder(request);
metricsService.recordOrderCreated(true);
log.info("Order created successfully: {}", order.getOrderId());
return ResponseEntity.ok(OrderResponse.from(order));
} catch (Exception e) {
metricsService.recordOrderCreated(false);
log.error("Order creation failed for user: {}", request.getUserId(), e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(OrderResponse.failed(e.getMessage()));
}
}
@GetMapping("/{orderId}")
public ResponseEntity<OrderResponse> getOrder(@PathVariable String orderId) {
log.debug("Querying order: {}", orderId);
Order order = orderService.getOrder(orderId);
if (order == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(OrderResponse.from(order));
}
@PostMapping("/{orderId}/payment-callback")
public ResponseEntity<Void> paymentCallback(
@PathVariable String orderId,
@RequestBody PaymentCallbackRequest request) {
log.info("Received payment callback for order: {}, status: {}",
orderId, request.getStatus());
orderService.handlePaymentCallback(orderId, request);
return ResponseEntity.ok().build();
}
}
OrderService:
@Service
public class OrderService {
@Autowired
private InventoryService inventoryService;
@Autowired
private PaymentService paymentService;
@Autowired
private OrderRepository orderRepository;
public Order createOrder(OrderRequest request) {
Order order = Order.builder()
.orderId(generateOrderId())
.userId(request.getUserId())
.items(request.getItems())
.amount(request.getAmount())
.status(OrderStatus.PENDING)
.build();
inventoryService.reserveStock(request.getItems());
PaymentResult paymentResult = paymentService.initiatePayment(order);
order.setPaymentId(paymentResult.getPaymentId());
return orderRepository.save(order);
}
public void handlePaymentCallback(String orderId, PaymentCallbackRequest request) {
Order order = orderRepository.findByOrderId(orderId);
if (order == null) {
throw new OrderNotFoundException(orderId);
}
if ("SUCCESS".equals(request.getStatus())) {
order.setStatus(OrderStatus.PAID);
inventoryService.confirmStock(order.getItems());
} else {
order.setStatus(OrderStatus.PAYMENT_FAILED);
inventoryService.releaseStock(order.getItems());
}
orderRepository.save(order);
}
}
七、Docker Compose 一键启动
7.1 docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- OTEL_SERVICE_NAME=order-service
- OTEL_TRACES_EXPORTER=jaeger
- OTEL_EXPORTER_JAEGER_ENDPOINT=http://jaeger:14250
- OTEL_METRICS_EXPORTER=prometheus
- OTEL_LOGS_EXPORTER=none
volumes:
- ./logs:/logs
depends_on:
- jaeger
- prometheus
promtail:
image: grafana/promtail:latest
volumes:
- ./logs:/logs
- ./config/promtail-config.yaml:/etc/promtail/config.yaml
depends_on:
- loki
command: -config.file=/etc/promtail/config.yaml
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686"
- "14250:14250"
environment:
- COLLECTOR_OTLP_ENABLED=true
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./config/prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- ./config/grafana/provisioning/datasources:/etc/grafana/provisioning/datasources
- ./config/grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards
- ./config/grafana/dashboards:/var/lib/grafana/dashboards
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
depends_on:
- loki
- jaeger
- prometheus
7.2 Grafana 数据源配置
datasources.yml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
isDefault: false
access: proxy
editable: true
- name: Loki
type: loki
url: http://loki:3100
isDefault: false
access: proxy
editable: true
- name: Jaeger
type: jaeger
url: http://jaeger:16686
isDefault: false
access: proxy
editable: true
7.3 一键启动
# 启动所有服务
docker-compose up -d
# 查看服务状态
docker-compose ps
# 访问各服务
# Grafana: http://localhost:3000 (admin/admin)
# Jaeger: http://localhost:16686
# Prometheus: http://localhost:9090
# Loki: http://localhost:3100
# 停止服务
docker-compose down
八、Grafana 仪表盘实战
8.1 仪表盘设计
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 订单服务仪表盘 │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 概览面板 │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ QPS │ │ 成功率 │ │ P99延迟 │ │ 活跃线程 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 请求延迟分布 │ │
│ │ ████████████████████░░░░░░░░░░░░░░░ │ │
│ │ 0-100ms 100-500ms 500ms-1s 1s+ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 错误率趋势 │ │
│ │ ████████████ │ │
│ │ █████████████████ │ │
│ │ ██████████████ │ │
│ │ ████████████████████ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ JVM 监控 │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ 堆内存使用 │ │ GC 次数 │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 链路追踪入口 │ │
│ │ [查询 Jaeger 链路] │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 实时日志流 │ │
│ │ {job="order-service"} |= "ERROR" | tail 50 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
8.2 关键指标面板
面板 1:QPS
- 指标:
rate(http_server_requests_seconds_count[1m]) - 单位:req/s
- 图表类型:Time series
面板 2:成功率
- 指标:
sum(http_server_requests_seconds_count{status=~"2.."}) / sum(http_server_requests_seconds_count) - 单位:%
- 图表类型:Stat
面板 3:P99 延迟
- 指标:
histogram_quantile(0.99, rate(http_server_requests_seconds_bucket[5m])) - 单位:s
- 图表类型:Time series
面板 4:堆内存使用
- 指标:
jvm_memory_used_bytes{area="heap"} / 1024 / 1024 - 单位:MB
- 图表类型:Gauge
面板 5:GC 次数
- 指标:
sum(increase(jvm_gc_collection_seconds_count[1m])) - 单位:次/分钟
- 图表类型:Time series
九、端到端验证流程
9.1 验证步骤
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 端到端验证流程 │
│ │
│ Step 1: 启动环境 │
│ └── docker-compose up -d │
│ │
│ Step 2: 发送测试请求 │
│ └── curl -X POST http://localhost:8080/api/orders \ │
│ -H "Content-Type: application/json" \ │
│ -d '{"userId":"user-1","amount":100.00}' │
│ │
│ Step 3: 验证日志 │
│ └── Grafana → Explore → Loki → 查询订单创建日志 │
│ │
│ Step 4: 验证链路 │
│ └── Jaeger → 搜索 order-service → 查看创建订单链路 │
│ │
│ Step 5: 验证指标 │
│ └── Grafana → 仪表盘 → 确认 QPS、延迟、成功率更新 │
│ │
│ Step 6: 压测验证 │
│ └── wrk -t10 -c100 -d30s http://localhost:8080/api/orders │
│ │
└─────────────────────────────────────────────────────────────────┘
9.2 测试脚本
#!/bin/bash
echo "=== 端到端验证 ==="
echo ""
echo "1. 创建订单..."
ORDER_RESP=$(curl -s -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{"userId":"user-123","amount":99.99,"items":[{"productId":"prod-001","quantity":1}]}')
echo "响应: $ORDER_RESP"
ORDER_ID=$(echo $ORDER_RESP | jq -r '.orderId')
echo ""
echo "2. 查询订单..."
curl -s http://localhost:8080/api/orders/$ORDER_ID
echo ""
echo "3. 模拟支付回调..."
curl -s -X POST http://localhost:8080/api/orders/$ORDER_ID/payment-callback \
-H "Content-Type: application/json" \
-d '{"status":"SUCCESS","transactionId":"txn-001"}'
echo ""
echo ""
echo "=== 验证完成 ==="
echo "请访问以下地址查看:"
echo " Grafana: http://localhost:3000"
echo " Jaeger: http://localhost:16686"
echo " Prometheus: http://localhost:9090"
echo " Loki: http://localhost:3100"
十、告警配置
10.1 Prometheus 告警规则
alerting-rules.yml:
groups:
- name: order-service-alerts
rules:
- alert: HighErrorRate
expr: sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) /
sum(rate(http_server_requests_seconds_count[5m])) > 0.1
for: 1m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value }}% for order-service"
- alert: HighLatency
expr: histogram_quantile(0.99, rate(http_server_requests_seconds_bucket[5m])) > 2
for: 1m
labels:
severity: warning
annotations:
summary: "High P99 latency"
description: "P99 latency is {{ $value }}s for order-service"
- alert: LowMemory
expr: 1 - (jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}) < 0.2
for: 5m
labels:
severity: critical
annotations:
summary: "Low heap memory"
description: "Heap memory usage is {{ $value }}% full"
- alert: HighGC
expr: sum(increase(jvm_gc_collection_seconds_count[5m])) > 30
for: 1m
labels:
severity: warning
annotations:
summary: "High GC frequency"
description: "{{ $value }} GC events in last 5 minutes"
10.2 Grafana 告警通知
# 在 Grafana 中配置通知渠道
# 支持:Email、Slack、Webhook、钉钉、企业微信等
# 告警策略
# 1. 错误率 > 10% → 立即通知
# 2. P99 延迟 > 2s → 持续 1 分钟通知
# 3. 内存使用率 > 80% → 持续 5 分钟通知
# 4. GC 频繁 → 持续 1 分钟通知
十一、总结
11.1 可观测性体系核心要点
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 可观测性体系核心要点 │
│ │
│ 日志系统 │
│ ├── 使用结构化 JSON 格式 │
│ ├── 包含 traceId/spanId 便于关联 │
│ ├── 使用 Promtail 采集到 Loki │
│ └── Grafana 统一查询 │
│ │
│ 链路追踪 │
│ ├── OpenTelemetry Agent 零代码侵入 │
│ ├── Jaeger 存储和展示 │
│ ├── 支持手动添加自定义 Span │
│ └── Grafana 集成链路查询 │
│ │
│ 指标系统 │
│ ├── Micrometer 标准化采集 │
│ ├── Prometheus 存储和查询 │
│ ├── 自定义业务指标 │
│ └── Grafana 仪表盘和告警 │
│ │
│ 三者关联 │
│ ├── 日志 → traceId → 链路详情 │
│ ├── 链路 → 服务 → 指标仪表盘 │
│ ├── 指标告警 → 日志/链路 快速定位 │
│ └── 形成完整的可观测闭环 │
│ │
└─────────────────────────────────────────────────────────────────┘
11.2 关键配置清单
✅ 日志配置
- Logback JSON 格式输出
- 自定义字段(app_name, traceId, spanId)
- 文件滚动策略
✅ 链路追踪配置
- OpenTelemetry Java Agent
- service.name 设置
- Jaeger 端点配置
✅ 指标配置
- Spring Boot Actuator + Micrometer
- Prometheus 导出
- 自定义业务指标
✅ 基础设施配置
- docker-compose 一键启动
- Grafana 数据源自动配置
- 告警规则配置
11.3 下一步建议
- 接入更多服务:将微服务架构中的其他服务也接入可观测性体系
- 配置告警通知:设置钉钉/企业微信/Slack 等告警渠道
- 优化仪表盘:根据业务需求定制更丰富的监控面板
- 学习 LQL/PromQL:掌握日志和指标的高级查询技巧
- 探索 Tempo:尝试 Grafana Tempo 替代 Jaeger(更轻量)
💡 互动话题:你在生产环境中是如何搭建可观测性体系的?遇到过哪些挑战?欢迎在评论区分享你的经验!
