chore: remove local replace directives

This commit is contained in:
Zach Kelling
2026-01-08 23:44:31 -08:00
parent 574a4da55d
commit c56d4bbde0
12 changed files with 1519 additions and 110 deletions
+86
View File
@@ -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
View File
@@ -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
}
}
+6
View File
@@ -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{
+2 -1
View File
@@ -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
)
+27 -1
View File
@@ -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=
+768
View File
@@ -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()
}
+42 -104
View File
@@ -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
)
+9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
}