diff --git a/telemetry/counter.go b/telemetry/counter.go index 1874903..e8ef59a 100644 --- a/telemetry/counter.go +++ b/telemetry/counter.go @@ -36,8 +36,12 @@ func newCounter(name, help string, labelNames []string) *Counter { func (c *Counter) Inc(labels Labels) { c.Add(1, labels) } // Add 累加 v(v 必须 >= 0)。 +// +// 空接收者(nil *Counter)安全:未初始化的全局指标(如 metrics.Init() 尚未调用时 +// 的 metrics.UpstreamTotal)会被静默忽略,而不是 panic。 +// 这让"直接调用 handler"的测试/复用场景不会因为漏掉指标初始化而崩溃。 func (c *Counter) Add(v float64, labels Labels) { - if v < 0 { + if c == nil || v < 0 { return } child := c.getOrCreate(labels) diff --git a/telemetry/gauge.go b/telemetry/gauge.go index 6c401d3..9b2c758 100644 --- a/telemetry/gauge.go +++ b/telemetry/gauge.go @@ -33,7 +33,11 @@ func newGauge(name, help string, labelNames []string) *Gauge { } // Set 直接设置当前值。 +// 空接收者(nil *Gauge)安全,见 Counter.Add 的说明。 func (g *Gauge) Set(v float64, labels Labels) { + if g == nil { + return + } child := g.getOrCreate(labels) child.bits.Store(float64bits(v)) } @@ -45,7 +49,11 @@ func (g *Gauge) Inc(labels Labels) { g.Add(1, labels) } func (g *Gauge) Dec(labels Labels) { g.Add(-1, labels) } // Add 累加 v(可负)。 +// 空接收者(nil *Gauge)安全,见 Counter.Add 的说明。 func (g *Gauge) Add(v float64, labels Labels) { + if g == nil { + return + } child := g.getOrCreate(labels) for { bits := child.bits.Load() diff --git a/telemetry/histogram.go b/telemetry/histogram.go index 6482105..a9287b9 100644 --- a/telemetry/histogram.go +++ b/telemetry/histogram.go @@ -47,7 +47,11 @@ func newHistogram(name, help string, buckets []float64, labelNames []string) *Hi } // Observe 记录一个观测值。 +// 空接收者(nil *Histogram)安全,见 Counter.Add 的说明。 func (h *Histogram) Observe(v float64, labels Labels) { + if h == nil { + return + } child := h.getOrCreate(labels) for { bits := child.sumBits.Load() diff --git a/telemetry/labels.go b/telemetry/labels.go index a45604a..d95fd04 100644 --- a/telemetry/labels.go +++ b/telemetry/labels.go @@ -5,7 +5,12 @@ // - 零外部依赖,只使用标准库; // - label key 在指标注册时锁定,运行期不可新增(避免 cardinality 爆炸); // - 所有并发安全由实现保证,调用方无需加锁; -// - Meter 是高层入口,NoopMeter 用于测试。 +// - Meter 是高层入口,NoopMeter 用于测试; +// - **空接收者安全**: 未初始化的 *Counter / *Gauge / *Histogram(即 nil 指针) +// 上的 Inc/Add/Set/Observe 一律静默忽略,不 panic。 +// 这条约定是为 metrics 包的全局指标变量服务的 —— 它们默认为 nil,只有 main +// 调用过 metrics.Init() 之后才有值;直接复用 handler 的场景(集成测试、 +// 未来做成库)不应该因为漏掉指标初始化而崩掉整个请求处理。 package telemetry import (