merge v4 changes
Some checks failed
coverage / build (push) Failing after 3m29s
test / test (push) Has started running

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
2025-10-24 12:05:04 +03:00
parent 3e86864ce7
commit 4bb73514e9
10 changed files with 167 additions and 34 deletions

View File

@@ -4,8 +4,8 @@ package meter
import (
"io"
"sort"
"strconv"
"strings"
"sync"
"time"
)
@@ -49,9 +49,11 @@ type Meter interface {
Set(opts ...Option) Meter
// Histogram get or create histogram
Histogram(name string, labels ...string) Histogram
// HistogramExt get or create histogram with specified quantiles
HistogramExt(name string, quantiles []float64, labels ...string) Histogram
// Summary get or create summary
Summary(name string, labels ...string) Summary
// SummaryExt get or create summary with spcified quantiles and window time
// SummaryExt get or create summary with specified quantiles and window time
SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) Summary
// Write writes metrics to io.Writer
Write(w io.Writer, opts ...Option) error
@@ -59,6 +61,8 @@ type Meter interface {
Options() Options
// String return meter type
String() string
// Unregister metric name and drop all data
Unregister(name string, labels ...string) bool
}
// Counter is a counter
@@ -80,7 +84,11 @@ type FloatCounter interface {
// Gauge is a float64 gauge
type Gauge interface {
Add(float64)
Get() float64
Set(float64)
Dec()
Inc()
}
// Histogram is a histogram for non-negative values with automatically created buckets
@@ -117,6 +125,39 @@ func BuildLabels(labels ...string) []string {
return labels
}
var spool = newStringsPool(500)
type stringsPool struct {
p *sync.Pool
c int
}
func newStringsPool(size int) *stringsPool {
p := &stringsPool{c: size}
p.p = &sync.Pool{
New: func() interface{} {
return &strings.Builder{}
},
}
return p
}
func (p *stringsPool) Cap() int {
return p.c
}
func (p *stringsPool) Get() *strings.Builder {
return p.p.Get().(*strings.Builder)
}
func (p *stringsPool) Put(b *strings.Builder) {
if b.Cap() > p.c {
return
}
b.Reset()
p.p.Put(b)
}
// BuildName used to combine metric with labels.
// If labels count is odd, drop last element
func BuildName(name string, labels ...string) string {
@@ -125,8 +166,6 @@ func BuildName(name string, labels ...string) string {
}
if len(labels) > 2 {
sort.Sort(byKey(labels))
idx := 0
for {
if labels[idx] == labels[idx+2] {
@@ -141,7 +180,9 @@ func BuildName(name string, labels ...string) string {
}
}
var b strings.Builder
b := spool.Get()
defer spool.Put(b)
_, _ = b.WriteString(name)
_, _ = b.WriteRune('{')
for idx := 0; idx < len(labels); idx += 2 {
@@ -149,8 +190,9 @@ func BuildName(name string, labels ...string) string {
_, _ = b.WriteRune(',')
}
_, _ = b.WriteString(labels[idx])
_, _ = b.WriteString(`=`)
_, _ = b.WriteString(strconv.Quote(labels[idx+1]))
_, _ = b.WriteString(`="`)
_, _ = b.WriteString(labels[idx+1])
_, _ = b.WriteRune('"')
}
_, _ = b.WriteRune('}')

View File

@@ -50,11 +50,12 @@ func TestBuildName(t *testing.T) {
data := map[string][]string{
`my_metric{firstlabel="value2",zerolabel="value3"}`: {
"my_metric",
"zerolabel", "value3", "firstlabel", "value2",
"firstlabel", "value2",
"zerolabel", "value3",
},
`my_metric{broker="broker2",register="mdns",server="tcp"}`: {
"my_metric",
"broker", "broker1", "broker", "broker2", "server", "http", "server", "tcp", "register", "mdns",
"broker", "broker1", "broker", "broker2", "register", "mdns", "server", "http", "server", "tcp",
},
`my_metric{aaa="aaa"}`: {
"my_metric",

View File

@@ -28,6 +28,10 @@ func (r *noopMeter) Name() string {
return r.opts.Name
}
func (r *noopMeter) Unregister(name string, labels ...string) bool {
return true
}
// Init initialize options
func (r *noopMeter) Init(opts ...Option) error {
for _, o := range opts {
@@ -66,6 +70,11 @@ func (r *noopMeter) Histogram(_ string, labels ...string) Histogram {
return &noopHistogram{labels: labels}
}
// HistogramExt implements the Meter interface
func (r *noopMeter) HistogramExt(_ string, quantiles []float64, labels ...string) Histogram {
return &noopHistogram{labels: labels}
}
// Set implements the Meter interface
func (r *noopMeter) Set(opts ...Option) Meter {
m := &noopMeter{opts: r.opts}
@@ -132,6 +141,18 @@ type noopGauge struct {
labels []string
}
func (r *noopGauge) Add(float64) {
}
func (r *noopGauge) Set(float64) {
}
func (r *noopGauge) Inc() {
}
func (r *noopGauge) Dec() {
}
func (r *noopGauge) Get() float64 {
return 0
}

View File

@@ -4,6 +4,8 @@ import (
"context"
)
var DefaultQuantiles = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
// Option powers the configuration for metrics implementations:
type Option func(*Options)
@@ -23,6 +25,8 @@ type Options struct {
WriteProcessMetrics bool
// WriteFDMetrics flag to write fd metrics
WriteFDMetrics bool
// Quantiles specifies buckets for histogram
Quantiles []float64
}
// NewOptions prepares a set of options:
@@ -61,14 +65,12 @@ func Address(value string) Option {
}
}
/*
// TimingObjectives defines the desired spread of statistics for histogram / timing metrics:
func TimingObjectives(value map[float64]float64) Option {
// Quantiles defines the desired spread of statistics for histogram metrics:
func Quantiles(quantiles []float64) Option {
return func(o *Options) {
o.TimingObjectives = value
o.Quantiles = quantiles
}
}
*/
// Labels add the meter labels
func Labels(ls ...string) Option {