Reviewed-on: #409 Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org> Co-committed-by: Vasiliy Tolstov <v.tolstov@unistack.org>
		
			
				
	
	
		
			195 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package meter is for instrumentation
 | |
| package meter
 | |
| 
 | |
| import (
 | |
| 	"io"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	// DefaultMeter is the default meter
 | |
| 	DefaultMeter = NewMeter()
 | |
| 	// DefaultAddress data will be made available on this host:port
 | |
| 	DefaultAddress = ":9090"
 | |
| 	// DefaultPath the meter endpoint where the Meter data will be made available
 | |
| 	DefaultPath = "/metrics"
 | |
| 	// DefaultMeterStatsInterval specifies interval for meter updating
 | |
| 	DefaultMeterStatsInterval = 5 * time.Second
 | |
| 	// DefaultSummaryQuantiles is the default spread of stats for summary
 | |
| 	DefaultSummaryQuantiles = []float64{0.5, 0.9, 0.97, 0.99, 1}
 | |
| 	// DefaultSummaryWindow is the default window for summary
 | |
| 	DefaultSummaryWindow = 5 * time.Minute
 | |
| 	// DefaultSkipEndpoints is the slice of endpoint that must not be metered
 | |
| 	DefaultSkipEndpoints = []string{
 | |
| 		"MeterService.Metrics",
 | |
| 		"HealthService.Live",
 | |
| 		"HealthService.Ready",
 | |
| 		"HealthService.Version",
 | |
| 	}
 | |
| )
 | |
| 
 | |
| // Meter is an interface for collecting and instrumenting metrics
 | |
| type Meter interface {
 | |
| 	// Name returns meter name
 | |
| 	Name() string
 | |
| 	// Init initialize meter
 | |
| 	Init(opts ...Option) error
 | |
| 	// Clone create meter copy with new options
 | |
| 	Clone(opts ...Option) Meter
 | |
| 	// Counter get or create counter
 | |
| 	Counter(name string, labels ...string) Counter
 | |
| 	// FloatCounter get or create float counter
 | |
| 	FloatCounter(name string, labels ...string) FloatCounter
 | |
| 	// Gauge get or create gauge
 | |
| 	Gauge(name string, fn func() float64, labels ...string) Gauge
 | |
| 	// Set create new meter metrics set
 | |
| 	Set(opts ...Option) Meter
 | |
| 	// Histogram get or create histogram
 | |
| 	Histogram(name string, 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(name string, window time.Duration, quantiles []float64, labels ...string) Summary
 | |
| 	// Write writes metrics to io.Writer
 | |
| 	Write(w io.Writer, opts ...Option) error
 | |
| 	// Options returns meter options
 | |
| 	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
 | |
| type Counter interface {
 | |
| 	Add(int)
 | |
| 	Dec()
 | |
| 	Get() uint64
 | |
| 	Inc()
 | |
| 	Set(uint64)
 | |
| }
 | |
| 
 | |
| // FloatCounter is a float64 counter
 | |
| type FloatCounter interface {
 | |
| 	Add(float64)
 | |
| 	Get() float64
 | |
| 	Set(float64)
 | |
| 	Sub(float64)
 | |
| }
 | |
| 
 | |
| // Gauge is a float64 gauge
 | |
| type Gauge interface {
 | |
| 	Get() float64
 | |
| }
 | |
| 
 | |
| // Histogram is a histogram for non-negative values with automatically created buckets
 | |
| type Histogram interface {
 | |
| 	Reset()
 | |
| 	Update(float64)
 | |
| 	UpdateDuration(time.Time)
 | |
| 	// VisitNonZeroBuckets(f func(vmrange string, count uint64))
 | |
| }
 | |
| 
 | |
| // Summary is the summary
 | |
| type Summary interface {
 | |
| 	Update(float64)
 | |
| 	UpdateDuration(time.Time)
 | |
| }
 | |
| 
 | |
| // sort labels alphabeticaly by label name
 | |
| type byKey []string
 | |
| 
 | |
| func (k byKey) Len() int           { return len(k) / 2 }
 | |
| func (k byKey) Less(i, j int) bool { return k[i*2] < k[j*2] }
 | |
| func (k byKey) Swap(i, j int) {
 | |
| 	k[i*2], k[j*2] = k[j*2], k[i*2]
 | |
| 	k[i*2+1], k[j*2+1] = k[j*2+1], k[i*2+1]
 | |
| }
 | |
| 
 | |
| // BuildLabels used to sort labels and delete duplicates.
 | |
| // Last value wins in case of duplicate label keys.
 | |
| func BuildLabels(labels ...string) []string {
 | |
| 	if len(labels)%2 == 1 {
 | |
| 		labels = labels[:len(labels)-1]
 | |
| 	}
 | |
| 	sort.Sort(byKey(labels))
 | |
| 	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 {
 | |
| 	if len(labels)%2 == 1 {
 | |
| 		labels = labels[:len(labels)-1]
 | |
| 	}
 | |
| 
 | |
| 	if len(labels) > 2 {
 | |
| 		idx := 0
 | |
| 		for {
 | |
| 			if labels[idx] == labels[idx+2] {
 | |
| 				copy(labels[idx:], labels[idx+2:])
 | |
| 				labels = labels[:len(labels)-2]
 | |
| 			} else {
 | |
| 				idx += 2
 | |
| 			}
 | |
| 			if idx+2 >= len(labels) {
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	b := spool.Get()
 | |
| 	defer spool.Put(b)
 | |
| 
 | |
| 	_, _ = b.WriteString(name)
 | |
| 	_, _ = b.WriteRune('{')
 | |
| 	for idx := 0; idx < len(labels); idx += 2 {
 | |
| 		if idx > 0 {
 | |
| 			_, _ = b.WriteRune(',')
 | |
| 		}
 | |
| 		_, _ = b.WriteString(labels[idx])
 | |
| 		_, _ = b.WriteString(`="`)
 | |
| 		_, _ = b.WriteString(labels[idx+1])
 | |
| 		_, _ = b.WriteRune('"')
 | |
| 	}
 | |
| 	_, _ = b.WriteRune('}')
 | |
| 
 | |
| 	return b.String()
 | |
| }
 |