blob: 3f8fd790d66ab22cb927d26302973afe65b762c2 [file] [log] [blame]
khenaidoo59ce9dd2019-11-11 13:05:32 -05001// Copyright 2014 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package prometheus
15
16import (
17 "errors"
18 "math"
19 "sync/atomic"
khenaidoo26721882021-08-11 17:42:52 -040020 "time"
khenaidoo59ce9dd2019-11-11 13:05:32 -050021
22 dto "github.com/prometheus/client_model/go"
23)
24
25// Counter is a Metric that represents a single numerical value that only ever
26// goes up. That implies that it cannot be used to count items whose number can
27// also go down, e.g. the number of currently running goroutines. Those
28// "counters" are represented by Gauges.
29//
30// A Counter is typically used to count requests served, tasks completed, errors
31// occurred, etc.
32//
33// To create Counter instances, use NewCounter.
34type Counter interface {
35 Metric
36 Collector
37
38 // Inc increments the counter by 1. Use Add to increment it by arbitrary
39 // non-negative values.
40 Inc()
41 // Add adds the given value to the counter. It panics if the value is <
42 // 0.
43 Add(float64)
44}
45
khenaidoo26721882021-08-11 17:42:52 -040046// ExemplarAdder is implemented by Counters that offer the option of adding a
47// value to the Counter together with an exemplar. Its AddWithExemplar method
48// works like the Add method of the Counter interface but also replaces the
49// currently saved exemplar (if any) with a new one, created from the provided
50// value, the current time as timestamp, and the provided labels. Empty Labels
51// will lead to a valid (label-less) exemplar. But if Labels is nil, the current
52// exemplar is left in place. AddWithExemplar panics if the value is < 0, if any
53// of the provided labels are invalid, or if the provided labels contain more
54// than 64 runes in total.
55type ExemplarAdder interface {
56 AddWithExemplar(value float64, exemplar Labels)
57}
58
khenaidoo59ce9dd2019-11-11 13:05:32 -050059// CounterOpts is an alias for Opts. See there for doc comments.
60type CounterOpts Opts
61
62// NewCounter creates a new Counter based on the provided CounterOpts.
63//
khenaidoo26721882021-08-11 17:42:52 -040064// The returned implementation also implements ExemplarAdder. It is safe to
65// perform the corresponding type assertion.
66//
khenaidoo59ce9dd2019-11-11 13:05:32 -050067// The returned implementation tracks the counter value in two separate
68// variables, a float64 and a uint64. The latter is used to track calls of the
69// Inc method and calls of the Add method with a value that can be represented
70// as a uint64. This allows atomic increments of the counter with optimal
71// performance. (It is common to have an Inc call in very hot execution paths.)
72// Both internal tracking values are added up in the Write method. This has to
73// be taken into account when it comes to precision and overflow behavior.
74func NewCounter(opts CounterOpts) Counter {
75 desc := NewDesc(
76 BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
77 opts.Help,
78 nil,
79 opts.ConstLabels,
80 )
khenaidoo26721882021-08-11 17:42:52 -040081 result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now}
khenaidoo59ce9dd2019-11-11 13:05:32 -050082 result.init(result) // Init self-collection.
83 return result
84}
85
86type counter struct {
87 // valBits contains the bits of the represented float64 value, while
88 // valInt stores values that are exact integers. Both have to go first
89 // in the struct to guarantee alignment for atomic operations.
90 // http://golang.org/pkg/sync/atomic/#pkg-note-BUG
91 valBits uint64
92 valInt uint64
93
94 selfCollector
95 desc *Desc
96
97 labelPairs []*dto.LabelPair
khenaidoo26721882021-08-11 17:42:52 -040098 exemplar atomic.Value // Containing nil or a *dto.Exemplar.
99
100 now func() time.Time // To mock out time.Now() for testing.
khenaidoo59ce9dd2019-11-11 13:05:32 -0500101}
102
103func (c *counter) Desc() *Desc {
104 return c.desc
105}
106
107func (c *counter) Add(v float64) {
108 if v < 0 {
109 panic(errors.New("counter cannot decrease in value"))
110 }
khenaidoo26721882021-08-11 17:42:52 -0400111
khenaidoo59ce9dd2019-11-11 13:05:32 -0500112 ival := uint64(v)
113 if float64(ival) == v {
114 atomic.AddUint64(&c.valInt, ival)
115 return
116 }
117
118 for {
119 oldBits := atomic.LoadUint64(&c.valBits)
120 newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
121 if atomic.CompareAndSwapUint64(&c.valBits, oldBits, newBits) {
122 return
123 }
124 }
125}
126
khenaidoo26721882021-08-11 17:42:52 -0400127func (c *counter) AddWithExemplar(v float64, e Labels) {
128 c.Add(v)
129 c.updateExemplar(v, e)
130}
131
khenaidoo59ce9dd2019-11-11 13:05:32 -0500132func (c *counter) Inc() {
133 atomic.AddUint64(&c.valInt, 1)
134}
135
136func (c *counter) Write(out *dto.Metric) error {
137 fval := math.Float64frombits(atomic.LoadUint64(&c.valBits))
138 ival := atomic.LoadUint64(&c.valInt)
139 val := fval + float64(ival)
140
khenaidoo26721882021-08-11 17:42:52 -0400141 var exemplar *dto.Exemplar
142 if e := c.exemplar.Load(); e != nil {
143 exemplar = e.(*dto.Exemplar)
144 }
145
146 return populateMetric(CounterValue, val, c.labelPairs, exemplar, out)
147}
148
149func (c *counter) updateExemplar(v float64, l Labels) {
150 if l == nil {
151 return
152 }
153 e, err := newExemplar(v, c.now(), l)
154 if err != nil {
155 panic(err)
156 }
157 c.exemplar.Store(e)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500158}
159
160// CounterVec is a Collector that bundles a set of Counters that all share the
161// same Desc, but have different values for their variable labels. This is used
162// if you want to count the same thing partitioned by various dimensions
163// (e.g. number of HTTP requests, partitioned by response code and
164// method). Create instances with NewCounterVec.
165type CounterVec struct {
khenaidoo26721882021-08-11 17:42:52 -0400166 *MetricVec
khenaidoo59ce9dd2019-11-11 13:05:32 -0500167}
168
169// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
170// partitioned by the given label names.
171func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
172 desc := NewDesc(
173 BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
174 opts.Help,
175 labelNames,
176 opts.ConstLabels,
177 )
178 return &CounterVec{
khenaidoo26721882021-08-11 17:42:52 -0400179 MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
khenaidoo59ce9dd2019-11-11 13:05:32 -0500180 if len(lvs) != len(desc.variableLabels) {
181 panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
182 }
khenaidoo26721882021-08-11 17:42:52 -0400183 result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
khenaidoo59ce9dd2019-11-11 13:05:32 -0500184 result.init(result) // Init self-collection.
185 return result
186 }),
187 }
188}
189
190// GetMetricWithLabelValues returns the Counter for the given slice of label
khenaidoo26721882021-08-11 17:42:52 -0400191// values (same order as the variable labels in Desc). If that combination of
khenaidoo59ce9dd2019-11-11 13:05:32 -0500192// label values is accessed for the first time, a new Counter is created.
193//
194// It is possible to call this method without using the returned Counter to only
195// create the new Counter but leave it at its starting value 0. See also the
196// SummaryVec example.
197//
198// Keeping the Counter for later use is possible (and should be considered if
199// performance is critical), but keep in mind that Reset, DeleteLabelValues and
200// Delete can be used to delete the Counter from the CounterVec. In that case,
201// the Counter will still exist, but it will not be exported anymore, even if a
202// Counter with the same label values is created later.
203//
204// An error is returned if the number of label values is not the same as the
khenaidoo26721882021-08-11 17:42:52 -0400205// number of variable labels in Desc (minus any curried labels).
khenaidoo59ce9dd2019-11-11 13:05:32 -0500206//
207// Note that for more than one label value, this method is prone to mistakes
208// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
209// an alternative to avoid that type of mistake. For higher label numbers, the
210// latter has a much more readable (albeit more verbose) syntax, but it comes
211// with a performance overhead (for creating and processing the Labels map).
212// See also the GaugeVec example.
213func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
khenaidoo26721882021-08-11 17:42:52 -0400214 metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500215 if metric != nil {
216 return metric.(Counter), err
217 }
218 return nil, err
219}
220
221// GetMetricWith returns the Counter for the given Labels map (the label names
khenaidoo26721882021-08-11 17:42:52 -0400222// must match those of the variable labels in Desc). If that label map is
khenaidoo59ce9dd2019-11-11 13:05:32 -0500223// accessed for the first time, a new Counter is created. Implications of
224// creating a Counter without using it and keeping the Counter for later use are
225// the same as for GetMetricWithLabelValues.
226//
227// An error is returned if the number and names of the Labels are inconsistent
khenaidoo26721882021-08-11 17:42:52 -0400228// with those of the variable labels in Desc (minus any curried labels).
khenaidoo59ce9dd2019-11-11 13:05:32 -0500229//
230// This method is used for the same purpose as
231// GetMetricWithLabelValues(...string). See there for pros and cons of the two
232// methods.
233func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
khenaidoo26721882021-08-11 17:42:52 -0400234 metric, err := v.MetricVec.GetMetricWith(labels)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500235 if metric != nil {
236 return metric.(Counter), err
237 }
238 return nil, err
239}
240
241// WithLabelValues works as GetMetricWithLabelValues, but panics where
242// GetMetricWithLabelValues would have returned an error. Not returning an
243// error allows shortcuts like
244// myVec.WithLabelValues("404", "GET").Add(42)
245func (v *CounterVec) WithLabelValues(lvs ...string) Counter {
246 c, err := v.GetMetricWithLabelValues(lvs...)
247 if err != nil {
248 panic(err)
249 }
250 return c
251}
252
253// With works as GetMetricWith, but panics where GetMetricWithLabels would have
254// returned an error. Not returning an error allows shortcuts like
255// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
256func (v *CounterVec) With(labels Labels) Counter {
257 c, err := v.GetMetricWith(labels)
258 if err != nil {
259 panic(err)
260 }
261 return c
262}
263
264// CurryWith returns a vector curried with the provided labels, i.e. the
265// returned vector has those labels pre-set for all labeled operations performed
266// on it. The cardinality of the curried vector is reduced accordingly. The
267// order of the remaining labels stays the same (just with the curried labels
268// taken out of the sequence – which is relevant for the
269// (GetMetric)WithLabelValues methods). It is possible to curry a curried
270// vector, but only with labels not yet used for currying before.
271//
272// The metrics contained in the CounterVec are shared between the curried and
273// uncurried vectors. They are just accessed differently. Curried and uncurried
274// vectors behave identically in terms of collection. Only one must be
275// registered with a given registry (usually the uncurried version). The Reset
276// method deletes all metrics, even if called on a curried vector.
277func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) {
khenaidoo26721882021-08-11 17:42:52 -0400278 vec, err := v.MetricVec.CurryWith(labels)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500279 if vec != nil {
280 return &CounterVec{vec}, err
281 }
282 return nil, err
283}
284
285// MustCurryWith works as CurryWith but panics where CurryWith would have
286// returned an error.
287func (v *CounterVec) MustCurryWith(labels Labels) *CounterVec {
288 vec, err := v.CurryWith(labels)
289 if err != nil {
290 panic(err)
291 }
292 return vec
293}
294
295// CounterFunc is a Counter whose value is determined at collect time by calling a
296// provided function.
297//
298// To create CounterFunc instances, use NewCounterFunc.
299type CounterFunc interface {
300 Metric
301 Collector
302}
303
304// NewCounterFunc creates a new CounterFunc based on the provided
305// CounterOpts. The value reported is determined by calling the given function
306// from within the Write method. Take into account that metric collection may
307// happen concurrently. If that results in concurrent calls to Write, like in
308// the case where a CounterFunc is directly registered with Prometheus, the
309// provided function must be concurrency-safe. The function should also honor
310// the contract for a Counter (values only go up, not down), but compliance will
311// not be checked.
khenaidoo26721882021-08-11 17:42:52 -0400312//
313// Check out the ExampleGaugeFunc examples for the similar GaugeFunc.
khenaidoo59ce9dd2019-11-11 13:05:32 -0500314func NewCounterFunc(opts CounterOpts, function func() float64) CounterFunc {
315 return newValueFunc(NewDesc(
316 BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
317 opts.Help,
318 nil,
319 opts.ConstLabels,
320 ), CounterValue, function)
321}