mirror of https://github.com/hashicorp/consul
vendor: update go-metrics
parent
0f8b1faad8
commit
ce572546e5
|
@ -5,6 +5,7 @@ package circonus
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/armon/go-metrics"
|
||||||
cgm "github.com/circonus-labs/circonus-gometrics"
|
cgm "github.com/circonus-labs/circonus-gometrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -61,6 +62,12 @@ func (s *CirconusSink) SetGauge(key []string, val float32) {
|
||||||
s.metrics.SetGauge(flatKey, int64(val))
|
s.metrics.SetGauge(flatKey, int64(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetGaugeWithLabels sets value for a gauge metric with the given labels
|
||||||
|
func (s *CirconusSink) SetGaugeWithLabels(key []string, val float32, labels []metrics.Label) {
|
||||||
|
flatKey := s.flattenKeyLabels(key, labels)
|
||||||
|
s.metrics.SetGauge(flatKey, int64(val))
|
||||||
|
}
|
||||||
|
|
||||||
// EmitKey is not implemented in circonus
|
// EmitKey is not implemented in circonus
|
||||||
func (s *CirconusSink) EmitKey(key []string, val float32) {
|
func (s *CirconusSink) EmitKey(key []string, val float32) {
|
||||||
// NOP
|
// NOP
|
||||||
|
@ -72,12 +79,24 @@ func (s *CirconusSink) IncrCounter(key []string, val float32) {
|
||||||
s.metrics.IncrementByValue(flatKey, uint64(val))
|
s.metrics.IncrementByValue(flatKey, uint64(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IncrCounterWithLabels increments a counter metric with the given labels
|
||||||
|
func (s *CirconusSink) IncrCounterWithLabels(key []string, val float32, labels []metrics.Label) {
|
||||||
|
flatKey := s.flattenKeyLabels(key, labels)
|
||||||
|
s.metrics.IncrementByValue(flatKey, uint64(val))
|
||||||
|
}
|
||||||
|
|
||||||
// AddSample adds a sample to a histogram metric
|
// AddSample adds a sample to a histogram metric
|
||||||
func (s *CirconusSink) AddSample(key []string, val float32) {
|
func (s *CirconusSink) AddSample(key []string, val float32) {
|
||||||
flatKey := s.flattenKey(key)
|
flatKey := s.flattenKey(key)
|
||||||
s.metrics.RecordValue(flatKey, float64(val))
|
s.metrics.RecordValue(flatKey, float64(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddSampleWithLabels adds a sample to a histogram metric with the given labels
|
||||||
|
func (s *CirconusSink) AddSampleWithLabels(key []string, val float32, labels []metrics.Label) {
|
||||||
|
flatKey := s.flattenKeyLabels(key, labels)
|
||||||
|
s.metrics.RecordValue(flatKey, float64(val))
|
||||||
|
}
|
||||||
|
|
||||||
// Flattens key to Circonus metric name
|
// Flattens key to Circonus metric name
|
||||||
func (s *CirconusSink) flattenKey(parts []string) string {
|
func (s *CirconusSink) flattenKey(parts []string) string {
|
||||||
joined := strings.Join(parts, "`")
|
joined := strings.Join(parts, "`")
|
||||||
|
@ -90,3 +109,11 @@ func (s *CirconusSink) flattenKey(parts []string) string {
|
||||||
}
|
}
|
||||||
}, joined)
|
}, joined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flattens the key along with labels for formatting, removes spaces
|
||||||
|
func (s *CirconusSink) flattenKeyLabels(parts []string, labels []metrics.Label) string {
|
||||||
|
for _, label := range labels {
|
||||||
|
parts = append(parts, label.Value)
|
||||||
|
}
|
||||||
|
return s.flattenKey(parts)
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/DataDog/datadog-go/statsd"
|
"github.com/DataDog/datadog-go/statsd"
|
||||||
|
"github.com/armon/go-metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DogStatsdSink provides a MetricSink that can be used
|
// DogStatsdSink provides a MetricSink that can be used
|
||||||
|
@ -45,7 +46,10 @@ func (s *DogStatsdSink) EnableHostNamePropagation() {
|
||||||
|
|
||||||
func (s *DogStatsdSink) flattenKey(parts []string) string {
|
func (s *DogStatsdSink) flattenKey(parts []string) string {
|
||||||
joined := strings.Join(parts, ".")
|
joined := strings.Join(parts, ".")
|
||||||
return strings.Map(func(r rune) rune {
|
return strings.Map(sanitize, joined)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitize(r rune) rune {
|
||||||
switch r {
|
switch r {
|
||||||
case ':':
|
case ':':
|
||||||
fallthrough
|
fallthrough
|
||||||
|
@ -54,37 +58,37 @@ func (s *DogStatsdSink) flattenKey(parts []string) string {
|
||||||
default:
|
default:
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
}, joined)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DogStatsdSink) parseKey(key []string) ([]string, []string) {
|
func (s *DogStatsdSink) parseKey(key []string) ([]string, []metrics.Label) {
|
||||||
// Since DogStatsd supports dimensionality via tags on metric keys, this sink's approach is to splice the hostname out of the key in favor of a `host` tag
|
// Since DogStatsd supports dimensionality via tags on metric keys, this sink's approach is to splice the hostname out of the key in favor of a `host` tag
|
||||||
// The `host` tag is either forced here, or set downstream by the DogStatsd server
|
// The `host` tag is either forced here, or set downstream by the DogStatsd server
|
||||||
|
|
||||||
var tags []string
|
var labels []metrics.Label
|
||||||
hostName := s.hostName
|
hostName := s.hostName
|
||||||
|
|
||||||
// Splice the hostname out of the key
|
// Splice the hostname out of the key
|
||||||
for i, el := range key {
|
for i, el := range key {
|
||||||
if el == hostName {
|
if el == hostName {
|
||||||
key = append(key[:i], key[i+1:]...)
|
key = append(key[:i], key[i+1:]...)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.propagateHostname {
|
if s.propagateHostname {
|
||||||
tags = append(tags, fmt.Sprintf("host:%s", hostName))
|
labels = append(labels, metrics.Label{"host", hostName})
|
||||||
}
|
}
|
||||||
return key, tags
|
return key, labels
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of methods in the MetricSink interface
|
// Implementation of methods in the MetricSink interface
|
||||||
|
|
||||||
func (s *DogStatsdSink) SetGauge(key []string, val float32) {
|
func (s *DogStatsdSink) SetGauge(key []string, val float32) {
|
||||||
s.SetGaugeWithTags(key, val, []string{})
|
s.SetGaugeWithLabels(key, val, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DogStatsdSink) IncrCounter(key []string, val float32) {
|
func (s *DogStatsdSink) IncrCounter(key []string, val float32) {
|
||||||
s.IncrCounterWithTags(key, val, []string{})
|
s.IncrCounterWithLabels(key, val, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmitKey is not implemented since DogStatsd does not provide a metric type that holds an
|
// EmitKey is not implemented since DogStatsd does not provide a metric type that holds an
|
||||||
|
@ -93,33 +97,44 @@ func (s *DogStatsdSink) EmitKey(key []string, val float32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DogStatsdSink) AddSample(key []string, val float32) {
|
func (s *DogStatsdSink) AddSample(key []string, val float32) {
|
||||||
s.AddSampleWithTags(key, val, []string{})
|
s.AddSampleWithLabels(key, val, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following ...WithTags methods correspond to Datadog's Tag extension to Statsd.
|
// The following ...WithLabels methods correspond to Datadog's Tag extension to Statsd.
|
||||||
// http://docs.datadoghq.com/guides/dogstatsd/#tags
|
// http://docs.datadoghq.com/guides/dogstatsd/#tags
|
||||||
|
func (s *DogStatsdSink) SetGaugeWithLabels(key []string, val float32, labels []metrics.Label) {
|
||||||
func (s *DogStatsdSink) SetGaugeWithTags(key []string, val float32, tags []string) {
|
flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
|
||||||
flatKey, tags := s.getFlatkeyAndCombinedTags(key, tags)
|
|
||||||
rate := 1.0
|
rate := 1.0
|
||||||
s.client.Gauge(flatKey, float64(val), tags, rate)
|
s.client.Gauge(flatKey, float64(val), tags, rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DogStatsdSink) IncrCounterWithTags(key []string, val float32, tags []string) {
|
func (s *DogStatsdSink) IncrCounterWithLabels(key []string, val float32, labels []metrics.Label) {
|
||||||
flatKey, tags := s.getFlatkeyAndCombinedTags(key, tags)
|
flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
|
||||||
rate := 1.0
|
rate := 1.0
|
||||||
s.client.Count(flatKey, int64(val), tags, rate)
|
s.client.Count(flatKey, int64(val), tags, rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DogStatsdSink) AddSampleWithTags(key []string, val float32, tags []string) {
|
func (s *DogStatsdSink) AddSampleWithLabels(key []string, val float32, labels []metrics.Label) {
|
||||||
flatKey, tags := s.getFlatkeyAndCombinedTags(key, tags)
|
flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
|
||||||
rate := 1.0
|
rate := 1.0
|
||||||
s.client.TimeInMilliseconds(flatKey, float64(val), tags, rate)
|
s.client.TimeInMilliseconds(flatKey, float64(val), tags, rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DogStatsdSink) getFlatkeyAndCombinedTags(key []string, tags []string) (flattenedKey string, combinedTags []string) {
|
func (s *DogStatsdSink) getFlatkeyAndCombinedLabels(key []string, labels []metrics.Label) (string, []string) {
|
||||||
key, hostTags := s.parseKey(key)
|
key, parsedLabels := s.parseKey(key)
|
||||||
flatKey := s.flattenKey(key)
|
flatKey := s.flattenKey(key)
|
||||||
tags = append(tags, hostTags...)
|
labels = append(labels, parsedLabels...)
|
||||||
|
|
||||||
|
var tags []string
|
||||||
|
for _, label := range labels {
|
||||||
|
label.Name = strings.Map(sanitize, label.Name)
|
||||||
|
label.Value = strings.Map(sanitize, label.Value)
|
||||||
|
if label.Value != "" {
|
||||||
|
tags = append(tags, fmt.Sprintf("%s:%s", label.Name, label.Value))
|
||||||
|
} else {
|
||||||
|
tags = append(tags, label.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return flatKey, tags
|
return flatKey, tags
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -39,7 +40,7 @@ type IntervalMetrics struct {
|
||||||
Interval time.Time
|
Interval time.Time
|
||||||
|
|
||||||
// Gauges maps the key to the last set value
|
// Gauges maps the key to the last set value
|
||||||
Gauges map[string]float32
|
Gauges map[string]GaugeValue
|
||||||
|
|
||||||
// Points maps the string to the list of emitted values
|
// Points maps the string to the list of emitted values
|
||||||
// from EmitKey
|
// from EmitKey
|
||||||
|
@ -47,21 +48,21 @@ type IntervalMetrics struct {
|
||||||
|
|
||||||
// Counters maps the string key to a sum of the counter
|
// Counters maps the string key to a sum of the counter
|
||||||
// values
|
// values
|
||||||
Counters map[string]*AggregateSample
|
Counters map[string]SampledValue
|
||||||
|
|
||||||
// Samples maps the key to an AggregateSample,
|
// Samples maps the key to an AggregateSample,
|
||||||
// which has the rolled up view of a sample
|
// which has the rolled up view of a sample
|
||||||
Samples map[string]*AggregateSample
|
Samples map[string]SampledValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIntervalMetrics creates a new IntervalMetrics for a given interval
|
// NewIntervalMetrics creates a new IntervalMetrics for a given interval
|
||||||
func NewIntervalMetrics(intv time.Time) *IntervalMetrics {
|
func NewIntervalMetrics(intv time.Time) *IntervalMetrics {
|
||||||
return &IntervalMetrics{
|
return &IntervalMetrics{
|
||||||
Interval: intv,
|
Interval: intv,
|
||||||
Gauges: make(map[string]float32),
|
Gauges: make(map[string]GaugeValue),
|
||||||
Points: make(map[string][]float32),
|
Points: make(map[string][]float32),
|
||||||
Counters: make(map[string]*AggregateSample),
|
Counters: make(map[string]SampledValue),
|
||||||
Samples: make(map[string]*AggregateSample),
|
Samples: make(map[string]SampledValue),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,12 +70,12 @@ func NewIntervalMetrics(intv time.Time) *IntervalMetrics {
|
||||||
// about a sample
|
// about a sample
|
||||||
type AggregateSample struct {
|
type AggregateSample struct {
|
||||||
Count int // The count of emitted pairs
|
Count int // The count of emitted pairs
|
||||||
Rate float64 // The count of emitted pairs per time unit (usually 1 second)
|
Rate float64 `json:"-"` // The count of emitted pairs per time unit (usually 1 second)
|
||||||
Sum float64 // The sum of values
|
Sum float64 // The sum of values
|
||||||
SumSq float64 // The sum of squared values
|
SumSq float64 `json:"-"` // The sum of squared values
|
||||||
Min float64 // Minimum value
|
Min float64 // Minimum value
|
||||||
Max float64 // Maximum value
|
Max float64 // Maximum value
|
||||||
LastUpdated time.Time // When value was last updated
|
LastUpdated time.Time `json:"-"` // When value was last updated
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes a Stddev of the values
|
// Computes a Stddev of the values
|
||||||
|
@ -154,12 +155,16 @@ func NewInmemSink(interval, retain time.Duration) *InmemSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *InmemSink) SetGauge(key []string, val float32) {
|
func (i *InmemSink) SetGauge(key []string, val float32) {
|
||||||
k := i.flattenKey(key)
|
i.SetGaugeWithLabels(key, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *InmemSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
k, name := i.flattenKeyLabels(key, labels)
|
||||||
intv := i.getInterval()
|
intv := i.getInterval()
|
||||||
|
|
||||||
intv.Lock()
|
intv.Lock()
|
||||||
defer intv.Unlock()
|
defer intv.Unlock()
|
||||||
intv.Gauges[k] = val
|
intv.Gauges[k] = GaugeValue{Name: name, Value: val, Labels: labels}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *InmemSink) EmitKey(key []string, val float32) {
|
func (i *InmemSink) EmitKey(key []string, val float32) {
|
||||||
|
@ -173,30 +178,46 @@ func (i *InmemSink) EmitKey(key []string, val float32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *InmemSink) IncrCounter(key []string, val float32) {
|
func (i *InmemSink) IncrCounter(key []string, val float32) {
|
||||||
k := i.flattenKey(key)
|
i.IncrCounterWithLabels(key, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *InmemSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
k, name := i.flattenKeyLabels(key, labels)
|
||||||
intv := i.getInterval()
|
intv := i.getInterval()
|
||||||
|
|
||||||
intv.Lock()
|
intv.Lock()
|
||||||
defer intv.Unlock()
|
defer intv.Unlock()
|
||||||
|
|
||||||
agg := intv.Counters[k]
|
agg, ok := intv.Counters[k]
|
||||||
if agg == nil {
|
if !ok {
|
||||||
agg = &AggregateSample{}
|
agg = SampledValue{
|
||||||
|
Name: name,
|
||||||
|
AggregateSample: &AggregateSample{},
|
||||||
|
Labels: labels,
|
||||||
|
}
|
||||||
intv.Counters[k] = agg
|
intv.Counters[k] = agg
|
||||||
}
|
}
|
||||||
agg.Ingest(float64(val), i.rateDenom)
|
agg.Ingest(float64(val), i.rateDenom)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *InmemSink) AddSample(key []string, val float32) {
|
func (i *InmemSink) AddSample(key []string, val float32) {
|
||||||
k := i.flattenKey(key)
|
i.AddSampleWithLabels(key, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *InmemSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
k, name := i.flattenKeyLabels(key, labels)
|
||||||
intv := i.getInterval()
|
intv := i.getInterval()
|
||||||
|
|
||||||
intv.Lock()
|
intv.Lock()
|
||||||
defer intv.Unlock()
|
defer intv.Unlock()
|
||||||
|
|
||||||
agg := intv.Samples[k]
|
agg, ok := intv.Samples[k]
|
||||||
if agg == nil {
|
if !ok {
|
||||||
agg = &AggregateSample{}
|
agg = SampledValue{
|
||||||
|
Name: name,
|
||||||
|
AggregateSample: &AggregateSample{},
|
||||||
|
Labels: labels,
|
||||||
|
}
|
||||||
intv.Samples[k] = agg
|
intv.Samples[k] = agg
|
||||||
}
|
}
|
||||||
agg.Ingest(float64(val), i.rateDenom)
|
agg.Ingest(float64(val), i.rateDenom)
|
||||||
|
@ -261,6 +282,38 @@ func (i *InmemSink) getInterval() *IntervalMetrics {
|
||||||
|
|
||||||
// Flattens the key for formatting, removes spaces
|
// Flattens the key for formatting, removes spaces
|
||||||
func (i *InmemSink) flattenKey(parts []string) string {
|
func (i *InmemSink) flattenKey(parts []string) string {
|
||||||
joined := strings.Join(parts, ".")
|
buf := &bytes.Buffer{}
|
||||||
return strings.Replace(joined, " ", "_", -1)
|
replacer := strings.NewReplacer(" ", "_")
|
||||||
|
|
||||||
|
if len(parts) > 0 {
|
||||||
|
replacer.WriteString(buf, parts[0])
|
||||||
|
}
|
||||||
|
for _, part := range parts[1:] {
|
||||||
|
replacer.WriteString(buf, ".")
|
||||||
|
replacer.WriteString(buf, part)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flattens the key for formatting along with its labels, removes spaces
|
||||||
|
func (i *InmemSink) flattenKeyLabels(parts []string, labels []Label) (string, string) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
replacer := strings.NewReplacer(" ", "_")
|
||||||
|
|
||||||
|
if len(parts) > 0 {
|
||||||
|
replacer.WriteString(buf, parts[0])
|
||||||
|
}
|
||||||
|
for _, part := range parts[1:] {
|
||||||
|
replacer.WriteString(buf, ".")
|
||||||
|
replacer.WriteString(buf, part)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := buf.String()
|
||||||
|
|
||||||
|
for _, label := range labels {
|
||||||
|
replacer.WriteString(buf, fmt.Sprintf(";%s=%s", label.Name, label.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String(), key
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MetricsSummary holds a roll-up of metrics info for a given interval
|
||||||
|
type MetricsSummary struct {
|
||||||
|
Timestamp string
|
||||||
|
Gauges []GaugeValue
|
||||||
|
Points []PointValue
|
||||||
|
Counters []SampledValue
|
||||||
|
Samples []SampledValue
|
||||||
|
}
|
||||||
|
|
||||||
|
type GaugeValue struct {
|
||||||
|
Name string
|
||||||
|
Hash string `json:"-"`
|
||||||
|
Value float32
|
||||||
|
|
||||||
|
Labels []Label `json:"-"`
|
||||||
|
DisplayLabels map[string]string `json:"Labels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PointValue struct {
|
||||||
|
Name string
|
||||||
|
Points []float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type SampledValue struct {
|
||||||
|
Name string
|
||||||
|
Hash string `json:"-"`
|
||||||
|
*AggregateSample
|
||||||
|
Mean float64
|
||||||
|
Stddev float64
|
||||||
|
|
||||||
|
Labels []Label `json:"-"`
|
||||||
|
DisplayLabels map[string]string `json:"Labels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayMetrics returns a summary of the metrics from the most recent finished interval.
|
||||||
|
func (i *InmemSink) DisplayMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||||
|
data := i.Data()
|
||||||
|
|
||||||
|
var interval *IntervalMetrics
|
||||||
|
n := len(data)
|
||||||
|
switch {
|
||||||
|
case n == 0:
|
||||||
|
return nil, fmt.Errorf("no metric intervals have been initialized yet")
|
||||||
|
case n == 1:
|
||||||
|
// Show the current interval if it's all we have
|
||||||
|
interval = i.intervals[0]
|
||||||
|
default:
|
||||||
|
// Show the most recent finished interval if we have one
|
||||||
|
interval = i.intervals[n-2]
|
||||||
|
}
|
||||||
|
|
||||||
|
summary := MetricsSummary{
|
||||||
|
Timestamp: interval.Interval.Round(time.Second).UTC().String(),
|
||||||
|
Gauges: make([]GaugeValue, 0, len(interval.Gauges)),
|
||||||
|
Points: make([]PointValue, 0, len(interval.Points)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format and sort the output of each metric type, so it gets displayed in a
|
||||||
|
// deterministic order.
|
||||||
|
for name, points := range interval.Points {
|
||||||
|
summary.Points = append(summary.Points, PointValue{name, points})
|
||||||
|
}
|
||||||
|
sort.Slice(summary.Points, func(i, j int) bool {
|
||||||
|
return summary.Points[i].Name < summary.Points[j].Name
|
||||||
|
})
|
||||||
|
|
||||||
|
for hash, value := range interval.Gauges {
|
||||||
|
value.Hash = hash
|
||||||
|
value.DisplayLabels = make(map[string]string)
|
||||||
|
for _, label := range value.Labels {
|
||||||
|
value.DisplayLabels[label.Name] = label.Value
|
||||||
|
}
|
||||||
|
value.Labels = nil
|
||||||
|
|
||||||
|
summary.Gauges = append(summary.Gauges, value)
|
||||||
|
}
|
||||||
|
sort.Slice(summary.Gauges, func(i, j int) bool {
|
||||||
|
return summary.Gauges[i].Hash < summary.Gauges[j].Hash
|
||||||
|
})
|
||||||
|
|
||||||
|
summary.Counters = formatSamples(interval.Counters)
|
||||||
|
summary.Samples = formatSamples(interval.Samples)
|
||||||
|
|
||||||
|
return summary, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatSamples(source map[string]SampledValue) []SampledValue {
|
||||||
|
output := make([]SampledValue, 0, len(source))
|
||||||
|
for hash, sample := range source {
|
||||||
|
displayLabels := make(map[string]string)
|
||||||
|
for _, label := range sample.Labels {
|
||||||
|
displayLabels[label.Name] = label.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
output = append(output, SampledValue{
|
||||||
|
Name: sample.Name,
|
||||||
|
Hash: hash,
|
||||||
|
AggregateSample: sample.AggregateSample,
|
||||||
|
Mean: sample.AggregateSample.Mean(),
|
||||||
|
Stddev: sample.AggregateSample.Stddev(),
|
||||||
|
DisplayLabels: displayLabels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Slice(output, func(i, j int) bool {
|
||||||
|
return output[i].Hash < output[j].Hash
|
||||||
|
})
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
|
@ -79,7 +79,7 @@ func (i *InmemSignal) dumpStats() {
|
||||||
intv := data[i]
|
intv := data[i]
|
||||||
intv.RLock()
|
intv.RLock()
|
||||||
for name, val := range intv.Gauges {
|
for name, val := range intv.Gauges {
|
||||||
fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val)
|
fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val.Value)
|
||||||
}
|
}
|
||||||
for name, vals := range intv.Points {
|
for name, vals := range intv.Points {
|
||||||
for _, val := range vals {
|
for _, val := range vals {
|
||||||
|
@ -87,10 +87,10 @@ func (i *InmemSignal) dumpStats() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name, agg := range intv.Counters {
|
for name, agg := range intv.Counters {
|
||||||
fmt.Fprintf(buf, "[%v][C] '%s': %s\n", intv.Interval, name, agg)
|
fmt.Fprintf(buf, "[%v][C] '%s': %s\n", intv.Interval, name, agg.AggregateSample)
|
||||||
}
|
}
|
||||||
for name, agg := range intv.Samples {
|
for name, agg := range intv.Samples {
|
||||||
fmt.Fprintf(buf, "[%v][S] '%s': %s\n", intv.Interval, name, agg)
|
fmt.Fprintf(buf, "[%v][S] '%s': %s\n", intv.Interval, name, agg.AggregateSample)
|
||||||
}
|
}
|
||||||
intv.RUnlock()
|
intv.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,41 @@ package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Label struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Metrics) SetGauge(key []string, val float32) {
|
func (m *Metrics) SetGauge(key []string, val float32) {
|
||||||
if m.HostName != "" && m.EnableHostname {
|
m.SetGaugeWithLabels(key, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) SetGaugeWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
if m.HostName != "" {
|
||||||
|
if m.EnableHostnameLabel {
|
||||||
|
labels = append(labels, Label{"host", m.HostName})
|
||||||
|
} else if m.EnableHostname {
|
||||||
key = insert(0, m.HostName, key)
|
key = insert(0, m.HostName, key)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if m.EnableTypePrefix {
|
if m.EnableTypePrefix {
|
||||||
key = insert(0, "gauge", key)
|
key = insert(0, "gauge", key)
|
||||||
}
|
}
|
||||||
if m.ServiceName != "" {
|
if m.ServiceName != "" {
|
||||||
|
if m.EnableServiceLabel {
|
||||||
|
labels = append(labels, Label{"service", m.ServiceName})
|
||||||
|
} else {
|
||||||
key = insert(0, m.ServiceName, key)
|
key = insert(0, m.ServiceName, key)
|
||||||
}
|
}
|
||||||
m.sink.SetGauge(key, val)
|
}
|
||||||
|
if !m.allowMetric(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.sink.SetGaugeWithLabels(key, val, labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) EmitKey(key []string, val float32) {
|
func (m *Metrics) EmitKey(key []string, val float32) {
|
||||||
|
@ -25,40 +46,98 @@ func (m *Metrics) EmitKey(key []string, val float32) {
|
||||||
if m.ServiceName != "" {
|
if m.ServiceName != "" {
|
||||||
key = insert(0, m.ServiceName, key)
|
key = insert(0, m.ServiceName, key)
|
||||||
}
|
}
|
||||||
|
if !m.allowMetric(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
m.sink.EmitKey(key, val)
|
m.sink.EmitKey(key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) IncrCounter(key []string, val float32) {
|
func (m *Metrics) IncrCounter(key []string, val float32) {
|
||||||
|
m.IncrCounterWithLabels(key, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) IncrCounterWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
if m.HostName != "" && m.EnableHostnameLabel {
|
||||||
|
labels = append(labels, Label{"host", m.HostName})
|
||||||
|
}
|
||||||
if m.EnableTypePrefix {
|
if m.EnableTypePrefix {
|
||||||
key = insert(0, "counter", key)
|
key = insert(0, "counter", key)
|
||||||
}
|
}
|
||||||
if m.ServiceName != "" {
|
if m.ServiceName != "" {
|
||||||
|
if m.EnableServiceLabel {
|
||||||
|
labels = append(labels, Label{"service", m.ServiceName})
|
||||||
|
} else {
|
||||||
key = insert(0, m.ServiceName, key)
|
key = insert(0, m.ServiceName, key)
|
||||||
}
|
}
|
||||||
m.sink.IncrCounter(key, val)
|
}
|
||||||
|
if !m.allowMetric(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.sink.IncrCounterWithLabels(key, val, labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) AddSample(key []string, val float32) {
|
func (m *Metrics) AddSample(key []string, val float32) {
|
||||||
|
m.AddSampleWithLabels(key, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) AddSampleWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
if m.HostName != "" && m.EnableHostnameLabel {
|
||||||
|
labels = append(labels, Label{"host", m.HostName})
|
||||||
|
}
|
||||||
if m.EnableTypePrefix {
|
if m.EnableTypePrefix {
|
||||||
key = insert(0, "sample", key)
|
key = insert(0, "sample", key)
|
||||||
}
|
}
|
||||||
if m.ServiceName != "" {
|
if m.ServiceName != "" {
|
||||||
|
if m.EnableServiceLabel {
|
||||||
|
labels = append(labels, Label{"service", m.ServiceName})
|
||||||
|
} else {
|
||||||
key = insert(0, m.ServiceName, key)
|
key = insert(0, m.ServiceName, key)
|
||||||
}
|
}
|
||||||
m.sink.AddSample(key, val)
|
}
|
||||||
|
if !m.allowMetric(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.sink.AddSampleWithLabels(key, val, labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) MeasureSince(key []string, start time.Time) {
|
func (m *Metrics) MeasureSince(key []string, start time.Time) {
|
||||||
|
m.MeasureSinceWithLabels(key, start, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) MeasureSinceWithLabels(key []string, start time.Time, labels []Label) {
|
||||||
|
if m.HostName != "" && m.EnableHostnameLabel {
|
||||||
|
labels = append(labels, Label{"host", m.HostName})
|
||||||
|
}
|
||||||
if m.EnableTypePrefix {
|
if m.EnableTypePrefix {
|
||||||
key = insert(0, "timer", key)
|
key = insert(0, "timer", key)
|
||||||
}
|
}
|
||||||
if m.ServiceName != "" {
|
if m.ServiceName != "" {
|
||||||
|
if m.EnableServiceLabel {
|
||||||
|
labels = append(labels, Label{"service", m.ServiceName})
|
||||||
|
} else {
|
||||||
key = insert(0, m.ServiceName, key)
|
key = insert(0, m.ServiceName, key)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if !m.allowMetric(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
elapsed := now.Sub(start)
|
elapsed := now.Sub(start)
|
||||||
msec := float32(elapsed.Nanoseconds()) / float32(m.TimerGranularity)
|
msec := float32(elapsed.Nanoseconds()) / float32(m.TimerGranularity)
|
||||||
m.sink.AddSample(key, msec)
|
m.sink.AddSampleWithLabels(key, msec, labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether the metric should be allowed based on configured prefix filters
|
||||||
|
func (m *Metrics) allowMetric(key []string) bool {
|
||||||
|
if m.filter == nil || m.filter.Len() == 0 {
|
||||||
|
return m.Config.FilterDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
_, allowed, ok := m.filter.Root().LongestPrefix([]byte(strings.Join(key, ".")))
|
||||||
|
if !ok {
|
||||||
|
return m.Config.FilterDefault
|
||||||
|
}
|
||||||
|
return allowed.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Periodically collects runtime stats to publish
|
// Periodically collects runtime stats to publish
|
||||||
|
|
|
@ -10,31 +10,41 @@ import (
|
||||||
type MetricSink interface {
|
type MetricSink interface {
|
||||||
// A Gauge should retain the last value it is set to
|
// A Gauge should retain the last value it is set to
|
||||||
SetGauge(key []string, val float32)
|
SetGauge(key []string, val float32)
|
||||||
|
SetGaugeWithLabels(key []string, val float32, labels []Label)
|
||||||
|
|
||||||
// Should emit a Key/Value pair for each call
|
// Should emit a Key/Value pair for each call
|
||||||
EmitKey(key []string, val float32)
|
EmitKey(key []string, val float32)
|
||||||
|
|
||||||
// Counters should accumulate values
|
// Counters should accumulate values
|
||||||
IncrCounter(key []string, val float32)
|
IncrCounter(key []string, val float32)
|
||||||
|
IncrCounterWithLabels(key []string, val float32, labels []Label)
|
||||||
|
|
||||||
// Samples are for timing information, where quantiles are used
|
// Samples are for timing information, where quantiles are used
|
||||||
AddSample(key []string, val float32)
|
AddSample(key []string, val float32)
|
||||||
|
AddSampleWithLabels(key []string, val float32, labels []Label)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlackholeSink is used to just blackhole messages
|
// BlackholeSink is used to just blackhole messages
|
||||||
type BlackholeSink struct{}
|
type BlackholeSink struct{}
|
||||||
|
|
||||||
func (*BlackholeSink) SetGauge(key []string, val float32) {}
|
func (*BlackholeSink) SetGauge(key []string, val float32) {}
|
||||||
|
func (*BlackholeSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {}
|
||||||
func (*BlackholeSink) EmitKey(key []string, val float32) {}
|
func (*BlackholeSink) EmitKey(key []string, val float32) {}
|
||||||
func (*BlackholeSink) IncrCounter(key []string, val float32) {}
|
func (*BlackholeSink) IncrCounter(key []string, val float32) {}
|
||||||
|
func (*BlackholeSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {}
|
||||||
func (*BlackholeSink) AddSample(key []string, val float32) {}
|
func (*BlackholeSink) AddSample(key []string, val float32) {}
|
||||||
|
func (*BlackholeSink) AddSampleWithLabels(key []string, val float32, labels []Label) {}
|
||||||
|
|
||||||
// FanoutSink is used to sink to fanout values to multiple sinks
|
// FanoutSink is used to sink to fanout values to multiple sinks
|
||||||
type FanoutSink []MetricSink
|
type FanoutSink []MetricSink
|
||||||
|
|
||||||
func (fh FanoutSink) SetGauge(key []string, val float32) {
|
func (fh FanoutSink) SetGauge(key []string, val float32) {
|
||||||
|
fh.SetGaugeWithLabels(key, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fh FanoutSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
|
||||||
for _, s := range fh {
|
for _, s := range fh {
|
||||||
s.SetGauge(key, val)
|
s.SetGaugeWithLabels(key, val, labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,14 +55,22 @@ func (fh FanoutSink) EmitKey(key []string, val float32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fh FanoutSink) IncrCounter(key []string, val float32) {
|
func (fh FanoutSink) IncrCounter(key []string, val float32) {
|
||||||
|
fh.IncrCounterWithLabels(key, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fh FanoutSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
|
||||||
for _, s := range fh {
|
for _, s := range fh {
|
||||||
s.IncrCounter(key, val)
|
s.IncrCounterWithLabels(key, val, labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fh FanoutSink) AddSample(key []string, val float32) {
|
func (fh FanoutSink) AddSample(key []string, val float32) {
|
||||||
|
fh.AddSampleWithLabels(key, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fh FanoutSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
|
||||||
for _, s := range fh {
|
for _, s := range fh {
|
||||||
s.AddSample(key, val)
|
s.AddSampleWithLabels(key, val, labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-immutable-radix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is used to configure metrics settings
|
// Config is used to configure metrics settings
|
||||||
|
@ -11,10 +13,15 @@ type Config struct {
|
||||||
ServiceName string // Prefixed with keys to seperate services
|
ServiceName string // Prefixed with keys to seperate services
|
||||||
HostName string // Hostname to use. If not provided and EnableHostname, it will be os.Hostname
|
HostName string // Hostname to use. If not provided and EnableHostname, it will be os.Hostname
|
||||||
EnableHostname bool // Enable prefixing gauge values with hostname
|
EnableHostname bool // Enable prefixing gauge values with hostname
|
||||||
|
EnableHostnameLabel bool // Enable adding hostname to labels
|
||||||
|
EnableServiceLabel bool // Enable adding service to labels
|
||||||
EnableRuntimeMetrics bool // Enables profiling of runtime metrics (GC, Goroutines, Memory)
|
EnableRuntimeMetrics bool // Enables profiling of runtime metrics (GC, Goroutines, Memory)
|
||||||
EnableTypePrefix bool // Prefixes key with a type ("counter", "gauge", "timer")
|
EnableTypePrefix bool // Prefixes key with a type ("counter", "gauge", "timer")
|
||||||
TimerGranularity time.Duration // Granularity of timers.
|
TimerGranularity time.Duration // Granularity of timers.
|
||||||
ProfileInterval time.Duration // Interval to profile runtime metrics
|
ProfileInterval time.Duration // Interval to profile runtime metrics
|
||||||
|
AllowedPrefixes []string // A list of metric prefixes to allow, with '.' as the separator
|
||||||
|
BlockedPrefixes []string // A list of metric prefixes to block, with '.' as the separator
|
||||||
|
FilterDefault bool // Whether to allow metrics by default
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metrics represents an instance of a metrics sink that can
|
// Metrics represents an instance of a metrics sink that can
|
||||||
|
@ -23,6 +30,7 @@ type Metrics struct {
|
||||||
Config
|
Config
|
||||||
lastNumGC uint32
|
lastNumGC uint32
|
||||||
sink MetricSink
|
sink MetricSink
|
||||||
|
filter *iradix.Tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared global metrics instance
|
// Shared global metrics instance
|
||||||
|
@ -43,6 +51,7 @@ func DefaultConfig(serviceName string) *Config {
|
||||||
EnableTypePrefix: false, // Disable type prefix
|
EnableTypePrefix: false, // Disable type prefix
|
||||||
TimerGranularity: time.Millisecond, // Timers are in milliseconds
|
TimerGranularity: time.Millisecond, // Timers are in milliseconds
|
||||||
ProfileInterval: time.Second, // Poll runtime every second
|
ProfileInterval: time.Second, // Poll runtime every second
|
||||||
|
FilterDefault: true, // Don't filter metrics by default
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to get the hostname
|
// Try to get the hostname
|
||||||
|
@ -56,6 +65,14 @@ func New(conf *Config, sink MetricSink) (*Metrics, error) {
|
||||||
met := &Metrics{}
|
met := &Metrics{}
|
||||||
met.Config = *conf
|
met.Config = *conf
|
||||||
met.sink = sink
|
met.sink = sink
|
||||||
|
met.filter = iradix.New()
|
||||||
|
|
||||||
|
for _, prefix := range conf.AllowedPrefixes {
|
||||||
|
met.filter, _, _ = met.filter.Insert([]byte(prefix), true)
|
||||||
|
}
|
||||||
|
for _, prefix := range conf.BlockedPrefixes {
|
||||||
|
met.filter, _, _ = met.filter.Insert([]byte(prefix), false)
|
||||||
|
}
|
||||||
|
|
||||||
// Start the runtime collector
|
// Start the runtime collector
|
||||||
if conf.EnableRuntimeMetrics {
|
if conf.EnableRuntimeMetrics {
|
||||||
|
@ -79,6 +96,10 @@ func SetGauge(key []string, val float32) {
|
||||||
globalMetrics.Load().(*Metrics).SetGauge(key, val)
|
globalMetrics.Load().(*Metrics).SetGauge(key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetGaugeWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
globalMetrics.Load().(*Metrics).SetGaugeWithLabels(key, val, labels)
|
||||||
|
}
|
||||||
|
|
||||||
func EmitKey(key []string, val float32) {
|
func EmitKey(key []string, val float32) {
|
||||||
globalMetrics.Load().(*Metrics).EmitKey(key, val)
|
globalMetrics.Load().(*Metrics).EmitKey(key, val)
|
||||||
}
|
}
|
||||||
|
@ -87,10 +108,22 @@ func IncrCounter(key []string, val float32) {
|
||||||
globalMetrics.Load().(*Metrics).IncrCounter(key, val)
|
globalMetrics.Load().(*Metrics).IncrCounter(key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IncrCounterWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
globalMetrics.Load().(*Metrics).IncrCounterWithLabels(key, val, labels)
|
||||||
|
}
|
||||||
|
|
||||||
func AddSample(key []string, val float32) {
|
func AddSample(key []string, val float32) {
|
||||||
globalMetrics.Load().(*Metrics).AddSample(key, val)
|
globalMetrics.Load().(*Metrics).AddSample(key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddSampleWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
globalMetrics.Load().(*Metrics).AddSampleWithLabels(key, val, labels)
|
||||||
|
}
|
||||||
|
|
||||||
func MeasureSince(key []string, start time.Time) {
|
func MeasureSince(key []string, start time.Time) {
|
||||||
globalMetrics.Load().(*Metrics).MeasureSince(key, start)
|
globalMetrics.Load().(*Metrics).MeasureSince(key, start)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MeasureSinceWithLabels(key []string, start time.Time, labels []Label) {
|
||||||
|
globalMetrics.Load().(*Metrics).MeasureSinceWithLabels(key, start, labels)
|
||||||
|
}
|
||||||
|
|
|
@ -50,6 +50,11 @@ func (s *StatsdSink) SetGauge(key []string, val float32) {
|
||||||
s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
|
s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StatsdSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
flatKey := s.flattenKeyLabels(key, labels)
|
||||||
|
s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StatsdSink) EmitKey(key []string, val float32) {
|
func (s *StatsdSink) EmitKey(key []string, val float32) {
|
||||||
flatKey := s.flattenKey(key)
|
flatKey := s.flattenKey(key)
|
||||||
s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val))
|
s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val))
|
||||||
|
@ -60,11 +65,21 @@ func (s *StatsdSink) IncrCounter(key []string, val float32) {
|
||||||
s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
|
s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StatsdSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
flatKey := s.flattenKeyLabels(key, labels)
|
||||||
|
s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StatsdSink) AddSample(key []string, val float32) {
|
func (s *StatsdSink) AddSample(key []string, val float32) {
|
||||||
flatKey := s.flattenKey(key)
|
flatKey := s.flattenKey(key)
|
||||||
s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
|
s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StatsdSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
flatKey := s.flattenKeyLabels(key, labels)
|
||||||
|
s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
|
||||||
|
}
|
||||||
|
|
||||||
// Flattens the key for formatting, removes spaces
|
// Flattens the key for formatting, removes spaces
|
||||||
func (s *StatsdSink) flattenKey(parts []string) string {
|
func (s *StatsdSink) flattenKey(parts []string) string {
|
||||||
joined := strings.Join(parts, ".")
|
joined := strings.Join(parts, ".")
|
||||||
|
@ -80,6 +95,15 @@ func (s *StatsdSink) flattenKey(parts []string) string {
|
||||||
}, joined)
|
}, joined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flattens the key along with labels for formatting, removes spaces
|
||||||
|
func (s *StatsdSink) flattenKeyLabels(parts []string, labels []Label) string {
|
||||||
|
fullName := parts
|
||||||
|
for _, label := range labels {
|
||||||
|
fullName = append(parts, label.Value)
|
||||||
|
}
|
||||||
|
return s.flattenKey(fullName)
|
||||||
|
}
|
||||||
|
|
||||||
// Does a non-blocking push to the metrics queue
|
// Does a non-blocking push to the metrics queue
|
||||||
func (s *StatsdSink) pushMetric(m string) {
|
func (s *StatsdSink) pushMetric(m string) {
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -50,6 +50,11 @@ func (s *StatsiteSink) SetGauge(key []string, val float32) {
|
||||||
s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
|
s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StatsiteSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
flatKey := s.flattenKeyLabels(key, labels)
|
||||||
|
s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StatsiteSink) EmitKey(key []string, val float32) {
|
func (s *StatsiteSink) EmitKey(key []string, val float32) {
|
||||||
flatKey := s.flattenKey(key)
|
flatKey := s.flattenKey(key)
|
||||||
s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val))
|
s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val))
|
||||||
|
@ -60,11 +65,21 @@ func (s *StatsiteSink) IncrCounter(key []string, val float32) {
|
||||||
s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
|
s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StatsiteSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
flatKey := s.flattenKeyLabels(key, labels)
|
||||||
|
s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StatsiteSink) AddSample(key []string, val float32) {
|
func (s *StatsiteSink) AddSample(key []string, val float32) {
|
||||||
flatKey := s.flattenKey(key)
|
flatKey := s.flattenKey(key)
|
||||||
s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
|
s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StatsiteSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
|
||||||
|
flatKey := s.flattenKeyLabels(key, labels)
|
||||||
|
s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
|
||||||
|
}
|
||||||
|
|
||||||
// Flattens the key for formatting, removes spaces
|
// Flattens the key for formatting, removes spaces
|
||||||
func (s *StatsiteSink) flattenKey(parts []string) string {
|
func (s *StatsiteSink) flattenKey(parts []string) string {
|
||||||
joined := strings.Join(parts, ".")
|
joined := strings.Join(parts, ".")
|
||||||
|
@ -80,6 +95,14 @@ func (s *StatsiteSink) flattenKey(parts []string) string {
|
||||||
}, joined)
|
}, joined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flattens the key along with labels for formatting, removes spaces
|
||||||
|
func (s *StatsiteSink) flattenKeyLabels(parts []string, labels []Label) string {
|
||||||
|
for _, label := range labels {
|
||||||
|
parts = append(parts, label.Value)
|
||||||
|
}
|
||||||
|
return s.flattenKey(parts)
|
||||||
|
}
|
||||||
|
|
||||||
// Does a non-blocking push to the metrics queue
|
// Does a non-blocking push to the metrics queue
|
||||||
func (s *StatsiteSink) pushMetric(m string) {
|
func (s *StatsiteSink) pushMetric(m string) {
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
{"checksumSHA1":"AzjRkOQtVBTwIw4RJLTygFhJs3s=","path":"github.com/Microsoft/go-winio","revision":"c4dc1301f1dc0307acd38e611aa375a64dfe0642","revisionTime":"2017-07-12T04:46:15Z"},
|
{"checksumSHA1":"AzjRkOQtVBTwIw4RJLTygFhJs3s=","path":"github.com/Microsoft/go-winio","revision":"c4dc1301f1dc0307acd38e611aa375a64dfe0642","revisionTime":"2017-07-12T04:46:15Z"},
|
||||||
{"checksumSHA1":"9NR0rrcAT5J76C5xMS4AVksS9o0=","path":"github.com/StackExchange/wmi","revision":"e54cbda6595d7293a7a468ccf9525f6bc8887f99","revisionTime":"2016-08-11T21:45:55Z"},
|
{"checksumSHA1":"9NR0rrcAT5J76C5xMS4AVksS9o0=","path":"github.com/StackExchange/wmi","revision":"e54cbda6595d7293a7a468ccf9525f6bc8887f99","revisionTime":"2016-08-11T21:45:55Z"},
|
||||||
{"checksumSHA1":"l0iFqayYAaEip6Olaq3/LCOa/Sg=","path":"github.com/armon/circbuf","revision":"bbbad097214e2918d8543d5201d12bfd7bca254d","revisionTime":"2015-08-27T00:49:46Z"},
|
{"checksumSHA1":"l0iFqayYAaEip6Olaq3/LCOa/Sg=","path":"github.com/armon/circbuf","revision":"bbbad097214e2918d8543d5201d12bfd7bca254d","revisionTime":"2015-08-27T00:49:46Z"},
|
||||||
{"checksumSHA1":"M+ZeYktTT2wak9ZvQ0OZBbIHAGo=","path":"github.com/armon/go-metrics","revision":"f036747b9d0e8590f175a5d654a2194a7d9df4b5","revisionTime":"2017-06-01T21:44:32Z"},
|
{"checksumSHA1":"1fTBW3jW8+cYV5UNKrAMXsQhqnQ=","path":"github.com/armon/go-metrics","revision":"efce74234d0fcaac1bd5b2a32d6d3afd108c4498","revisionTime":"2017-08-08T03:49:35Z"},
|
||||||
{"checksumSHA1":"OmqT9Y1mAHvlAKeJh0jBHC9SH78=","path":"github.com/armon/go-metrics/circonus","revision":"3df31a1ada83e310c2e24b267c8e8b68836547b4","revisionTime":"2016-07-17T04:34:58Z"},
|
{"checksumSHA1":"xCsGGM9TKBogZDfSN536KtQdLko=","path":"github.com/armon/go-metrics/circonus","revision":"ded85ed431a7aee3f3af79f082b704d948058f64","revisionTime":"2017-08-07T19:17:41Z"},
|
||||||
{"checksumSHA1":"mAzNU3zeZGEwqjDT4ZkspFvx3TI=","path":"github.com/armon/go-metrics/datadog","revision":"3df31a1ada83e310c2e24b267c8e8b68836547b4","revisionTime":"2016-07-17T04:34:58Z"},
|
{"checksumSHA1":"Dt0n1sSivvvdZQdzc4Hu/yOG+T0=","path":"github.com/armon/go-metrics/datadog","revision":"ded85ed431a7aee3f3af79f082b704d948058f64","revisionTime":"2017-08-07T19:17:41Z"},
|
||||||
{"checksumSHA1":"gNO0JNpLzYOdInGeq7HqMZUzx9M=","path":"github.com/armon/go-radix","revision":"4239b77079c7b5d1243b7b4736304ce8ddb6f0f2","revisionTime":"2016-01-15T23:47:25Z"},
|
{"checksumSHA1":"gNO0JNpLzYOdInGeq7HqMZUzx9M=","path":"github.com/armon/go-radix","revision":"4239b77079c7b5d1243b7b4736304ce8ddb6f0f2","revisionTime":"2016-01-15T23:47:25Z"},
|
||||||
{"checksumSHA1":"dvd7Su+WNmHRP1+w1HezrPUCDsc=","path":"github.com/bgentry/speakeasy","revision":"e1439544d8ecd0f3e9373a636d447668096a8f81","revisionTime":"2016-05-20T23:26:10Z"},
|
{"checksumSHA1":"dvd7Su+WNmHRP1+w1HezrPUCDsc=","path":"github.com/bgentry/speakeasy","revision":"e1439544d8ecd0f3e9373a636d447668096a8f81","revisionTime":"2016-05-20T23:26:10Z"},
|
||||||
{"checksumSHA1":"twtRfb6484vfr2qqjiFkLThTjcQ=","path":"github.com/bgentry/speakeasy/example","revision":"e1439544d8ecd0f3e9373a636d447668096a8f81","revisionTime":"2016-05-20T23:26:10Z"},
|
{"checksumSHA1":"twtRfb6484vfr2qqjiFkLThTjcQ=","path":"github.com/bgentry/speakeasy/example","revision":"e1439544d8ecd0f3e9373a636d447668096a8f81","revisionTime":"2016-05-20T23:26:10Z"},
|
||||||
|
|
Loading…
Reference in New Issue