go可观测性三支柱需组件精准匹配:日志用zerolog适配loki字段规范,指标用countervec和histogramvec语义区分打点,追踪复用全局tracer并透传ctx确保trace_id串联。

日志该用 zerolog 还是 zap?先看你的日志流向
如果日志最终进 Loki,用 zerolog 更省心;zap 默认字段名是 lvl,而 Loki 的默认 parser 期待 level,不改配置就会丢 level 字段。
-
zerolog.New(os.Stderr).With().Timestamp().Str("service", "api").Logger()—— 初始化一次,后续所有日志自动带service和时间戳 - 若跑在 Kubernetes,务必设
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix,否则时区混乱导致日志时间戳错位 - 禁止字符串拼接:
log.Info().Str("user_id", u.ID).Msg("user login")✅,log.Info().Msg("user login: " + u.ID)❌(破坏结构化)
Metrics 必须区分 Counter 和 Histogram 的语义
把请求耗时用 Counter 统计,等于放弃 P95/P99 分析能力;把成功数塞进 Histogram,等于给 Prometheus 制造一堆无用 bucket 指标。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 请求总量、错误总数、队列长度 →
prometheus.NewCounterVec,标签用method、status_code - HTTP 延迟、DB 查询耗时 →
prometheus.NewHistogramVec,显式定义Buckets:[]float64{0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5} - 暴露前必须调用
prometheus.MustRegister(metric),否则/metrics返回空,且无任何报错提示
Tracing 不能每次请求 new Tracer,必须全局复用
新手常在 handler 里写 otel.Tracer("http"),结果每秒创建数百 tracer 实例,内存暴涨、GC 频繁、span 大量丢失。
- 在
main()初始化一次:tracer := otel.Tracer("my-service"),然后注入到中间件或 DB 封装层 - 手动创建 span 时,必须用
ctx, span := tracer.Start(r.Context(), "db.query"),漏传ctx就断链 - 别自己实现 trace 注入逻辑:用
otelhttp.NewHandler包裹http.Handler,或直接集成ginotel.Middleware(Gin 用户)
OpenTelemetry 导出器选 OTLP 还是 Jaeger?看你的后端是否标准化
如果后端是 Tempo、Jaeger 或阿里云链路追踪(非标准 SkyWalking 封装),优先选 otlptracegrpc;如果只跑本地调试,stdouttrace 足够。
- 生产环境导出必须用
otlptracegrpc.WithEndpoint("collector:4317"),而非localhost(容器内 DNS 不解析) - 禁用
otlptracegrpc.WithInsecure()上线,改用 TLS + mTLS 认证 - 避免同时注册多个 exporter(如 stdout + OTLP),会干扰 span 批处理节奏,导致丢数据
trace_id 字段为空、指标 label 缺少 service、span 的 parent_span_id 为零值——这些都不是独立模块的问题,而是三者之间字段没对齐、context 没透传到底的结果。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










