mirror of
https://github.com/luxfi/metric.git
synced 2026-07-27 04:12:44 +00:00
chore: remove local replace directives
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package metric
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/rpc/v2"
|
||||
)
|
||||
|
||||
// APIInterceptor tracks request durations and errors for RPC handlers.
|
||||
type APIInterceptor interface {
|
||||
InterceptRequest(i *rpc.RequestInfo) *http.Request
|
||||
AfterRequest(i *rpc.RequestInfo)
|
||||
}
|
||||
|
||||
type contextKey int
|
||||
|
||||
const requestTimestampKey contextKey = iota
|
||||
|
||||
type apiInterceptor struct {
|
||||
requestDurationCount CounterVec
|
||||
requestDurationSum GaugeVec
|
||||
requestErrors CounterVec
|
||||
}
|
||||
|
||||
func NewAPIInterceptor(registry Registry) (APIInterceptor, error) {
|
||||
metricsInstance := NewWithRegistry("api_interceptor", registry)
|
||||
|
||||
requestDurationCount := metricsInstance.NewCounterVec(
|
||||
"request_duration_count",
|
||||
"Number of times this type of request was made",
|
||||
[]string{"method"},
|
||||
)
|
||||
requestDurationSum := metricsInstance.NewGaugeVec(
|
||||
"request_duration_sum",
|
||||
"Amount of time in nanoseconds that has been spent handling this type of request",
|
||||
[]string{"method"},
|
||||
)
|
||||
requestErrors := metricsInstance.NewCounterVec(
|
||||
"request_error_count",
|
||||
"Number of request errors",
|
||||
[]string{"method"},
|
||||
)
|
||||
|
||||
return &apiInterceptor{
|
||||
requestDurationCount: requestDurationCount,
|
||||
requestDurationSum: requestDurationSum,
|
||||
requestErrors: requestErrors,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (*apiInterceptor) InterceptRequest(i *rpc.RequestInfo) *http.Request {
|
||||
ctx := i.Request.Context()
|
||||
ctx = context.WithValue(ctx, requestTimestampKey, time.Now())
|
||||
return i.Request.WithContext(ctx)
|
||||
}
|
||||
|
||||
func (apr *apiInterceptor) AfterRequest(i *rpc.RequestInfo) {
|
||||
timestampIntf := i.Request.Context().Value(requestTimestampKey)
|
||||
timestamp, ok := timestampIntf.(time.Time)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
durationMetricCount := apr.requestDurationCount.With(Labels{
|
||||
"method": i.Method,
|
||||
})
|
||||
durationMetricCount.Inc()
|
||||
|
||||
duration := time.Since(timestamp)
|
||||
durationMetricSum := apr.requestDurationSum.With(Labels{
|
||||
"method": i.Method,
|
||||
})
|
||||
durationMetricSum.Add(float64(duration))
|
||||
|
||||
if i.Error != nil {
|
||||
errMetric := apr.requestErrors.With(Labels{
|
||||
"method": i.Method,
|
||||
})
|
||||
errMetric.Inc()
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -37,10 +37,10 @@ func NewAveragerWithErrs(name, desc string, reg Registerer, errs *Errs) Averager
|
||||
}),
|
||||
}
|
||||
|
||||
if err := reg.Register(a.count); err != nil {
|
||||
if err := reg.Register(AsCollector(a.count)); err != nil {
|
||||
errs.Add(fmt.Errorf("%w: %w", ErrFailedRegistering, err))
|
||||
}
|
||||
if err := reg.Register(a.sum); err != nil {
|
||||
if err := reg.Register(AsCollector(a.sum)); err != nil {
|
||||
errs.Add(fmt.Errorf("%w: %w", ErrFailedRegistering, err))
|
||||
}
|
||||
return &a
|
||||
@@ -65,4 +65,4 @@ func AppendNamespace(namespace, name string) string {
|
||||
return name
|
||||
}
|
||||
return namespace + "_" + name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@ type MultiGatherer interface {
|
||||
Deregister(namespace string) bool
|
||||
}
|
||||
|
||||
// Gatherer aliases the Prometheus Gatherer interface.
|
||||
type Gatherer = prometheus.Gatherer
|
||||
|
||||
// MetricFamily aliases the Prometheus metric family type.
|
||||
type MetricFamily = dto.MetricFamily
|
||||
|
||||
// NewMultiGatherer returns a new MultiGatherer that merges metrics by namespace
|
||||
func NewMultiGatherer() MultiGatherer {
|
||||
return &multiGatherer{
|
||||
|
||||
@@ -3,9 +3,10 @@ module github.com/luxfi/metric
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/gorilla/rpc v1.2.1
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/prometheus/client_model v0.6.2
|
||||
github.com/prometheus/common v0.67.4
|
||||
github.com/prometheus/common v0.67.5
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
|
||||
@@ -1,22 +1,48 @@
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/gorilla/rpc v1.2.1 h1:yC+LMV5esttgpVvNORL/xX4jvTTEUE30UZhZ5JF7K9k=
|
||||
github.com/gorilla/rpc v1.2.1/go.mod h1:uNpOihAlF5xRFLuTYhfR0yfCTm0WTQSQttkMSptRfGk=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -0,0 +1,768 @@
|
||||
// Package metric provides high-performance metrics collection
|
||||
// Designed for high-performance with minimal allocations and no Prometheus dependency
|
||||
package metric
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// VictoriaCounter provides a high-performance counter without Prometheus dependency
|
||||
type VictoriaCounter struct {
|
||||
value uint64
|
||||
name string
|
||||
help string
|
||||
}
|
||||
|
||||
// NewVictoriaCounter creates a new VictoriaMetrics-style counter
|
||||
func NewVictoriaCounter(name, help string) *VictoriaCounter {
|
||||
return &VictoriaCounter{
|
||||
name: name,
|
||||
help: help,
|
||||
}
|
||||
}
|
||||
|
||||
// Inc increments the counter by 1
|
||||
func (vc *VictoriaCounter) Inc() {
|
||||
atomic.AddUint64(&vc.value, 1)
|
||||
}
|
||||
|
||||
// Add adds a value to the counter
|
||||
func (vc *VictoriaCounter) Add(val float64) {
|
||||
atomic.AddUint64(&vc.value, uint64(val))
|
||||
}
|
||||
|
||||
// Value returns the current value
|
||||
func (vc *VictoriaCounter) Value() uint64 {
|
||||
return atomic.LoadUint64(&vc.value)
|
||||
}
|
||||
|
||||
// Get returns the current value as float64.
|
||||
func (vc *VictoriaCounter) Get() float64 {
|
||||
return float64(atomic.LoadUint64(&vc.value))
|
||||
}
|
||||
|
||||
// String returns the counter in Prometheus exposition format
|
||||
func (vc *VictoriaCounter) String() string {
|
||||
return fmt.Sprintf("# HELP %s %s\n# TYPE %s counter\n%s %d", vc.name, vc.help, vc.name, vc.name, vc.Value())
|
||||
}
|
||||
|
||||
// VictoriaGauge provides a high-performance gauge without Prometheus dependency
|
||||
type VictoriaGauge struct {
|
||||
value int64 // Use int64 to handle negative values
|
||||
name string
|
||||
help string
|
||||
}
|
||||
|
||||
// NewVictoriaGauge creates a new VictoriaMetrics-style gauge
|
||||
func NewVictoriaGauge(name, help string) *VictoriaGauge {
|
||||
return &VictoriaGauge{
|
||||
name: name,
|
||||
help: help,
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets the gauge value
|
||||
func (vg *VictoriaGauge) Set(val float64) {
|
||||
atomic.StoreInt64(&vg.value, int64(math.Float64bits(val)))
|
||||
}
|
||||
|
||||
// Get returns the gauge value
|
||||
func (vg *VictoriaGauge) Get() float64 {
|
||||
return math.Float64frombits(uint64(atomic.LoadInt64(&vg.value)))
|
||||
}
|
||||
|
||||
// Inc increments the gauge by 1
|
||||
func (vg *VictoriaGauge) Inc() {
|
||||
for {
|
||||
oldVal := vg.Get()
|
||||
newVal := oldVal + 1
|
||||
oldBits := math.Float64bits(oldVal)
|
||||
newBits := math.Float64bits(newVal)
|
||||
if atomic.CompareAndSwapInt64(&vg.value, int64(oldBits), int64(newBits)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dec decrements the gauge by 1
|
||||
func (vg *VictoriaGauge) Dec() {
|
||||
for {
|
||||
oldVal := vg.Get()
|
||||
newVal := oldVal - 1
|
||||
oldBits := math.Float64bits(oldVal)
|
||||
newBits := math.Float64bits(newVal)
|
||||
if atomic.CompareAndSwapInt64(&vg.value, int64(oldBits), int64(newBits)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a value to the gauge
|
||||
func (vg *VictoriaGauge) Add(val float64) {
|
||||
for {
|
||||
oldVal := vg.Get()
|
||||
newVal := oldVal + val
|
||||
oldBits := math.Float64bits(oldVal)
|
||||
newBits := math.Float64bits(newVal)
|
||||
if atomic.CompareAndSwapInt64(&vg.value, int64(oldBits), int64(newBits)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sub subtracts a value from the gauge
|
||||
func (vg *VictoriaGauge) Sub(val float64) {
|
||||
for {
|
||||
oldVal := vg.Get()
|
||||
newVal := oldVal - val
|
||||
oldBits := math.Float64bits(oldVal)
|
||||
newBits := math.Float64bits(newVal)
|
||||
if atomic.CompareAndSwapInt64(&vg.value, int64(oldBits), int64(newBits)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the gauge in Prometheus exposition format
|
||||
func (vg *VictoriaGauge) String() string {
|
||||
return fmt.Sprintf("# HELP %s %s\n# TYPE %s gauge\n%s %f", vg.name, vg.help, vg.name, vg.name, vg.Get())
|
||||
}
|
||||
|
||||
// Value returns the current value.
|
||||
func (vg *VictoriaGauge) Value() float64 {
|
||||
return vg.Get()
|
||||
}
|
||||
|
||||
// VictoriaHistogram provides a high-performance histogram without Prometheus dependency
|
||||
type VictoriaHistogram struct {
|
||||
name string
|
||||
help string
|
||||
buckets []float64
|
||||
bucketCounts []uint64 // Count of values in each bucket
|
||||
count uint64 // Total count of observations
|
||||
sum float64 // Sum of all observations
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewVictoriaHistogram creates a new VictoriaMetrics-style histogram
|
||||
func NewVictoriaHistogram(name, help string, buckets []float64) *VictoriaHistogram {
|
||||
// Sort buckets to ensure they're in ascending order
|
||||
sortedBuckets := make([]float64, len(buckets))
|
||||
copy(sortedBuckets, buckets)
|
||||
for i := 0; i < len(sortedBuckets)-1; i++ {
|
||||
for j := i + 1; j < len(sortedBuckets); j++ {
|
||||
if sortedBuckets[i] > sortedBuckets[j] {
|
||||
sortedBuckets[i], sortedBuckets[j] = sortedBuckets[j], sortedBuckets[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &VictoriaHistogram{
|
||||
name: name,
|
||||
help: help,
|
||||
buckets: sortedBuckets,
|
||||
bucketCounts: make([]uint64, len(sortedBuckets)+1), // +1 for +Inf bucket
|
||||
}
|
||||
}
|
||||
|
||||
// Observe records a value in the histogram
|
||||
func (vh *VictoriaHistogram) Observe(val float64) {
|
||||
vh.mu.Lock()
|
||||
defer vh.mu.Unlock()
|
||||
|
||||
// Find the appropriate bucket
|
||||
bucketIdx := len(vh.buckets) // Default to +Inf bucket
|
||||
for i, bucket := range vh.buckets {
|
||||
if val <= bucket {
|
||||
bucketIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Increment the appropriate bucket count
|
||||
atomic.AddUint64(&vh.bucketCounts[bucketIdx], 1)
|
||||
|
||||
// Increment total count
|
||||
atomic.AddUint64(&vh.count, 1)
|
||||
|
||||
// Add to sum
|
||||
for {
|
||||
oldSum := vh.sum
|
||||
newSum := oldSum + val
|
||||
if atomic.CompareAndSwapUint64((*uint64)(unsafe.Pointer(&vh.sum)), math.Float64bits(oldSum), math.Float64bits(newSum)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetBucketCounts returns the current bucket counts
|
||||
func (vh *VictoriaHistogram) GetBucketCounts() []uint64 {
|
||||
vh.mu.RLock()
|
||||
defer vh.mu.RUnlock()
|
||||
|
||||
result := make([]uint64, len(vh.bucketCounts))
|
||||
for i := range vh.bucketCounts {
|
||||
result[i] = atomic.LoadUint64(&vh.bucketCounts[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetCount returns the total count
|
||||
func (vh *VictoriaHistogram) GetCount() uint64 {
|
||||
return atomic.LoadUint64(&vh.count)
|
||||
}
|
||||
|
||||
// GetSum returns the sum
|
||||
func (vh *VictoriaHistogram) GetSum() float64 {
|
||||
return vh.sum
|
||||
}
|
||||
|
||||
// String returns the histogram in Prometheus exposition format
|
||||
func (vh *VictoriaHistogram) String() string {
|
||||
vh.mu.RLock()
|
||||
defer vh.mu.RUnlock()
|
||||
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(fmt.Sprintf("# HELP %s %s\n", vh.name, vh.help))
|
||||
sb.WriteString(fmt.Sprintf("# TYPE %s histogram\n", vh.name))
|
||||
|
||||
// Write bucket counts
|
||||
cumulative := uint64(0)
|
||||
for i, bucket := range vh.buckets {
|
||||
cumulative += atomic.LoadUint64(&vh.bucketCounts[i])
|
||||
sb.WriteString(fmt.Sprintf("%s_bucket{le=\"%g\"} %d\n", vh.name, bucket, cumulative))
|
||||
}
|
||||
|
||||
// Write +Inf bucket
|
||||
cumulative += atomic.LoadUint64(&vh.bucketCounts[len(vh.buckets)])
|
||||
sb.WriteString(fmt.Sprintf("%s_bucket{le=\"+Inf\"} %d\n", vh.name, cumulative))
|
||||
|
||||
// Write count and sum
|
||||
sb.WriteString(fmt.Sprintf("%s_count %d\n", vh.name, atomic.LoadUint64(&vh.count)))
|
||||
sb.WriteString(fmt.Sprintf("%s_sum %g\n", vh.name, vh.sum))
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// VictoriaSummary provides a high-performance summary without Prometheus dependency
|
||||
type VictoriaSummary struct {
|
||||
name string
|
||||
help string
|
||||
count uint64
|
||||
sum float64
|
||||
quantiles map[float64]float64 // Quantile -> value
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewVictoriaSummary creates a new VictoriaMetrics-style summary
|
||||
func NewVictoriaSummary(name, help string) *VictoriaSummary {
|
||||
return &VictoriaSummary{
|
||||
name: name,
|
||||
help: help,
|
||||
quantiles: make(map[float64]float64),
|
||||
}
|
||||
}
|
||||
|
||||
// Observe records a value in the summary
|
||||
func (vs *VictoriaSummary) Observe(val float64) {
|
||||
vs.mu.Lock()
|
||||
defer vs.mu.Unlock()
|
||||
|
||||
atomic.AddUint64(&vs.count, 1)
|
||||
|
||||
// Add to sum atomically
|
||||
for {
|
||||
oldSum := vs.sum
|
||||
newSum := oldSum + val
|
||||
if atomic.CompareAndSwapUint64((*uint64)(unsafe.Pointer(&vs.sum)), math.Float64bits(oldSum), math.Float64bits(newSum)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Note: In a real VictoriaMetrics implementation, quantiles would be calculated differently
|
||||
// This is a simplified version for demonstration purposes
|
||||
}
|
||||
|
||||
// GetCount returns the total count
|
||||
func (vs *VictoriaSummary) GetCount() uint64 {
|
||||
return atomic.LoadUint64(&vs.count)
|
||||
}
|
||||
|
||||
// GetSum returns the sum
|
||||
func (vs *VictoriaSummary) GetSum() float64 {
|
||||
vs.mu.RLock()
|
||||
defer vs.mu.RUnlock()
|
||||
return vs.sum
|
||||
}
|
||||
|
||||
// String returns the summary in Prometheus exposition format
|
||||
func (vs *VictoriaSummary) String() string {
|
||||
vs.mu.RLock()
|
||||
defer vs.mu.RUnlock()
|
||||
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(fmt.Sprintf("# HELP %s %s\n", vs.name, vs.help))
|
||||
sb.WriteString(fmt.Sprintf("# TYPE %s summary\n", vs.name))
|
||||
|
||||
// Write count and sum
|
||||
sb.WriteString(fmt.Sprintf("%s_count %d\n", vs.name, atomic.LoadUint64(&vs.count)))
|
||||
sb.WriteString(fmt.Sprintf("%s_sum %g\n", vs.name, vs.sum))
|
||||
|
||||
// Write quantiles (simplified - in real implementation, quantiles would be calculated properly)
|
||||
for quantile, value := range vs.quantiles {
|
||||
sb.WriteString(fmt.Sprintf("%s{quantile=\"%g\"} %g\n", vs.name, quantile, value))
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// HighPerfMetricsRegistry provides a registry for high-performance metrics
|
||||
type HighPerfMetricsRegistry struct {
|
||||
counters map[string]*VictoriaCounter
|
||||
gauges map[string]*VictoriaGauge
|
||||
histograms map[string]*VictoriaHistogram
|
||||
summaries map[string]*VictoriaSummary
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewHighPerfMetricsRegistry creates a new high-performance registry
|
||||
func NewHighPerfMetricsRegistry() *HighPerfMetricsRegistry {
|
||||
return &HighPerfMetricsRegistry{
|
||||
counters: make(map[string]*VictoriaCounter),
|
||||
gauges: make(map[string]*VictoriaGauge),
|
||||
histograms: make(map[string]*VictoriaHistogram),
|
||||
summaries: make(map[string]*VictoriaSummary),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterCounter registers a counter
|
||||
func (hpr *HighPerfMetricsRegistry) RegisterCounter(name string, counter *VictoriaCounter) {
|
||||
hpr.mu.Lock()
|
||||
defer hpr.mu.Unlock()
|
||||
hpr.counters[name] = counter
|
||||
}
|
||||
|
||||
// RegisterGauge registers a gauge
|
||||
func (hpr *HighPerfMetricsRegistry) RegisterGauge(name string, gauge *VictoriaGauge) {
|
||||
hpr.mu.Lock()
|
||||
defer hpr.mu.Unlock()
|
||||
hpr.gauges[name] = gauge
|
||||
}
|
||||
|
||||
// RegisterHistogram registers a histogram
|
||||
func (hpr *HighPerfMetricsRegistry) RegisterHistogram(name string, histogram *VictoriaHistogram) {
|
||||
hpr.mu.Lock()
|
||||
defer hpr.mu.Unlock()
|
||||
hpr.histograms[name] = histogram
|
||||
}
|
||||
|
||||
// RegisterSummary registers a summary
|
||||
func (hpr *HighPerfMetricsRegistry) RegisterSummary(name string, summary *VictoriaSummary) {
|
||||
hpr.mu.Lock()
|
||||
defer hpr.mu.Unlock()
|
||||
hpr.summaries[name] = summary
|
||||
}
|
||||
|
||||
// GetMetrics returns all metrics in Prometheus exposition format
|
||||
func (hpr *HighPerfMetricsRegistry) GetMetrics() string {
|
||||
hpr.mu.RLock()
|
||||
defer hpr.mu.RUnlock()
|
||||
|
||||
var sb strings.Builder
|
||||
|
||||
// Add all counters
|
||||
for _, counter := range hpr.counters {
|
||||
sb.WriteString(counter.String())
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
// Add all gauges
|
||||
for _, gauge := range hpr.gauges {
|
||||
sb.WriteString(gauge.String())
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
// Add all histograms
|
||||
for _, histogram := range hpr.histograms {
|
||||
sb.WriteString(histogram.String())
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
// Add all summaries
|
||||
for _, summary := range hpr.summaries {
|
||||
sb.WriteString(summary.String())
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// VictoriaTimingMetric provides timing functionality similar to VictoriaMetrics
|
||||
type VictoriaTimingMetric struct {
|
||||
histogram *VictoriaHistogram
|
||||
start time.Time
|
||||
}
|
||||
|
||||
// NewVictoriaTimingMetric creates a new timing metric
|
||||
func NewVictoriaTimingMetric(histogram *VictoriaHistogram) *VictoriaTimingMetric {
|
||||
return &VictoriaTimingMetric{
|
||||
histogram: histogram,
|
||||
start: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the timing and records the duration
|
||||
func (vtm *VictoriaTimingMetric) Stop() {
|
||||
duration := time.Since(vtm.start).Seconds()
|
||||
vtm.histogram.Observe(duration)
|
||||
}
|
||||
|
||||
// Reset resets the timing
|
||||
func (vtm *VictoriaTimingMetric) Reset() {
|
||||
vtm.start = time.Now()
|
||||
}
|
||||
|
||||
// Duration returns the current duration
|
||||
func (vtm *VictoriaTimingMetric) Duration() time.Duration {
|
||||
return time.Since(vtm.start)
|
||||
}
|
||||
|
||||
// Start starts the timer and returns a function to stop it
|
||||
func (vtm *VictoriaTimingMetric) Start() func() {
|
||||
vtm.start = time.Now()
|
||||
return vtm.Stop
|
||||
}
|
||||
|
||||
// ObserveTime observes the given duration
|
||||
func (vtm *VictoriaTimingMetric) ObserveTime(d time.Duration) {
|
||||
vtm.histogram.Observe(d.Seconds())
|
||||
}
|
||||
|
||||
// HighPerfMetricsFactory creates high-performance metrics
|
||||
type HighPerfMetricsFactory struct {
|
||||
registry *VictoriaMetricsRegistry
|
||||
}
|
||||
|
||||
// NewHighPerfMetricsFactory creates a factory that produces high-performance metrics
|
||||
func NewHighPerfMetricsFactory() *HighPerfMetricsFactory {
|
||||
return &HighPerfMetricsFactory{
|
||||
registry: NewVictoriaMetricsRegistry(),
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new metrics instance with the given namespace.
|
||||
func (hpf *HighPerfMetricsFactory) New(namespace string) Metrics {
|
||||
return &highPerfMetrics{
|
||||
namespace: namespace,
|
||||
factory: hpf,
|
||||
}
|
||||
}
|
||||
|
||||
// NewWithRegistry creates a new metrics instance, ignoring the registry for high-perf metrics.
|
||||
func (hpf *HighPerfMetricsFactory) NewWithRegistry(namespace string, _ Registry) Metrics {
|
||||
return &highPerfMetrics{
|
||||
namespace: namespace,
|
||||
factory: hpf,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCounter creates a new high-performance counter
|
||||
func (hpf *HighPerfMetricsFactory) NewCounter(name, help string) Counter {
|
||||
counter := NewVictoriaCounter(name, help)
|
||||
hpf.registry.RegisterCounter(name, counter)
|
||||
return counter
|
||||
}
|
||||
|
||||
// NewGauge creates a new high-performance gauge
|
||||
func (hpf *HighPerfMetricsFactory) NewGauge(name, help string) Gauge {
|
||||
gauge := NewVictoriaGauge(name, help)
|
||||
hpf.registry.RegisterGauge(name, gauge)
|
||||
return gauge
|
||||
}
|
||||
|
||||
// NewHistogram creates a new high-performance histogram
|
||||
func (hpf *HighPerfMetricsFactory) NewHistogram(name, help string, buckets []float64) Histogram {
|
||||
histogram := NewVictoriaHistogram(name, help, buckets)
|
||||
hpf.registry.RegisterHistogram(name, histogram)
|
||||
return histogram
|
||||
}
|
||||
|
||||
// NewSummary creates a new high-performance summary
|
||||
func (hpf *HighPerfMetricsFactory) NewSummary(name, help string, _ map[float64]float64) Summary {
|
||||
summary := NewVictoriaSummary(name, help)
|
||||
hpf.registry.RegisterSummary(name, summary)
|
||||
return summary
|
||||
}
|
||||
|
||||
// GetRegistry returns the underlying registry
|
||||
func (hpf *HighPerfMetricsFactory) GetRegistry() *VictoriaMetricsRegistry {
|
||||
return hpf.registry
|
||||
}
|
||||
|
||||
type highPerfMetrics struct {
|
||||
namespace string
|
||||
factory *HighPerfMetricsFactory
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) NewCounter(name, help string) Counter {
|
||||
return m.factory.NewCounter(prefixedName(m.namespace, name), help)
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) NewCounterVec(name, help string, labelNames []string) CounterVec {
|
||||
return newHighPerfCounterVec(m.factory, prefixedName(m.namespace, name), help, labelNames)
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) NewGauge(name, help string) Gauge {
|
||||
return m.factory.NewGauge(prefixedName(m.namespace, name), help)
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) NewGaugeVec(name, help string, labelNames []string) GaugeVec {
|
||||
return newHighPerfGaugeVec(m.factory, prefixedName(m.namespace, name), help, labelNames)
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) NewHistogram(name, help string, buckets []float64) Histogram {
|
||||
return m.factory.NewHistogram(prefixedName(m.namespace, name), help, buckets)
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) NewHistogramVec(name, help string, labelNames []string, buckets []float64) HistogramVec {
|
||||
return newHighPerfHistogramVec(m.factory, prefixedName(m.namespace, name), help, labelNames, buckets)
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) NewSummary(name, help string, objectives map[float64]float64) Summary {
|
||||
return m.factory.NewSummary(prefixedName(m.namespace, name), help, objectives)
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) NewSummaryVec(name, help string, labelNames []string, objectives map[float64]float64) SummaryVec {
|
||||
return newHighPerfSummaryVec(m.factory, prefixedName(m.namespace, name), help, labelNames, objectives)
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) Registry() Registry {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *highPerfMetrics) PrometheusRegistry() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func prefixedName(namespace, name string) string {
|
||||
if namespace == "" {
|
||||
return name
|
||||
}
|
||||
return namespace + "_" + name
|
||||
}
|
||||
|
||||
type highPerfCounterVec struct {
|
||||
factory *HighPerfMetricsFactory
|
||||
name string
|
||||
help string
|
||||
labelNames []string
|
||||
mu sync.Mutex
|
||||
counters map[string]Counter
|
||||
}
|
||||
|
||||
func newHighPerfCounterVec(factory *HighPerfMetricsFactory, name, help string, labelNames []string) *highPerfCounterVec {
|
||||
return &highPerfCounterVec{
|
||||
factory: factory,
|
||||
name: name,
|
||||
help: help,
|
||||
labelNames: append([]string(nil), labelNames...),
|
||||
counters: make(map[string]Counter),
|
||||
}
|
||||
}
|
||||
|
||||
func (v *highPerfCounterVec) With(labels Labels) Counter {
|
||||
key := labelsKey(v.labelNames, labels)
|
||||
return v.getOrCreate(key)
|
||||
}
|
||||
|
||||
func (v *highPerfCounterVec) WithLabelValues(values ...string) Counter {
|
||||
key := valuesKey(v.labelNames, values)
|
||||
return v.getOrCreate(key)
|
||||
}
|
||||
|
||||
func (v *highPerfCounterVec) getOrCreate(key string) Counter {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
if c, ok := v.counters[key]; ok {
|
||||
return c
|
||||
}
|
||||
counter := v.factory.NewCounter(v.name+key, v.help)
|
||||
v.counters[key] = counter
|
||||
return counter
|
||||
}
|
||||
|
||||
type highPerfGaugeVec struct {
|
||||
factory *HighPerfMetricsFactory
|
||||
name string
|
||||
help string
|
||||
labelNames []string
|
||||
mu sync.Mutex
|
||||
gauges map[string]Gauge
|
||||
}
|
||||
|
||||
func newHighPerfGaugeVec(factory *HighPerfMetricsFactory, name, help string, labelNames []string) *highPerfGaugeVec {
|
||||
return &highPerfGaugeVec{
|
||||
factory: factory,
|
||||
name: name,
|
||||
help: help,
|
||||
labelNames: append([]string(nil), labelNames...),
|
||||
gauges: make(map[string]Gauge),
|
||||
}
|
||||
}
|
||||
|
||||
func (v *highPerfGaugeVec) With(labels Labels) Gauge {
|
||||
key := labelsKey(v.labelNames, labels)
|
||||
return v.getOrCreate(key)
|
||||
}
|
||||
|
||||
func (v *highPerfGaugeVec) WithLabelValues(values ...string) Gauge {
|
||||
key := valuesKey(v.labelNames, values)
|
||||
return v.getOrCreate(key)
|
||||
}
|
||||
|
||||
func (v *highPerfGaugeVec) getOrCreate(key string) Gauge {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
if g, ok := v.gauges[key]; ok {
|
||||
return g
|
||||
}
|
||||
gauge := v.factory.NewGauge(v.name+key, v.help)
|
||||
v.gauges[key] = gauge
|
||||
return gauge
|
||||
}
|
||||
|
||||
type highPerfHistogramVec struct {
|
||||
factory *HighPerfMetricsFactory
|
||||
name string
|
||||
help string
|
||||
labelNames []string
|
||||
buckets []float64
|
||||
mu sync.Mutex
|
||||
histograms map[string]Histogram
|
||||
}
|
||||
|
||||
func newHighPerfHistogramVec(factory *HighPerfMetricsFactory, name, help string, labelNames []string, buckets []float64) *highPerfHistogramVec {
|
||||
return &highPerfHistogramVec{
|
||||
factory: factory,
|
||||
name: name,
|
||||
help: help,
|
||||
labelNames: append([]string(nil), labelNames...),
|
||||
buckets: append([]float64(nil), buckets...),
|
||||
histograms: make(map[string]Histogram),
|
||||
}
|
||||
}
|
||||
|
||||
func (v *highPerfHistogramVec) With(labels Labels) Histogram {
|
||||
key := labelsKey(v.labelNames, labels)
|
||||
return v.getOrCreate(key)
|
||||
}
|
||||
|
||||
func (v *highPerfHistogramVec) WithLabelValues(values ...string) Histogram {
|
||||
key := valuesKey(v.labelNames, values)
|
||||
return v.getOrCreate(key)
|
||||
}
|
||||
|
||||
func (v *highPerfHistogramVec) getOrCreate(key string) Histogram {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
if h, ok := v.histograms[key]; ok {
|
||||
return h
|
||||
}
|
||||
histogram := v.factory.NewHistogram(v.name+key, v.help, v.buckets)
|
||||
v.histograms[key] = histogram
|
||||
return histogram
|
||||
}
|
||||
|
||||
type highPerfSummaryVec struct {
|
||||
factory *HighPerfMetricsFactory
|
||||
name string
|
||||
help string
|
||||
labelNames []string
|
||||
objectives map[float64]float64
|
||||
mu sync.Mutex
|
||||
summaries map[string]Summary
|
||||
}
|
||||
|
||||
func newHighPerfSummaryVec(factory *HighPerfMetricsFactory, name, help string, labelNames []string, objectives map[float64]float64) *highPerfSummaryVec {
|
||||
objCopy := make(map[float64]float64, len(objectives))
|
||||
for k, v := range objectives {
|
||||
objCopy[k] = v
|
||||
}
|
||||
return &highPerfSummaryVec{
|
||||
factory: factory,
|
||||
name: name,
|
||||
help: help,
|
||||
labelNames: append([]string(nil), labelNames...),
|
||||
objectives: objCopy,
|
||||
summaries: make(map[string]Summary),
|
||||
}
|
||||
}
|
||||
|
||||
func (v *highPerfSummaryVec) With(labels Labels) Summary {
|
||||
key := labelsKey(v.labelNames, labels)
|
||||
return v.getOrCreate(key)
|
||||
}
|
||||
|
||||
func (v *highPerfSummaryVec) WithLabelValues(values ...string) Summary {
|
||||
key := valuesKey(v.labelNames, values)
|
||||
return v.getOrCreate(key)
|
||||
}
|
||||
|
||||
func (v *highPerfSummaryVec) getOrCreate(key string) Summary {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
if s, ok := v.summaries[key]; ok {
|
||||
return s
|
||||
}
|
||||
summary := v.factory.NewSummary(v.name+key, v.help, v.objectives)
|
||||
v.summaries[key] = summary
|
||||
return summary
|
||||
}
|
||||
|
||||
func labelsKey(labelNames []string, labels Labels) string {
|
||||
if len(labelNames) == 0 {
|
||||
return ""
|
||||
}
|
||||
var sb strings.Builder
|
||||
sb.WriteString("{")
|
||||
for i, name := range labelNames {
|
||||
if i > 0 {
|
||||
sb.WriteString(",")
|
||||
}
|
||||
sb.WriteString(name)
|
||||
sb.WriteString("=\"")
|
||||
sb.WriteString(labels[name])
|
||||
sb.WriteString("\"")
|
||||
}
|
||||
sb.WriteString("}")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func valuesKey(labelNames []string, values []string) string {
|
||||
if len(labelNames) == 0 {
|
||||
return ""
|
||||
}
|
||||
var sb strings.Builder
|
||||
sb.WriteString("{")
|
||||
for i, name := range labelNames {
|
||||
if i > 0 {
|
||||
sb.WriteString(",")
|
||||
}
|
||||
sb.WriteString(name)
|
||||
sb.WriteString("=\"")
|
||||
if i < len(values) {
|
||||
sb.WriteString(values[i])
|
||||
}
|
||||
sb.WriteString("\"")
|
||||
}
|
||||
sb.WriteString("}")
|
||||
return sb.String()
|
||||
}
|
||||
@@ -8,12 +8,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
// Counter is a metric that can only increase
|
||||
type Counter interface {
|
||||
prometheus.Collector
|
||||
// Inc increments the counter by 1
|
||||
Inc()
|
||||
// Add increments the counter by the given value
|
||||
@@ -24,7 +22,6 @@ type Counter interface {
|
||||
|
||||
// Gauge is a metric that can increase or decrease
|
||||
type Gauge interface {
|
||||
prometheus.Collector
|
||||
// Set sets the gauge to the given value
|
||||
Set(float64)
|
||||
// Inc increments the gauge by 1
|
||||
@@ -41,14 +38,12 @@ type Gauge interface {
|
||||
|
||||
// Histogram samples observations and counts them in configurable buckets
|
||||
type Histogram interface {
|
||||
prometheus.Collector
|
||||
// Observe adds a single observation to the histogram
|
||||
Observe(float64)
|
||||
}
|
||||
|
||||
// Summary captures individual observations and provides quantiles
|
||||
type Summary interface {
|
||||
prometheus.Collector
|
||||
// Observe adds a single observation to the summary
|
||||
Observe(float64)
|
||||
}
|
||||
@@ -64,92 +59,30 @@ type Timer interface {
|
||||
// Labels represents a set of label key-value pairs
|
||||
type Labels map[string]string
|
||||
|
||||
// Registerer is an alias for prometheus.Registerer
|
||||
type Registerer = prometheus.Registerer
|
||||
|
||||
// Gatherer is an alias for prometheus.Gatherer
|
||||
type Gatherer = prometheus.Gatherer
|
||||
|
||||
// MetricFamily alias for dto.MetricFamily
|
||||
type MetricFamily = dto.MetricFamily
|
||||
|
||||
// Registry is an alias for prometheus.Registry to keep it internal
|
||||
// We use prometheus.Registry directly but alias it to avoid external dependencies
|
||||
type Registry = *prometheus.Registry
|
||||
|
||||
// Collector is an alias for prometheus.Collector
|
||||
type Collector = prometheus.Collector
|
||||
|
||||
// Metric is an alias for prometheus.Metric
|
||||
type Metric = prometheus.Metric
|
||||
|
||||
// Desc is an alias for prometheus.Desc
|
||||
type Desc = *prometheus.Desc
|
||||
|
||||
// Metrics is the main interface for creating metrics
|
||||
type Metrics interface {
|
||||
// NewCounter creates a new counter
|
||||
NewCounter(name, help string) Counter
|
||||
// NewCounterVec creates a new counter vector
|
||||
NewCounterVec(name, help string, labelNames []string) CounterVec
|
||||
|
||||
// NewGauge creates a new gauge
|
||||
NewGauge(name, help string) Gauge
|
||||
// NewGaugeVec creates a new gauge vector
|
||||
NewGaugeVec(name, help string, labelNames []string) GaugeVec
|
||||
|
||||
// NewHistogram creates a new histogram
|
||||
NewHistogram(name, help string, buckets []float64) Histogram
|
||||
// NewHistogramVec creates a new histogram vector
|
||||
NewHistogramVec(name, help string, labelNames []string, buckets []float64) HistogramVec
|
||||
|
||||
// NewSummary creates a new summary
|
||||
NewSummary(name, help string, objectives map[float64]float64) Summary
|
||||
// NewSummaryVec creates a new summary vector
|
||||
NewSummaryVec(name, help string, labelNames []string, objectives map[float64]float64) SummaryVec
|
||||
|
||||
// Registry returns the underlying registry
|
||||
Registry() Registry
|
||||
|
||||
// PrometheusRegistry returns the prometheus registerer for compatibility
|
||||
// PrometheusRegistry returns the underlying Prometheus registry, if any
|
||||
PrometheusRegistry() interface{}
|
||||
}
|
||||
|
||||
// CounterVec is a vector of counters
|
||||
type CounterVec interface {
|
||||
prometheus.Collector
|
||||
// With returns a counter with the given label values
|
||||
With(Labels) Counter
|
||||
// WithLabelValues returns a counter with the given label values
|
||||
WithLabelValues(labelValues ...string) Counter
|
||||
}
|
||||
|
||||
// GaugeVec is a vector of gauges
|
||||
type GaugeVec interface {
|
||||
prometheus.Collector
|
||||
// With returns a gauge with the given label values
|
||||
With(Labels) Gauge
|
||||
// WithLabelValues returns a gauge with the given label values
|
||||
WithLabelValues(labelValues ...string) Gauge
|
||||
}
|
||||
|
||||
// HistogramVec is a vector of histograms
|
||||
type HistogramVec interface {
|
||||
prometheus.Collector
|
||||
// With returns a histogram with the given label values
|
||||
With(Labels) Histogram
|
||||
// WithLabelValues returns a histogram with the given label values
|
||||
WithLabelValues(labelValues ...string) Histogram
|
||||
}
|
||||
|
||||
// SummaryVec is a vector of summaries
|
||||
type SummaryVec interface {
|
||||
// With returns a summary with the given label values
|
||||
With(Labels) Summary
|
||||
// WithLabelValues returns a summary with the given label values
|
||||
WithLabelValues(labelValues ...string) Summary
|
||||
}
|
||||
|
||||
// Factory creates new metrics instances
|
||||
type Factory interface {
|
||||
// New creates a new metrics instance with the given namespace
|
||||
@@ -158,6 +91,30 @@ type Factory interface {
|
||||
NewWithRegistry(namespace string, registry Registry) Metrics
|
||||
}
|
||||
|
||||
// CounterVec is a labeled counter collection
|
||||
type CounterVec interface {
|
||||
With(Labels) Counter
|
||||
WithLabelValues(...string) Counter
|
||||
}
|
||||
|
||||
// GaugeVec is a labeled gauge collection
|
||||
type GaugeVec interface {
|
||||
With(Labels) Gauge
|
||||
WithLabelValues(...string) Gauge
|
||||
}
|
||||
|
||||
// HistogramVec is a labeled histogram collection
|
||||
type HistogramVec interface {
|
||||
With(Labels) Histogram
|
||||
WithLabelValues(...string) Histogram
|
||||
}
|
||||
|
||||
// SummaryVec is a labeled summary collection
|
||||
type SummaryVec interface {
|
||||
With(Labels) Summary
|
||||
WithLabelValues(...string) Summary
|
||||
}
|
||||
|
||||
// MetricsHTTPHandler handles HTTP requests for metrics
|
||||
type MetricsHTTPHandler interface {
|
||||
// ServeHTTP handles an HTTP request
|
||||
@@ -185,7 +142,7 @@ type Request interface {
|
||||
}
|
||||
|
||||
// Global factory instance
|
||||
var defaultFactory Factory = NewPrometheusFactory()
|
||||
var defaultFactory Factory = NewHighPerfMetricsFactory()
|
||||
|
||||
// SetFactory sets the global metrics factory
|
||||
func SetFactory(factory Factory) {
|
||||
@@ -197,50 +154,31 @@ func New(namespace string) Metrics {
|
||||
return defaultFactory.New(namespace)
|
||||
}
|
||||
|
||||
// NewWithRegistry creates a new metrics instance with a custom registry
|
||||
// NewWithRegistry creates a new metrics instance with the provided registry.
|
||||
// If registry is nil, it falls back to the default factory.
|
||||
func NewWithRegistry(namespace string, registry Registry) Metrics {
|
||||
return defaultFactory.NewWithRegistry(namespace, registry)
|
||||
if registry == nil {
|
||||
return New(namespace)
|
||||
}
|
||||
return NewPrometheusFactoryWithRegistry(registry).New(namespace)
|
||||
}
|
||||
|
||||
// Export prometheus types
|
||||
type (
|
||||
CounterOpts = prometheus.CounterOpts
|
||||
GaugeOpts = prometheus.GaugeOpts
|
||||
HistogramOpts = prometheus.HistogramOpts
|
||||
SummaryOpts = prometheus.SummaryOpts
|
||||
Gatherers = prometheus.Gatherers
|
||||
)
|
||||
|
||||
// Constructor functions that return wrapped types
|
||||
// NewCounter creates a new high-performance counter
|
||||
func NewCounter(opts CounterOpts) Counter {
|
||||
return WrapPrometheusCounter(prometheus.NewCounter(opts))
|
||||
}
|
||||
|
||||
func NewCounterVec(opts CounterOpts, labelNames []string) CounterVec {
|
||||
return WrapPrometheusCounterVec(prometheus.NewCounterVec(opts, labelNames))
|
||||
return NewCounterWithOpts(opts)
|
||||
}
|
||||
|
||||
// NewGauge creates a new high-performance gauge
|
||||
func NewGauge(opts GaugeOpts) Gauge {
|
||||
return WrapPrometheusGauge(prometheus.NewGauge(opts))
|
||||
return NewGaugeWithOpts(opts)
|
||||
}
|
||||
|
||||
func NewGaugeVec(opts GaugeOpts, labelNames []string) GaugeVec {
|
||||
return WrapPrometheusGaugeVec(prometheus.NewGaugeVec(opts, labelNames))
|
||||
// NewHistogram creates a new high-performance histogram
|
||||
func NewHistogram(opts HistogramOpts) Histogram {
|
||||
return WrapPrometheusHistogram(prometheus.NewHistogram(opts))
|
||||
}
|
||||
|
||||
func NewHistogramVec(opts HistogramOpts, labelNames []string) HistogramVec {
|
||||
return WrapPrometheusHistogramVec(prometheus.NewHistogramVec(opts, labelNames))
|
||||
// NewSummary creates a new high-performance summary
|
||||
func NewSummary(opts SummaryOpts) Summary {
|
||||
return WrapPrometheusSummary(prometheus.NewSummary(opts))
|
||||
}
|
||||
|
||||
// Keep these as direct aliases since they don't need wrapping
|
||||
var (
|
||||
NewHistogram = prometheus.NewHistogram
|
||||
NewSummary = prometheus.NewSummary
|
||||
NewSummaryVec = prometheus.NewSummaryVec
|
||||
NewRegistry = prometheus.NewRegistry
|
||||
NewDesc = prometheus.NewDesc
|
||||
MustNewConstMetric = prometheus.MustNewConstMetric
|
||||
Register = prometheus.Register
|
||||
MustRegister = prometheus.MustRegister
|
||||
Unregister = prometheus.Unregister
|
||||
)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package metric
|
||||
|
||||
const (
|
||||
NamespaceSeparatorByte = '_'
|
||||
NamespaceSeparator = string(NamespaceSeparatorByte)
|
||||
)
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
// Package metric provides high-performance optimized metrics collection
|
||||
// Designed for high-performance with minimal allocations and no Prometheus dependency
|
||||
package metric
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DefBuckets defines default histogram buckets similar to VictoriaMetrics
|
||||
var DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
|
||||
|
||||
// OptimizedCounter provides a high-performance counter
|
||||
// Uses atomic operations to avoid locking overhead
|
||||
type OptimizedCounter struct {
|
||||
value uint64
|
||||
name string
|
||||
help string
|
||||
}
|
||||
|
||||
// NewOptimizedCounter creates a new optimized counter
|
||||
func NewOptimizedCounter(name, help string) *OptimizedCounter {
|
||||
return &OptimizedCounter{
|
||||
name: name,
|
||||
help: help,
|
||||
}
|
||||
}
|
||||
|
||||
// Inc increments the counter by 1
|
||||
func (c *OptimizedCounter) Inc() {
|
||||
atomic.AddUint64(&c.value, 1)
|
||||
}
|
||||
|
||||
// Add adds a value to the counter
|
||||
func (c *OptimizedCounter) Add(val float64) {
|
||||
atomic.AddUint64(&c.value, uint64(val))
|
||||
}
|
||||
|
||||
// Value returns the current value
|
||||
func (c *OptimizedCounter) Value() uint64 {
|
||||
return atomic.LoadUint64(&c.value)
|
||||
}
|
||||
|
||||
// Get returns the current value
|
||||
func (c *OptimizedCounter) Get() float64 {
|
||||
return float64(atomic.LoadUint64(&c.value))
|
||||
}
|
||||
|
||||
// OptimizedGauge provides a high-performance gauge
|
||||
type OptimizedGauge struct {
|
||||
value int64 // Use int64 to handle negative values
|
||||
name string
|
||||
help string
|
||||
}
|
||||
|
||||
// NewOptimizedGauge creates a new optimized gauge
|
||||
func NewOptimizedGauge(name, help string) *OptimizedGauge {
|
||||
return &OptimizedGauge{
|
||||
name: name,
|
||||
help: help,
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets the gauge value
|
||||
func (g *OptimizedGauge) Set(val float64) {
|
||||
atomic.StoreInt64(&g.value, int64(math.Float64bits(val)))
|
||||
}
|
||||
|
||||
// Get returns the gauge value
|
||||
func (g *OptimizedGauge) Get() float64 {
|
||||
return math.Float64frombits(uint64(atomic.LoadInt64(&g.value)))
|
||||
}
|
||||
|
||||
// Inc increments the gauge by 1
|
||||
func (g *OptimizedGauge) Inc() {
|
||||
for {
|
||||
oldVal := g.Get()
|
||||
newVal := oldVal + 1
|
||||
oldBits := math.Float64bits(oldVal)
|
||||
newBits := math.Float64bits(newVal)
|
||||
if atomic.CompareAndSwapInt64(&g.value, int64(oldBits), int64(newBits)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dec decrements the gauge by 1
|
||||
func (g *OptimizedGauge) Dec() {
|
||||
for {
|
||||
oldVal := g.Get()
|
||||
newVal := oldVal - 1
|
||||
oldBits := math.Float64bits(oldVal)
|
||||
newBits := math.Float64bits(newVal)
|
||||
if atomic.CompareAndSwapInt64(&g.value, int64(oldBits), int64(newBits)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a value to the gauge
|
||||
func (g *OptimizedGauge) Add(val float64) {
|
||||
for {
|
||||
oldVal := g.Get()
|
||||
newVal := oldVal + val
|
||||
oldBits := math.Float64bits(oldVal)
|
||||
newBits := math.Float64bits(newVal)
|
||||
if atomic.CompareAndSwapInt64(&g.value, int64(oldBits), int64(newBits)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sub subtracts a value from the gauge
|
||||
func (g *OptimizedGauge) Sub(val float64) {
|
||||
for {
|
||||
oldVal := g.Get()
|
||||
newVal := oldVal - val
|
||||
oldBits := math.Float64bits(oldVal)
|
||||
newBits := math.Float64bits(newVal)
|
||||
if atomic.CompareAndSwapInt64(&g.value, int64(oldBits), int64(newBits)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Value returns the current value
|
||||
func (g *OptimizedGauge) Value() float64 {
|
||||
return g.Get()
|
||||
}
|
||||
|
||||
|
||||
// OptimizedHistogram provides a high-performance histogram
|
||||
// Uses bucket optimization similar to VictoriaMetrics
|
||||
type OptimizedHistogram struct {
|
||||
name string
|
||||
help string
|
||||
buckets []float64
|
||||
bucketCounts []uint64 // Count of values in each bucket
|
||||
count uint64 // Total count of observations
|
||||
sum float64 // Sum of all observations
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewOptimizedHistogram creates a new optimized histogram
|
||||
func NewOptimizedHistogram(name, help string, buckets []float64) *OptimizedHistogram {
|
||||
// Sort buckets to ensure they're in ascending order
|
||||
sortedBuckets := make([]float64, len(buckets))
|
||||
copy(sortedBuckets, buckets)
|
||||
for i := 0; i < len(sortedBuckets)-1; i++ {
|
||||
for j := i + 1; j < len(sortedBuckets); j++ {
|
||||
if sortedBuckets[i] > sortedBuckets[j] {
|
||||
sortedBuckets[i], sortedBuckets[j] = sortedBuckets[j], sortedBuckets[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &OptimizedHistogram{
|
||||
name: name,
|
||||
help: help,
|
||||
buckets: sortedBuckets,
|
||||
bucketCounts: make([]uint64, len(sortedBuckets)+1), // +1 for +Inf bucket
|
||||
}
|
||||
}
|
||||
|
||||
// Observe records a value in the histogram
|
||||
func (h *OptimizedHistogram) Observe(val float64) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
// Find the appropriate bucket
|
||||
bucketIdx := len(h.buckets) // Default to +Inf bucket
|
||||
for i, bucket := range h.buckets {
|
||||
if val <= bucket {
|
||||
bucketIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Increment the appropriate bucket count
|
||||
atomic.AddUint64(&h.bucketCounts[bucketIdx], 1)
|
||||
|
||||
// Increment total count
|
||||
atomic.AddUint64(&h.count, 1)
|
||||
|
||||
// Add to sum
|
||||
for {
|
||||
oldSum := h.sum
|
||||
newSum := oldSum + val
|
||||
if atomic.CompareAndSwapUint64((*uint64)(unsafe.Pointer(&h.sum)), math.Float64bits(oldSum), math.Float64bits(newSum)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetBucketCounts returns the current bucket counts
|
||||
func (h *OptimizedHistogram) GetBucketCounts() []uint64 {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
result := make([]uint64, len(h.bucketCounts))
|
||||
for i := range h.bucketCounts {
|
||||
result[i] = atomic.LoadUint64(&h.bucketCounts[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetCount returns the total count
|
||||
func (h *OptimizedHistogram) GetCount() uint64 {
|
||||
return atomic.LoadUint64(&h.count)
|
||||
}
|
||||
|
||||
// GetSum returns the sum
|
||||
func (h *OptimizedHistogram) GetSum() float64 {
|
||||
return h.sum
|
||||
}
|
||||
|
||||
// OptimizedSummary provides a high-performance summary
|
||||
type OptimizedSummary struct {
|
||||
name string
|
||||
help string
|
||||
count uint64
|
||||
sum float64
|
||||
quantiles map[float64]float64 // Quantile -> value
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewOptimizedSummary creates a new optimized summary
|
||||
func NewOptimizedSummary(name, help string) *OptimizedSummary {
|
||||
return &OptimizedSummary{
|
||||
name: name,
|
||||
help: help,
|
||||
quantiles: make(map[float64]float64),
|
||||
}
|
||||
}
|
||||
|
||||
// Observe records a value in the summary
|
||||
func (s *OptimizedSummary) Observe(val float64) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
atomic.AddUint64(&s.count, 1)
|
||||
|
||||
// Add to sum atomically
|
||||
for {
|
||||
oldSum := s.sum
|
||||
newSum := oldSum + val
|
||||
if atomic.CompareAndSwapUint64((*uint64)(unsafe.Pointer(&s.sum)), math.Float64bits(oldSum), math.Float64bits(newSum)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Note: In a real VictoriaMetrics implementation, quantiles would be calculated differently
|
||||
// This is a simplified version for demonstration purposes
|
||||
}
|
||||
|
||||
// GetCount returns the total count
|
||||
func (s *OptimizedSummary) GetCount() uint64 {
|
||||
return atomic.LoadUint64(&s.count)
|
||||
}
|
||||
|
||||
// GetSum returns the sum
|
||||
func (s *OptimizedSummary) GetSum() float64 {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.sum
|
||||
}
|
||||
|
||||
// MetricsRegistry provides a high-performance metrics registry
|
||||
type MetricsRegistry struct {
|
||||
counters map[string]*OptimizedCounter
|
||||
gauges map[string]*OptimizedGauge
|
||||
histograms map[string]*OptimizedHistogram
|
||||
summaries map[string]*OptimizedSummary
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewMetricsRegistry creates a new metrics registry
|
||||
func NewMetricsRegistry() *MetricsRegistry {
|
||||
return &MetricsRegistry{
|
||||
counters: make(map[string]*OptimizedCounter),
|
||||
gauges: make(map[string]*OptimizedGauge),
|
||||
histograms: make(map[string]*OptimizedHistogram),
|
||||
summaries: make(map[string]*OptimizedSummary),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterCounter registers a counter
|
||||
func (r *MetricsRegistry) RegisterCounter(name string, counter *OptimizedCounter) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.counters[name] = counter
|
||||
}
|
||||
|
||||
// GetCounter gets a counter by name
|
||||
func (r *MetricsRegistry) GetCounter(name string) *OptimizedCounter {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.counters[name]
|
||||
}
|
||||
|
||||
// RegisterGauge registers a gauge
|
||||
func (r *MetricsRegistry) RegisterGauge(name string, gauge *OptimizedGauge) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.gauges[name] = gauge
|
||||
}
|
||||
|
||||
// GetGauge gets a gauge by name
|
||||
func (r *MetricsRegistry) GetGauge(name string) *OptimizedGauge {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.gauges[name]
|
||||
}
|
||||
|
||||
// RegisterHistogram registers a histogram
|
||||
func (r *MetricsRegistry) RegisterHistogram(name string, histogram *OptimizedHistogram) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.histograms[name] = histogram
|
||||
}
|
||||
|
||||
// GetHistogram gets a histogram by name
|
||||
func (r *MetricsRegistry) GetHistogram(name string) *OptimizedHistogram {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.histograms[name]
|
||||
}
|
||||
|
||||
// RegisterSummary registers a summary
|
||||
func (r *MetricsRegistry) RegisterSummary(name string, summary *OptimizedSummary) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.summaries[name] = summary
|
||||
}
|
||||
|
||||
// GetSummary gets a summary by name
|
||||
func (r *MetricsRegistry) GetSummary(name string) *OptimizedSummary {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.summaries[name]
|
||||
}
|
||||
|
||||
// GetMetrics returns all metrics in a format similar to Prometheus exposition format
|
||||
func (r *MetricsRegistry) GetMetrics() string {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
var sb strings.Builder
|
||||
|
||||
// Add all counters
|
||||
for name, counter := range r.counters {
|
||||
sb.WriteString(fmt.Sprintf("# HELP %s %s\n# TYPE %s counter\n%s %d\n", name, counter.help, name, name, counter.Value()))
|
||||
}
|
||||
|
||||
// Add all gauges
|
||||
for name, gauge := range r.gauges {
|
||||
sb.WriteString(fmt.Sprintf("# HELP %s %s\n# TYPE %s gauge\n%s %f\n", name, gauge.help, name, name, gauge.Value()))
|
||||
}
|
||||
|
||||
// Add all histograms
|
||||
for name, histogram := range r.histograms {
|
||||
sb.WriteString(fmt.Sprintf("# HELP %s %s\n# TYPE %s histogram\n", name, histogram.help, name))
|
||||
|
||||
// Write bucket counts
|
||||
cumulative := uint64(0)
|
||||
for i, bucket := range histogram.buckets {
|
||||
cumulative += atomic.LoadUint64(&histogram.bucketCounts[i])
|
||||
sb.WriteString(fmt.Sprintf("%s_bucket{le=\"%g\"} %d\n", name, bucket, cumulative))
|
||||
}
|
||||
|
||||
// Write +Inf bucket
|
||||
cumulative += atomic.LoadUint64(&histogram.bucketCounts[len(histogram.buckets)])
|
||||
sb.WriteString(fmt.Sprintf("%s_bucket{le=\"+Inf\"} %d\n", name, cumulative))
|
||||
|
||||
// Write count and sum
|
||||
sb.WriteString(fmt.Sprintf("%s_count %d\n", name, atomic.LoadUint64(&histogram.count)))
|
||||
sb.WriteString(fmt.Sprintf("%s_sum %g\n", name, histogram.sum))
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
// Add all summaries
|
||||
for name, summary := range r.summaries {
|
||||
sb.WriteString(fmt.Sprintf("# HELP %s %s\n# TYPE %s summary\n", name, summary.help, name))
|
||||
|
||||
// Write count and sum
|
||||
sb.WriteString(fmt.Sprintf("%s_count %d\n", name, atomic.LoadUint64(&summary.count)))
|
||||
sb.WriteString(fmt.Sprintf("%s_sum %g\n", name, summary.sum))
|
||||
|
||||
// Write quantiles (simplified - in real implementation, quantiles would be calculated properly)
|
||||
for quantile, value := range summary.quantiles {
|
||||
sb.WriteString(fmt.Sprintf("%s{quantile=\"%g\"} %g\n", name, quantile, value))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// TimingMetric provides timing functionality similar to VictoriaMetrics
|
||||
type TimingMetric struct {
|
||||
histogram *OptimizedHistogram
|
||||
start time.Time
|
||||
}
|
||||
|
||||
// NewTimingMetric creates a new timing metric
|
||||
func NewTimingMetric(histogram *OptimizedHistogram) *TimingMetric {
|
||||
return &TimingMetric{
|
||||
histogram: histogram,
|
||||
start: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the timing and records the duration
|
||||
func (t *TimingMetric) Stop() {
|
||||
duration := time.Since(t.start).Seconds()
|
||||
t.histogram.Observe(duration)
|
||||
}
|
||||
|
||||
// Reset resets the timing
|
||||
func (t *TimingMetric) Reset() {
|
||||
t.start = time.Now()
|
||||
}
|
||||
|
||||
// Duration returns the current duration
|
||||
func (t *TimingMetric) Duration() time.Duration {
|
||||
return time.Since(t.start)
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package metric
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
// Option types are aliases to Prometheus options for compatibility.
|
||||
type CounterOpts = prometheus.CounterOpts
|
||||
type GaugeOpts = prometheus.GaugeOpts
|
||||
type HistogramOpts = prometheus.HistogramOpts
|
||||
type SummaryOpts = prometheus.SummaryOpts
|
||||
|
||||
// NewCounterVec creates a wrapped counter vec from options.
|
||||
func NewCounterVec(opts CounterOpts, labelNames []string) CounterVec {
|
||||
return WrapPrometheusCounterVec(prometheus.NewCounterVec(opts, labelNames))
|
||||
}
|
||||
|
||||
// NewGaugeVec creates a wrapped gauge vec from options.
|
||||
func NewGaugeVec(opts GaugeOpts, labelNames []string) GaugeVec {
|
||||
return WrapPrometheusGaugeVec(prometheus.NewGaugeVec(opts, labelNames))
|
||||
}
|
||||
|
||||
// NewHistogramVec creates a wrapped histogram vec from options.
|
||||
func NewHistogramVec(opts HistogramOpts, labelNames []string) HistogramVec {
|
||||
return WrapPrometheusHistogramVec(prometheus.NewHistogramVec(opts, labelNames))
|
||||
}
|
||||
|
||||
// NewSummaryVec creates a wrapped summary vec from options.
|
||||
func NewSummaryVec(opts SummaryOpts, labelNames []string) SummaryVec {
|
||||
return WrapPrometheusSummaryVec(prometheus.NewSummaryVec(opts, labelNames))
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// Copyright (C) 2026, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package metric
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// Registry aliases the Prometheus registry type.
|
||||
// We keep a pointer alias to avoid an extra wrapper type.
|
||||
type Registry = *prometheus.Registry
|
||||
|
||||
// Registerer aliases the Prometheus Registerer interface.
|
||||
type Registerer = prometheus.Registerer
|
||||
|
||||
// NewRegistry returns a new Prometheus registry.
|
||||
func NewRegistry() Registry {
|
||||
return prometheus.NewRegistry()
|
||||
}
|
||||
|
||||
// VictoriaMetricsRegistry provides a minimal registry for VictoriaMetrics-style
|
||||
// metrics without pulling in a heavy dependency.
|
||||
type VictoriaMetricsRegistry struct {
|
||||
mu sync.Mutex
|
||||
counters map[string]*VictoriaCounter
|
||||
gauges map[string]*VictoriaGauge
|
||||
histograms map[string]*VictoriaHistogram
|
||||
summaries map[string]*VictoriaSummary
|
||||
}
|
||||
|
||||
// NewVictoriaMetricsRegistry creates an empty VictoriaMetricsRegistry.
|
||||
func NewVictoriaMetricsRegistry() *VictoriaMetricsRegistry {
|
||||
return &VictoriaMetricsRegistry{
|
||||
counters: make(map[string]*VictoriaCounter),
|
||||
gauges: make(map[string]*VictoriaGauge),
|
||||
histograms: make(map[string]*VictoriaHistogram),
|
||||
summaries: make(map[string]*VictoriaSummary),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterCounter records a counter by name, returning the existing one if present.
|
||||
func (r *VictoriaMetricsRegistry) RegisterCounter(name string, counter *VictoriaCounter) *VictoriaCounter {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if existing, ok := r.counters[name]; ok {
|
||||
return existing
|
||||
}
|
||||
r.counters[name] = counter
|
||||
return counter
|
||||
}
|
||||
|
||||
// RegisterGauge records a gauge by name, returning the existing one if present.
|
||||
func (r *VictoriaMetricsRegistry) RegisterGauge(name string, gauge *VictoriaGauge) *VictoriaGauge {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if existing, ok := r.gauges[name]; ok {
|
||||
return existing
|
||||
}
|
||||
r.gauges[name] = gauge
|
||||
return gauge
|
||||
}
|
||||
|
||||
// RegisterHistogram records a histogram by name, returning the existing one if present.
|
||||
func (r *VictoriaMetricsRegistry) RegisterHistogram(name string, histogram *VictoriaHistogram) *VictoriaHistogram {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if existing, ok := r.histograms[name]; ok {
|
||||
return existing
|
||||
}
|
||||
r.histograms[name] = histogram
|
||||
return histogram
|
||||
}
|
||||
|
||||
// RegisterSummary records a summary by name, returning the existing one if present.
|
||||
func (r *VictoriaMetricsRegistry) RegisterSummary(name string, summary *VictoriaSummary) *VictoriaSummary {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if existing, ok := r.summaries[name]; ok {
|
||||
return existing
|
||||
}
|
||||
r.summaries[name] = summary
|
||||
return summary
|
||||
}
|
||||
+26
-1
@@ -17,6 +17,16 @@ func WrapPrometheusGauge(g prometheus.Gauge) Gauge {
|
||||
return &prometheusGauge{gauge: g}
|
||||
}
|
||||
|
||||
// WrapPrometheusHistogram wraps a prometheus.Histogram to implement our Histogram interface
|
||||
func WrapPrometheusHistogram(h prometheus.Histogram) Histogram {
|
||||
return &prometheusHistogram{histogram: h}
|
||||
}
|
||||
|
||||
// WrapPrometheusSummary wraps a prometheus.Summary to implement our Summary interface
|
||||
func WrapPrometheusSummary(s prometheus.Summary) Summary {
|
||||
return &prometheusSummary{summary: s}
|
||||
}
|
||||
|
||||
// WrapPrometheusCounterVec wraps a prometheus.CounterVec to implement our CounterVec interface
|
||||
func WrapPrometheusCounterVec(cv *prometheus.CounterVec) CounterVec {
|
||||
return &prometheusCounterVec{vec: cv}
|
||||
@@ -32,6 +42,11 @@ func WrapPrometheusHistogramVec(hv *prometheus.HistogramVec) HistogramVec {
|
||||
return &prometheusHistogramVec{vec: hv}
|
||||
}
|
||||
|
||||
// WrapPrometheusSummaryVec wraps a prometheus.SummaryVec to implement our SummaryVec interface
|
||||
func WrapPrometheusSummaryVec(sv *prometheus.SummaryVec) SummaryVec {
|
||||
return &prometheusSummaryVec{vec: sv}
|
||||
}
|
||||
|
||||
// NewCounterWithOpts creates a wrapped counter from options
|
||||
func NewCounterWithOpts(opts prometheus.CounterOpts) Counter {
|
||||
return WrapPrometheusCounter(prometheus.NewCounter(opts))
|
||||
@@ -42,6 +57,16 @@ func NewGaugeWithOpts(opts prometheus.GaugeOpts) Gauge {
|
||||
return WrapPrometheusGauge(prometheus.NewGauge(opts))
|
||||
}
|
||||
|
||||
// NewHistogramWithOpts creates a wrapped histogram from options
|
||||
func NewHistogramWithOpts(opts prometheus.HistogramOpts) Histogram {
|
||||
return WrapPrometheusHistogram(prometheus.NewHistogram(opts))
|
||||
}
|
||||
|
||||
// NewSummaryWithOpts creates a wrapped summary from options
|
||||
func NewSummaryWithOpts(opts prometheus.SummaryOpts) Summary {
|
||||
return WrapPrometheusSummary(prometheus.NewSummary(opts))
|
||||
}
|
||||
|
||||
// NewCounterVecWithOpts creates a wrapped counter vec from options
|
||||
func NewCounterVecWithOpts(opts prometheus.CounterOpts, labelNames []string) CounterVec {
|
||||
return WrapPrometheusCounterVec(prometheus.NewCounterVec(opts, labelNames))
|
||||
@@ -96,4 +121,4 @@ func (c *collectorVecAdapter) Describe(ch chan<- *prometheus.Desc) {
|
||||
|
||||
func (c *collectorVecAdapter) Collect(ch chan<- prometheus.Metric) {
|
||||
// No-op for compatibility
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user