2026-08-15 13:30:35 +08:00
|
|
|
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)。
|
2026-10-03 16:13:47 +08:00
|
|
|
//
|
|
|
|
|
// 空接收者(nil *Counter)安全:未初始化的全局指标(如 metrics.Init() 尚未调用时
|
|
|
|
|
// 的 metrics.UpstreamTotal)会被静默忽略,而不是 panic。
|
|
|
|
|
// 这让"直接调用 handler"的测试/复用场景不会因为漏掉指标初始化而崩溃。
|
2026-08-15 13:30:35 +08:00
|
|
|
func (c *Counter) Add(v float64, labels Labels) {
|
2026-10-03 16:13:47 +08:00
|
|
|
if c == nil || v < 0 {
|
2026-08-15 13:30:35 +08:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|