otelmetric.mustnewmeter因依赖全局不可变meterprovider,测试/热重载时易致指标重复注册;应使用noopmeterprovider或带reader的临时provider,并避免init中调用。

为什么 otelmetric.MustNewMeter 不能直接用在测试或热重载场景
因为 otelmetric.MustNewMeter 底层依赖全局注册的 meterProvider,而默认的 otelmetric.NewMeterProvider() 创建的是不可变实例——一旦初始化完成,后续调用 meterProvider.Meter() 返回的都是同一个底层实现。如果你在单元测试里反复创建、关闭、再创建新 provider,但没显式清理旧 meter 的引用,就可能触发指标重复注册(比如 duplicate metric name 错误)。
- 测试中务必用
telemetry.NewNoopMeterProvider()或带WithReader的临时 provider,避免污染全局状态 - 生产代码里别在函数内反复调用
MustNewMeter;应把meter作为依赖注入,生命周期与服务对齐 - 注意 Go 的包级变量初始化顺序:如果多个包都 init 时调用
MustNewMeter,可能因导入顺序导致 meter 绑定到未配置好的 provider 上
导出器选 otlphttp.Exporter 还是 otlpgrpc.Exporter
取决于你后端接收方支持什么协议,而不是“哪个更快”。otlpgrpc 默认走 gRPC,需要后端开启 gRPC 端口(如 4317),且 Go 客户端需额外引入 google.golang.org/grpc;otlphttp 走 HTTP/1.1 或 HTTP/2,端口通常是 4318,部署更轻量,但默认不压缩 payload。
- 本地开发调试优先用
otlphttp:启动otel-collector时加--set=exporters.otlphttp.endpoint=localhost:4318即可 - K8s 环境若已统一用 Istio 或 gRPC 负载均衡,选
otlpgrpc更省连接数,但得确保 sidecar 支持 gRPC 流 - 两者都支持 TLS 和认证,但
otlphttp的WithEndpoint参数值要带https://前缀,otlpgrpc则用WithEndpoint("host:port")+WithTLSCredentials
Int64Counter 和 Int64UpDownCounter 到底该用哪个
看指标语义是否允许“回退”。比如请求计数、错误次数这种只增不减的,用 Int64Counter;而活跃连接数、内存使用字节数这类可能上升也可能下降的,必须用 Int64UpDownCounter,否则上报负值会 panic 或被静默丢弃。
-
Int64Counter的Add方法不允许传负数,传了会 panic:"counter cannot decrease" -
Int64UpDownCounter允许负值,但要注意它不是“差值”——每次Add(-1)都是绝对变化,不是和上次值比较 - 如果业务逻辑里需要“当前值”,别靠累加估算;应单独维护一个原子变量,用
Int64Gauge定期Set,比如runtime.NumGoroutine()这类瞬时状态
自定义指标标签(attributes)写错位置导致查不到数据
标签必须在 instrument 创建时通过 instrument.WithDescription 或 instrument.WithUnit 以外的选项传入——不对,这是常见误解。attributes 实际是在每次 Add 或 Record 时传的,不是定义 instrument 时绑定的。很多人把标签写在 Meter.Int64Counter("http.requests", ...) 的 opts 里,结果所有打点都带同样静态标签,失去了区分维度的意义。
立即学习“go语言免费学习笔记(深入)”;
- 正确做法:定义 counter 时不带业务属性,调用
counter.Add(ctx, 1, attribute.String("http.method", "GET"), attribute.String("http.status_code", "200")) - 高频打点场景下,避免每次 new attribute;用
attribute.KeyValue缓存复用,比如status200 := attribute.String("http.status_code", "200") - OpenTelemetry 规范限制每条指标最多 10 个 attributes,超了会被截断,且 key 名建议小写+点号分隔(如
http.client_ip),别用空格或大写字母
指标名称里的点号(.)不是路径分隔符,而是命名约定的一部分;后端存储(如 Prometheus)会把它转成下划线,但 OpenTelemetry SDK 本身不解析这个结构。真正影响查询的,是打点时传的 attributes 组合是否足够正交——漏掉一个关键 label,比如忘了加 service.name,就可能在 Grafana 里根本看不到这条指标。










