可观测性体系:Metrics、日志与追踪
梳理 go-ai-scheduler 的可观测性实现,包括 Prometheus Metrics、结构化日志、分布式追踪与 Grafana 看板。
可观测性三支柱
go-ai-scheduler 在三个维度上实现了可观测性:
Metrics(指标) ──▶ Prometheus + Grafana
Logs(日志) ──▶ 结构化 JSON 日志
Tracing(追踪) ──▶ TraceID 贯穿全链路
Metrics
指标注册
// pkg/metrics/metrics.go
type Registry struct {
counters map[string]*prometheus.CounterVec
histograms map[string]*prometheus.HistogramVec
gauges map[string]*prometheus.GaugeVec
}
var DefaultRegistry = NewRegistry()
核心指标
| 指标名 | 类型 | 说明 |
|---|---|---|
scheduler_dispatch_total | Counter | 任务分发次数,label: result=success/error |
scheduler_trigger_total | Counter | 任务触发次数 |
scheduler_retry_total | Counter | 重试次数 |
leader_election_total | Counter | Leader 选举次数,label: backend=mysql/etcd/local, result=acquired/contended |
worker_heartbeat_total | Counter | Worker 心跳次数 |
ai_tokens_total | Counter | AI Service LLM Token 消耗 |
ai_requests_total | Counter | AI Service 请求次数 |
dispatch_latency_ms | Histogram | 分发延迟分布 |
pending_instances | Gauge | 当前 pending 实例数 |
worker_load | Gauge | Worker 当前负载 |
使用示例
// 记录分发成功
metrics.DefaultRegistry.IncCounter("scheduler_dispatch_total",
map[string]string{"result": "success"})
// 记录分发失败
metrics.DefaultRegistry.IncCounter("scheduler_dispatch_total",
map[string]string{"result": "error"})
// 记录 Leader 选举
metrics.DefaultRegistry.IncCounter("leader_election_total",
map[string]string{"backend": "mysql", "result": "acquired"})
Prometheus 采集
所有服务在 /metrics 路径暴露 Prometheus 格式指标:
http.Handle("/metrics", promhttp.Handler())
结构化日志
日志格式
{"ts":"2026-05-26T10:30:00Z","level":"INFO","service":"scheduler","msg":"task dispatched","task_id":123,"worker_id":"worker-1","shard":"0/1"}
字段规范
| 字段 | 说明 |
|---|---|
ts | ISO8601 时间戳 |
level | DEBUG / INFO / WARN / ERROR |
service | api / scheduler / worker / ai-service |
msg | 日志消息 |
trace_id | 分布式追踪 ID(如有) |
| 业务字段 | 如 task_id, worker_id, instance_id 等 |
使用方式
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
logger.Info("task dispatched",
"task_id", task.ID,
"worker_id", worker.ID,
"shard", fmt.Sprintf("%d/%d", shard, shardTotal))
分布式追踪
TraceID 传递
TraceID 在任务全链路中传递:
// Scheduler 创建实例时生成 TraceID
instance.TraceID = generateTraceID()
// 通过 HTTP Header 传递给 Worker
req.Header.Set("X-Trace-ID", instance.TraceID)
// Worker 执行时携带 TraceID
logger.Info("task execution started",
"trace_id", traceID,
"schedule_instance_id", instanceID)
// Worker 上报结果时带回 TraceID
result.TraceID = traceID
全链路追踪
用户创建任务
│ TraceID: abc-123
▼
API Service 处理请求
│ TraceID: abc-123
▼
Scheduler 创建实例
│ TraceID: abc-123
▼
Dispatcher 发送任务
│ TraceID: abc-123
▼
Worker 执行任务
│ TraceID: abc-123
▼
Worker 上报结果
│ TraceID: abc-123
▼
Scheduler 更新状态
│ TraceID: abc-123
Grafana 看板
项目内置了 Grafana Dashboard 配置(deployments/grafana/dashboard.json),包含以下面板:
Scheduler 面板
- 分发速率:每分钟成功/失败分发次数
- 触发速率:每分钟 Cron 触发次数
- Pending 队列:当前 pending 实例数趋势
- 重试队列:retry_waiting 实例数趋势
- 分发延迟 P99:分发延迟的百分位分布
Worker 面板
- Worker 在线状态:各 Worker 心跳状态
- Worker 负载分布:各 Worker CurrentLoad 对比
- 任务执行时长:各类型任务的执行耗时
AI Service 面板
- AI 请求速率:每分钟 AI 接口调用次数
- Token 消耗:每分钟 LLM Token 消耗量
- AI 响应延迟:AI 接口的响应时间分布
- 错误率:AI 接口的错误比例
告警规则建议
基于指标可以配置以下告警:
groups:
- name: scheduler-alerts
rules:
- alert: HighPendingInstances
expr: pending_instances > 800
for: 5m
annotations:
summary: "Pending 实例数过高"
description: "当前 pending 实例数 {{ $value }},接近上限 1000"
- alert: WorkerOffline
expr: worker_heartbeat_total{status="offline"} > 0
for: 1m
annotations:
summary: "Worker 离线"
- alert: HighFailureRate
expr: rate(scheduler_dispatch_total{result="error"}[5m]) > 0.1
for: 5m
annotations:
summary: "分发失败率过高"
- alert: LeaderElectionFailing
expr: rate(leader_election_total{result="contended"}[5m]) > 10
for: 5m
annotations:
summary: "Leader 选举竞争激烈"
小结
可观测性体系的设计要点:
- Metrics 覆盖核心路径:分发、触发、重试、选举、AI 请求,全部有指标
- 日志结构化:JSON 格式,统一字段,便于日志系统解析和检索
- TraceID 贯穿全链路:从 API 到 Worker 再到 AI Service,一个 ID 追踪到底
- 开箱即用的 Grafana:内置 Dashboard,部署后即可查看系统状态
- 告警可配置:基于 Prometheus 指标,支持常见告警场景
可观测性不是"锦上添花",而是分布式系统的必需品。没有 Metrics 就不知道系统是否健康,没有日志就无法排查问题,没有 TraceID 就追踪不了跨服务调用。go-ai-scheduler 用不到 200 行 Metrics 代码 + 标准库 slog,就搭建了一套完整的可观测体系。