1. 6.1 日志、指标、追踪的集成

第三~五章覆盖运行时与分布式路径;本节说明 zhenyi-base/zlogzhenyi/zmetrics(Prometheus 暴露、运行时采集)与 消息内 Trace 字段 + zactor.SetTraceHooks 如何拼成可观测性闭环。实现以仓库为准,文中 API 以当前导出符号为准。

1.1. 6.1.1 可观测性的三个支柱

现代分布式系统的可观测性由三部分组成:

支柱 解决什么问题 典型工具
日志(Logging) "发生了什么"——记录事件详情 ELK、Loki
指标(Metrics) "发生了多少"——聚合统计、趋势分析 Prometheus、Grafana
追踪(Tracing) "发生在哪里"——跨服务调用链路 Jaeger、Zipkin

三者互补;本框架提供 结构化日志自研指标注册表 + /metricsTrace 字段透传 + 可选 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 内自行把 SpanContextzmsg.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 / draining503

1.6. 6.1.6 本节要点

  1. 日志zlog(zap);trace 相关字段见 zlog.TraceZapFields / TraceFieldsFromContextzhenyi-base/zlog)。
  2. 指标zmetrics.Enable(ctx, addr)/metricsRegisterHealthCheckSetDraining 影响探针。
  3. 慢调用FrameworkTuning.SlowLogThresholdHandlerSlowLogThreshold 分工见上。
  4. Tracezmsg.Message 携带 TraceIdHi/LoSpanId;Gate SetTraceHook;Actor SetTraceHooks(仅允许 首次 sync.Once 注册)。
  5. 扩展:OTel/Jaeger 通过 hook 注入,需自行保证 ID 编码与采样策略

6.2 节:预注册指标名与 PromQL 示例(以 zmetrics/framework.go 为索引)。

results matching ""

    No results matching ""