diff --git a/examples/random/main.go b/examples/random/main.go index 1da9bd1e5..f0789b9ba 100644 --- a/examples/random/main.go +++ b/examples/random/main.go @@ -90,9 +90,13 @@ func main() { for { v := (rand.NormFloat64() * *normDomain) + *normMean rpcDurations.WithLabelValues("normal").Observe(v) - rpcDurationsHistogram.ObserveWithExemplar( - // Demonstrate exemplar support with a dummy ID. This would be - // something like a trace ID in a real application. + // Demonstrate exemplar support with a dummy ID. This + // would be something like a trace ID in a real + // application. Note the necessary type assertion. We + // already know that rpcDurationsHistogram implements + // the ExemplarObserver interface and thus don't need to + // check the outcome of the tipe assertion. + rpcDurationsHistogram.(prometheus.ExemplarObserver).ObserveWithExemplar( v, prometheus.Labels{"dummyID": fmt.Sprint(rand.Intn(100000))}, ) time.Sleep(time.Duration(75*oscillationFactor()) * time.Millisecond) diff --git a/prometheus/counter.go b/prometheus/counter.go index b0b8d8ab3..a1877badb 100644 --- a/prometheus/counter.go +++ b/prometheus/counter.go @@ -41,13 +41,18 @@ type Counter interface { // Add adds the given value to the counter. It panics if the value is < // 0. Add(float64) - // AddWithExemplar works like Add but also replaces the currently saved - // exemplar (if any) with a new one, created from the provided value, - // the current time as timestamp, and the provided labels. Empty Labels - // will lead to a valid (label-less) exemplar. But if Labels is nil, the - // current exemplar is left in place. This method panics if the value is - // < 0, if any of the provided labels are invalid, or if the provided - // labels contain more than 64 runes in total. +} + +// ExemplarAdder is implemented by Counters that offer the option of adding a +// value to the Counter together with an exemplar. Its AddWithExemplar method +// works like the Add method of the Counter interface but also replaces the +// currently saved exemplar (if any) with a new one, created from the provided +// value, the current time as timestamp, and the provided labels. Empty Labels +// will lead to a valid (label-less) exemplar. But if Labels is nil, the current +// exemplar is left in place. AddWithExemplar panics if the value is < 0, if any +// of the provided labels are invalid, or if the provided labels contain more +// than 64 runes in total. +type ExemplarAdder interface { AddWithExemplar(value float64, exemplar Labels) } @@ -56,6 +61,9 @@ type CounterOpts Opts // NewCounter creates a new Counter based on the provided CounterOpts. // +// The returned implementation also implements ExemplarAdder. It is safe to +// perform the corresponding type assertion. +// // The returned implementation tracks the counter value in two separate // variables, a float64 and a uint64. The latter is used to track calls of the // Inc method and calls of the Add method with a value that can be represented diff --git a/prometheus/histogram.go b/prometheus/histogram.go index 5b1475718..4271f438a 100644 --- a/prometheus/histogram.go +++ b/prometheus/histogram.go @@ -48,15 +48,6 @@ type Histogram interface { // Observe adds a single observation to the histogram. Observe(float64) - // ObserveWithExemplar works like Observe but also replaces the - // currently saved exemplar for the relevant bucket (possibly none) with - // a new one, created from the provided value, the current time as - // timestamp, and the provided Labels. Empty Labels will lead to a valid - // (label-less) exemplar. But if Labels is nil, the current exemplar in - // the relevant bucket is left in place. This method panics if any of - // the provided labels are invalid or if the provided labels contain - // more than 64 runes in total. - ObserveWithExemplar(value float64, exemplar Labels) } // bucketLabel is used for the label that defines the upper bound of a @@ -161,6 +152,10 @@ type HistogramOpts struct { // NewHistogram creates a new Histogram based on the provided HistogramOpts. It // panics if the buckets in HistogramOpts are not in strictly increasing order. +// +// The returned implementation also implements ExemplarObserver. It is safe to +// perform the corresponding type assertion. Exemplars are tracked separately +// for each bucket. func NewHistogram(opts HistogramOpts) Histogram { return newHistogram( NewDesc( diff --git a/prometheus/histogram_test.go b/prometheus/histogram_test.go index 561128d36..2e8f4b8c1 100644 --- a/prometheus/histogram_test.go +++ b/prometheus/histogram_test.go @@ -189,7 +189,7 @@ func TestHistogramConcurrency(t *testing.T) { if n%2 == 0 { sum.Observe(v) } else { - sum.ObserveWithExemplar(v, Labels{"foo": "bar"}) + sum.(ExemplarObserver).ObserveWithExemplar(v, Labels{"foo": "bar"}) } } end.Done() diff --git a/prometheus/observer.go b/prometheus/observer.go index 5806cd09e..44128016f 100644 --- a/prometheus/observer.go +++ b/prometheus/observer.go @@ -50,3 +50,15 @@ type ObserverVec interface { Collector } + +// ExemplarObserver is implemented by Observers that offer the option of +// observing a value together with an exemplar. Its ObserveWithExemplar method +// works like the Observe method of an Observer but also replaces the currently +// saved exemplar (if any) with a new one, created from the provided value, the +// current time as timestamp, and the provided Labels. Empty Labels will lead to +// a valid (label-less) exemplar. But if Labels is nil, the current exemplar is +// left in place. ObserveWithExemplar panics if any of the provided labels are +// invalid or if the provided labels contain more than 64 runes in total. +type ExemplarObserver interface { + ObserveWithExemplar(value float64, exemplar Labels) +}