67 lines
1.5 KiB
Go
67 lines
1.5 KiB
Go
package telemetry
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"net/http"
|
||
|
|
"sort"
|
||
|
|
"sync"
|
||
|
|
)
|
||
|
|
|
||
|
|
// collector 是 Counter / Gauge / Histogram 共同实现的内部接口。
|
||
|
|
type collector interface {
|
||
|
|
collect(w io.Writer)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Registry 持有已注册的全部指标,提供 Prometheus 文本格式导出。
|
||
|
|
type Registry struct {
|
||
|
|
mu sync.RWMutex
|
||
|
|
entries map[string]collector
|
||
|
|
order []string // 保留注册顺序,使输出可预测
|
||
|
|
}
|
||
|
|
|
||
|
|
func newRegistry() *Registry {
|
||
|
|
return &Registry{
|
||
|
|
entries: make(map[string]collector),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *Registry) register(name string, c collector) error {
|
||
|
|
r.mu.Lock()
|
||
|
|
defer r.mu.Unlock()
|
||
|
|
if _, exists := r.entries[name]; exists {
|
||
|
|
return fmt.Errorf("metric %q already registered", name)
|
||
|
|
}
|
||
|
|
r.entries[name] = c
|
||
|
|
r.order = append(r.order, name)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Gather 把所有指标按注册顺序写入 w,文本格式遵循 Prometheus 0.0.4。
|
||
|
|
func (r *Registry) Gather(w io.Writer) error {
|
||
|
|
r.mu.RLock()
|
||
|
|
order := append([]string(nil), r.order...)
|
||
|
|
defer r.mu.RUnlock()
|
||
|
|
for _, name := range order {
|
||
|
|
r.entries[name].collect(w)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handler 返回标准 Prometheus 抓取端点。
|
||
|
|
func (r *Registry) Handler() http.Handler {
|
||
|
|
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||
|
|
w.Header().Set("Content-Type", "text/plain; version=0.0.4; charset=utf-8")
|
||
|
|
_ = r.Gather(w)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// 注册顺序的辅助,用于测试断言。
|
||
|
|
func (r *Registry) names() []string {
|
||
|
|
r.mu.RLock()
|
||
|
|
defer r.mu.RUnlock()
|
||
|
|
out := append([]string(nil), r.order...)
|
||
|
|
sort.Strings(out)
|
||
|
|
return out
|
||
|
|
}
|