khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 1 | // 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 | |
| 14 | package prometheus |
| 15 | |
| 16 | import ( |
| 17 | "errors" |
| 18 | "math" |
| 19 | "sync/atomic" |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 20 | "time" |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 21 | |
| 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. |
| 34 | type 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 | |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 46 | // 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. |
| 55 | type ExemplarAdder interface { |
| 56 | AddWithExemplar(value float64, exemplar Labels) |
| 57 | } |
| 58 | |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 59 | // CounterOpts is an alias for Opts. See there for doc comments. |
| 60 | type CounterOpts Opts |
| 61 | |
| 62 | // NewCounter creates a new Counter based on the provided CounterOpts. |
| 63 | // |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 64 | // The returned implementation also implements ExemplarAdder. It is safe to |
| 65 | // perform the corresponding type assertion. |
| 66 | // |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 67 | // 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. |
| 74 | func NewCounter(opts CounterOpts) Counter { |
| 75 | desc := NewDesc( |
| 76 | BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), |
| 77 | opts.Help, |
| 78 | nil, |
| 79 | opts.ConstLabels, |
| 80 | ) |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 81 | result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now} |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 82 | result.init(result) // Init self-collection. |
| 83 | return result |
| 84 | } |
| 85 | |
| 86 | type 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 |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 98 | exemplar atomic.Value // Containing nil or a *dto.Exemplar. |
| 99 | |
| 100 | now func() time.Time // To mock out time.Now() for testing. |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 101 | } |
| 102 | |
| 103 | func (c *counter) Desc() *Desc { |
| 104 | return c.desc |
| 105 | } |
| 106 | |
| 107 | func (c *counter) Add(v float64) { |
| 108 | if v < 0 { |
| 109 | panic(errors.New("counter cannot decrease in value")) |
| 110 | } |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 111 | |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 112 | 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 | |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 127 | func (c *counter) AddWithExemplar(v float64, e Labels) { |
| 128 | c.Add(v) |
| 129 | c.updateExemplar(v, e) |
| 130 | } |
| 131 | |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 132 | func (c *counter) Inc() { |
| 133 | atomic.AddUint64(&c.valInt, 1) |
| 134 | } |
| 135 | |
| 136 | func (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 | |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 141 | 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 | |
| 149 | func (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) |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 158 | } |
| 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. |
| 165 | type CounterVec struct { |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 166 | *MetricVec |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | // NewCounterVec creates a new CounterVec based on the provided CounterOpts and |
| 170 | // partitioned by the given label names. |
| 171 | func 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{ |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 179 | MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 180 | if len(lvs) != len(desc.variableLabels) { |
| 181 | panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) |
| 182 | } |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 183 | result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now} |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 184 | result.init(result) // Init self-collection. |
| 185 | return result |
| 186 | }), |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | // GetMetricWithLabelValues returns the Counter for the given slice of label |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 191 | // values (same order as the variable labels in Desc). If that combination of |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 192 | // 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 |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 205 | // number of variable labels in Desc (minus any curried labels). |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 206 | // |
| 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. |
| 213 | func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) { |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 214 | metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...) |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 215 | 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 |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 222 | // must match those of the variable labels in Desc). If that label map is |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 223 | // 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 |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 228 | // with those of the variable labels in Desc (minus any curried labels). |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 229 | // |
| 230 | // This method is used for the same purpose as |
| 231 | // GetMetricWithLabelValues(...string). See there for pros and cons of the two |
| 232 | // methods. |
| 233 | func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) { |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 234 | metric, err := v.MetricVec.GetMetricWith(labels) |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 235 | 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) |
| 245 | func (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) |
| 256 | func (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. |
| 277 | func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) { |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 278 | vec, err := v.MetricVec.CurryWith(labels) |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 279 | 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. |
| 287 | func (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. |
| 299 | type 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. |
khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame^] | 312 | // |
| 313 | // Check out the ExampleGaugeFunc examples for the similar GaugeFunc. |
khenaidoo | 59ce9dd | 2019-11-11 13:05:32 -0500 | [diff] [blame] | 314 | func 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 | } |