package telemetry import ( "fmt" "io" "sort" "sync" "sync/atomic" ) // Counter 单调递增的累计指标(整数语义,内部用 float64 位以 atomic 操作)。 type Counter struct { metricName string help string labelNames []string mu sync.RWMutex values map[string]*counterChild // key = labelKey(...) } type counterChild struct { labels Labels bits atomic.Uint64 // float64 } func newCounter(name, help string, labelNames []string) *Counter { return &Counter{ metricName: name, help: help, labelNames: append([]string(nil), labelNames...), values: make(map[string]*counterChild), } } // Inc 计数 +1。 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 c == nil || v < 0 { return } child := c.getOrCreate(labels) for { bits := child.bits.Load() cur := float64frombits(bits) next := float64bits(cur + v) if child.bits.CompareAndSwap(bits, next) { return } } } func (c *Counter) getOrCreate(labels Labels) *counterChild { key := labelKey(c.labelNames, labels) c.mu.RLock() if child, ok := c.values[key]; ok { c.mu.RUnlock() return child } c.mu.RUnlock() c.mu.Lock() defer c.mu.Unlock() if child, ok := c.values[key]; ok { return child } child := &counterChild{labels: copyLabels(labels, c.labelNames)} c.values[key] = child return child } func (c *Counter) collect(w io.Writer) { fmt.Fprintf(w, "# HELP %s %s\n", c.metricName, c.help) fmt.Fprintf(w, "# TYPE %s counter\n", c.metricName) c.mu.RLock() keys := make([]string, 0, len(c.values)) for k := range c.values { keys = append(keys, k) } sort.Strings(keys) defer c.mu.RUnlock() for _, k := range keys { child := c.values[k] val := float64frombits(child.bits.Load()) writeMetricLine(w, c.metricName, child.labels, val) } }