1. 6.1 日志、指标、追踪的集成
第三~五章覆盖运行时与分布式路径;本节说明 zhenyi-base/zlog、zhenyi/zmetrics(Prometheus 暴露、运行时采集)与 消息内 Trace 字段 + zactor.SetTraceHooks 如何拼成可观测性闭环。实现以仓库为准,文中 API 以当前导出符号为准。
1.1. 6.1.1 可观测性的三个支柱
现代分布式系统的可观测性由三部分组成:
| 支柱 | 解决什么问题 | 典型工具 |
|---|---|---|
| 日志(Logging) | "发生了什么"——记录事件详情 | ELK、Loki |
| 指标(Metrics) | "发生了多少"——聚合统计、趋势分析 | Prometheus、Grafana |
| 追踪(Tracing) | "发生在哪里"——跨服务调用链路 | Jaeger、Zipkin |
三者互补;本框架提供 结构化日志、自研指标注册表 + /metrics、Trace 字段透传 + 可选 hook(非强制接入 OTel)。
1.2. 6.1.2 日志:结构化 + 上下文
zhenyi 的日志基于 zlog(封装 zap),默认结构化输出:
{"level":"info","ts":"2024-03-26T10:15:30.123+0800","caller":"gate.go:208","msg":"OnRead","msgId":1001,"actorId":5,"cost":"12.3ms"}
1.2.1. 关键上下文字段
每条日志自动携带:
| 字段 | 含义 |
|---|---|
actorId |
当前 Actor 的 ID |
actorType |
Actor 类型 |
msgId |
消息 ID |
traceIdHi / traceIdLo |
与 zmsg.Message 及 context 中的 trace 字段对应(若写入日志) |
cost |
处理耗时(如果记录) |
1.2.2. 日志级别
// 调试信息(开发环境)
zlog.Debug("processing message", zap.Int32("msgId", msgId))
// 正常运行信息
zlog.Info("actor started", zap.Uint64("actorId", id))
// 警告(不影响运行但需要关注)
zlog.Warn("slow handler", zap.Duration("cost", cost), zap.Int32("msgId", msgId))
// 错误(需要人工介入)
zlog.Error("failed to connect NATS", zap.Error(err))
1.2.3. 慢消息日志
- 通用处理路径:多用
zmodel.GetFrameworkTuning().SlowLogThreshold(默认 10ms,见zmodel/actor.go)。 - per-handler Prometheus 计数:
zmetrics.HandlerSlowLogThreshold(默认 10ms)用于HandlerMetric.RecordCall是否slow.Inc()。
两者可分别调;生产上建议统一配置来源,避免「日志认为不慢、指标认为慢」的割裂。
1.3. 6.1.3 指标:Prometheus 集成
zhenyi 内置 Prometheus 指标服务,一行代码启动:
srv := zmetrics.Enable(ctx, ":9090")
defer srv.Shutdown(context.Background())
启动后访问 http://localhost:9090/metrics 可看到所有指标。
1.3.1. 指标类型
注册表提供 Counter / Gauge / Histogram 等类型(实现见 zhenyi/zmetrics),面向热路径使用 原子操作;具体字段布局以源码为准,不必与早期教程中的简化 struct 逐项一致。
1.3.2. 全局注册表
registry := zmetrics.Global()
// 创建指标
msgTotal := registry.Counter("zhenyi_msg_total", "Total messages processed")
onlineUsers := registry.Gauge("zhenyi_online_users", "Current online users")
latency := registry.Histogram("zhenyi_handler_latency_ms", "Handler latency", zmetrics.DefaultLatencyBounds)
1.3.3. Prometheus 输出格式
# HELP zhenyi_handler_total Per-handler call count
# TYPE zhenyi_handler_total counter
zhenyi_handler_total{handler="1001",actor_id="1",actor_type="2"} 12345
# HELP zhenyi_handler_latency_ms Per-handler latency in ms
# TYPE zhenyi_handler_latency_ms histogram
zhenyi_handler_latency_ms_bucket{handler="1001",le="1"} 100
zhenyi_handler_latency_ms_bucket{handler="1001",le="5"} 500
zhenyi_handler_latency_ms_bucket{handler="1001",le="10"} 1000
zhenyi_handler_latency_ms_bucket{handler="1001",le="+Inf"} 12345
zhenyi_handler_latency_ms_sum{handler="1001"} 12345.6
zhenyi_handler_latency_ms_count{handler="1001"} 12345
1.4. 6.1.4 追踪:Trace ID 透传
zhenyi 实现了简化的分布式追踪:每条消息携带 TraceIdHi + TraceIdLo(128 位),在跨 Actor、跨进程传递时自动透传。
1.4.1. 生成 Trace ID
zgate.Server.GenCliMsg:若已 SetTraceHook(inject) 则走自定义注入;否则 TraceIdHi = zid.NextFast() 等默认快路径(见 gate.go)。对外请使用 server.SetTraceHook(func(msg *zmsg.Message)),不要依赖未导出字段。
1.4.2. 跨 Actor 透传
// Actor 发消息给另一个 Actor
m.TraceIdHi = msg.TraceIdHi
m.TraceIdLo = msg.TraceIdLo
m.SpanId = msg.SpanId
1.4.3. 日志关联
zlog.Info("handler called",
zap.Uint64("traceIdHi", msg.TraceIdHi),
zap.Int32("msgId", msgId))
通过 traceIdHi 可以把同一条请求的所有日志串起来,在 ELK/Loki 中搜索。
1.4.4. 与 OpenTelemetry 的关系
当前消息侧为 128 位 TraceId(uint64×2)+ SpanId,与完整 OTel Span 树正交。接入 OTel 时应在 SetTraceHook / zactor.SetTraceHooks 内自行把 SpanContext 与 zmsg.Message 字段对齐(注意类型为 uint64);不要使用错误示例中将 fmt.Sprintf 结果喂给 TraceIDFromBytes 之类写法。
1.5. 6.1.5 一键启用
func main() {
ctx := context.Background()
// 启动 Prometheus 指标服务
metricsSrv := zmetrics.Enable(ctx, ":9090")
defer metricsSrv.Shutdown(context.Background())
// 注册健康检查
metricsSrv.RegisterHealthCheck("etcd", func() error {
if !etcdClient.IsConnected() {
return errors.New("etcd disconnected")
}
return nil
})
metricsSrv.RegisterHealthCheck("nats", func() error {
if !natsClient.IsConnected() {
return errors.New("nats disconnected")
}
return nil
})
// 启动 Actor 系统...
}
1.5.1. 暴露的端点
| 端点 | 用途 |
|---|---|
GET /metrics |
Prometheus text exposition |
GET /healthz |
存活;SetDraining 或非空健康检查失败时返回 503(见 server.go) |
GET /readyz |
就绪;starting / draining 为 503 |
1.6. 6.1.6 本节要点
- 日志:
zlog(zap);trace 相关字段见zlog.TraceZapFields/TraceFieldsFromContext(zhenyi-base/zlog)。 - 指标:
zmetrics.Enable(ctx, addr)→/metrics;RegisterHealthCheck;SetDraining影响探针。 - 慢调用:
FrameworkTuning.SlowLogThreshold与HandlerSlowLogThreshold分工见上。 - Trace:
zmsg.Message携带TraceIdHi/Lo、SpanId;GateSetTraceHook;ActorSetTraceHooks(仅允许 首次sync.Once注册)。 - 扩展:OTel/Jaeger 通过 hook 注入,需自行保证 ID 编码与采样策略。
6.2 节:预注册指标名与 PromQL 示例(以 zmetrics/framework.go 为索引)。