// Package meter is for instrumentation package meter import ( "io" "sort" "strconv" "strings" "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 } // 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 } // 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 { sort.Sort(byKey(labels)) 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 } } } var b strings.Builder _, _ = 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(strconv.Quote(labels[idx+1])) } _, _ = b.WriteRune('}') return b.String() }