khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -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 | "math" |
| 18 | "sync/atomic" |
| 19 | "time" |
| 20 | |
| 21 | dto "github.com/prometheus/client_model/go" |
| 22 | ) |
| 23 | |
| 24 | // Gauge is a Metric that represents a single numerical value that can |
| 25 | // arbitrarily go up and down. |
| 26 | // |
| 27 | // A Gauge is typically used for measured values like temperatures or current |
| 28 | // memory usage, but also "counts" that can go up and down, like the number of |
| 29 | // running goroutines. |
| 30 | // |
| 31 | // To create Gauge instances, use NewGauge. |
| 32 | type Gauge interface { |
| 33 | Metric |
| 34 | Collector |
| 35 | |
| 36 | // Set sets the Gauge to an arbitrary value. |
| 37 | Set(float64) |
| 38 | // Inc increments the Gauge by 1. Use Add to increment it by arbitrary |
| 39 | // values. |
| 40 | Inc() |
| 41 | // Dec decrements the Gauge by 1. Use Sub to decrement it by arbitrary |
| 42 | // values. |
| 43 | Dec() |
| 44 | // Add adds the given value to the Gauge. (The value can be negative, |
| 45 | // resulting in a decrease of the Gauge.) |
| 46 | Add(float64) |
| 47 | // Sub subtracts the given value from the Gauge. (The value can be |
| 48 | // negative, resulting in an increase of the Gauge.) |
| 49 | Sub(float64) |
| 50 | |
| 51 | // SetToCurrentTime sets the Gauge to the current Unix time in seconds. |
| 52 | SetToCurrentTime() |
| 53 | } |
| 54 | |
| 55 | // GaugeOpts is an alias for Opts. See there for doc comments. |
| 56 | type GaugeOpts Opts |
| 57 | |
| 58 | // NewGauge creates a new Gauge based on the provided GaugeOpts. |
| 59 | // |
| 60 | // The returned implementation is optimized for a fast Set method. If you have a |
| 61 | // choice for managing the value of a Gauge via Set vs. Inc/Dec/Add/Sub, pick |
| 62 | // the former. For example, the Inc method of the returned Gauge is slower than |
| 63 | // the Inc method of a Counter returned by NewCounter. This matches the typical |
| 64 | // scenarios for Gauges and Counters, where the former tends to be Set-heavy and |
| 65 | // the latter Inc-heavy. |
| 66 | func NewGauge(opts GaugeOpts) Gauge { |
| 67 | desc := NewDesc( |
| 68 | BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), |
| 69 | opts.Help, |
| 70 | nil, |
| 71 | opts.ConstLabels, |
| 72 | ) |
| 73 | result := &gauge{desc: desc, labelPairs: desc.constLabelPairs} |
| 74 | result.init(result) // Init self-collection. |
| 75 | return result |
| 76 | } |
| 77 | |
| 78 | type gauge struct { |
| 79 | // valBits contains the bits of the represented float64 value. It has |
| 80 | // to go first in the struct to guarantee alignment for atomic |
| 81 | // operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG |
| 82 | valBits uint64 |
| 83 | |
| 84 | selfCollector |
| 85 | |
| 86 | desc *Desc |
| 87 | labelPairs []*dto.LabelPair |
| 88 | } |
| 89 | |
| 90 | func (g *gauge) Desc() *Desc { |
| 91 | return g.desc |
| 92 | } |
| 93 | |
| 94 | func (g *gauge) Set(val float64) { |
| 95 | atomic.StoreUint64(&g.valBits, math.Float64bits(val)) |
| 96 | } |
| 97 | |
| 98 | func (g *gauge) SetToCurrentTime() { |
| 99 | g.Set(float64(time.Now().UnixNano()) / 1e9) |
| 100 | } |
| 101 | |
| 102 | func (g *gauge) Inc() { |
| 103 | g.Add(1) |
| 104 | } |
| 105 | |
| 106 | func (g *gauge) Dec() { |
| 107 | g.Add(-1) |
| 108 | } |
| 109 | |
| 110 | func (g *gauge) Add(val float64) { |
| 111 | for { |
| 112 | oldBits := atomic.LoadUint64(&g.valBits) |
| 113 | newBits := math.Float64bits(math.Float64frombits(oldBits) + val) |
| 114 | if atomic.CompareAndSwapUint64(&g.valBits, oldBits, newBits) { |
| 115 | return |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | func (g *gauge) Sub(val float64) { |
| 121 | g.Add(val * -1) |
| 122 | } |
| 123 | |
| 124 | func (g *gauge) Write(out *dto.Metric) error { |
| 125 | val := math.Float64frombits(atomic.LoadUint64(&g.valBits)) |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 126 | return populateMetric(GaugeValue, val, g.labelPairs, nil, out) |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 127 | } |
| 128 | |
| 129 | // GaugeVec is a Collector that bundles a set of Gauges that all share the same |
| 130 | // Desc, but have different values for their variable labels. This is used if |
| 131 | // you want to count the same thing partitioned by various dimensions |
| 132 | // (e.g. number of operations queued, partitioned by user and operation |
| 133 | // type). Create instances with NewGaugeVec. |
| 134 | type GaugeVec struct { |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 135 | *MetricVec |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | // NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and |
| 139 | // partitioned by the given label names. |
| 140 | func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { |
| 141 | desc := NewDesc( |
| 142 | BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), |
| 143 | opts.Help, |
| 144 | labelNames, |
| 145 | opts.ConstLabels, |
| 146 | ) |
| 147 | return &GaugeVec{ |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 148 | MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 149 | if len(lvs) != len(desc.variableLabels) { |
| 150 | panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) |
| 151 | } |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 152 | result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)} |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 153 | result.init(result) // Init self-collection. |
| 154 | return result |
| 155 | }), |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | // GetMetricWithLabelValues returns the Gauge for the given slice of label |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 160 | // values (same order as the variable labels in Desc). If that combination of |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 161 | // label values is accessed for the first time, a new Gauge is created. |
| 162 | // |
| 163 | // It is possible to call this method without using the returned Gauge to only |
| 164 | // create the new Gauge but leave it at its starting value 0. See also the |
| 165 | // SummaryVec example. |
| 166 | // |
| 167 | // Keeping the Gauge for later use is possible (and should be considered if |
| 168 | // performance is critical), but keep in mind that Reset, DeleteLabelValues and |
| 169 | // Delete can be used to delete the Gauge from the GaugeVec. In that case, the |
| 170 | // Gauge will still exist, but it will not be exported anymore, even if a |
| 171 | // Gauge with the same label values is created later. See also the CounterVec |
| 172 | // example. |
| 173 | // |
| 174 | // An error is returned if the number of label values is not the same as the |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 175 | // number of variable labels in Desc (minus any curried labels). |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 176 | // |
| 177 | // Note that for more than one label value, this method is prone to mistakes |
| 178 | // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as |
| 179 | // an alternative to avoid that type of mistake. For higher label numbers, the |
| 180 | // latter has a much more readable (albeit more verbose) syntax, but it comes |
| 181 | // with a performance overhead (for creating and processing the Labels map). |
| 182 | func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) { |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 183 | metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...) |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 184 | if metric != nil { |
| 185 | return metric.(Gauge), err |
| 186 | } |
| 187 | return nil, err |
| 188 | } |
| 189 | |
| 190 | // GetMetricWith returns the Gauge for the given Labels map (the label names |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 191 | // must match those of the variable labels in Desc). If that label map is |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 192 | // accessed for the first time, a new Gauge is created. Implications of |
| 193 | // creating a Gauge without using it and keeping the Gauge for later use are |
| 194 | // the same as for GetMetricWithLabelValues. |
| 195 | // |
| 196 | // An error is returned if the number and names of the Labels are inconsistent |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 197 | // with those of the variable labels in Desc (minus any curried labels). |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 198 | // |
| 199 | // This method is used for the same purpose as |
| 200 | // GetMetricWithLabelValues(...string). See there for pros and cons of the two |
| 201 | // methods. |
| 202 | func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 203 | metric, err := v.MetricVec.GetMetricWith(labels) |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 204 | if metric != nil { |
| 205 | return metric.(Gauge), err |
| 206 | } |
| 207 | return nil, err |
| 208 | } |
| 209 | |
| 210 | // WithLabelValues works as GetMetricWithLabelValues, but panics where |
| 211 | // GetMetricWithLabelValues would have returned an error. Not returning an |
| 212 | // error allows shortcuts like |
| 213 | // myVec.WithLabelValues("404", "GET").Add(42) |
| 214 | func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { |
| 215 | g, err := v.GetMetricWithLabelValues(lvs...) |
| 216 | if err != nil { |
| 217 | panic(err) |
| 218 | } |
| 219 | return g |
| 220 | } |
| 221 | |
| 222 | // With works as GetMetricWith, but panics where GetMetricWithLabels would have |
| 223 | // returned an error. Not returning an error allows shortcuts like |
| 224 | // myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) |
| 225 | func (v *GaugeVec) With(labels Labels) Gauge { |
| 226 | g, err := v.GetMetricWith(labels) |
| 227 | if err != nil { |
| 228 | panic(err) |
| 229 | } |
| 230 | return g |
| 231 | } |
| 232 | |
| 233 | // CurryWith returns a vector curried with the provided labels, i.e. the |
| 234 | // returned vector has those labels pre-set for all labeled operations performed |
| 235 | // on it. The cardinality of the curried vector is reduced accordingly. The |
| 236 | // order of the remaining labels stays the same (just with the curried labels |
| 237 | // taken out of the sequence – which is relevant for the |
| 238 | // (GetMetric)WithLabelValues methods). It is possible to curry a curried |
| 239 | // vector, but only with labels not yet used for currying before. |
| 240 | // |
| 241 | // The metrics contained in the GaugeVec are shared between the curried and |
| 242 | // uncurried vectors. They are just accessed differently. Curried and uncurried |
| 243 | // vectors behave identically in terms of collection. Only one must be |
| 244 | // registered with a given registry (usually the uncurried version). The Reset |
| 245 | // method deletes all metrics, even if called on a curried vector. |
| 246 | func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) { |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 247 | vec, err := v.MetricVec.CurryWith(labels) |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 248 | if vec != nil { |
| 249 | return &GaugeVec{vec}, err |
| 250 | } |
| 251 | return nil, err |
| 252 | } |
| 253 | |
| 254 | // MustCurryWith works as CurryWith but panics where CurryWith would have |
| 255 | // returned an error. |
| 256 | func (v *GaugeVec) MustCurryWith(labels Labels) *GaugeVec { |
| 257 | vec, err := v.CurryWith(labels) |
| 258 | if err != nil { |
| 259 | panic(err) |
| 260 | } |
| 261 | return vec |
| 262 | } |
| 263 | |
| 264 | // GaugeFunc is a Gauge whose value is determined at collect time by calling a |
| 265 | // provided function. |
| 266 | // |
| 267 | // To create GaugeFunc instances, use NewGaugeFunc. |
| 268 | type GaugeFunc interface { |
| 269 | Metric |
| 270 | Collector |
| 271 | } |
| 272 | |
| 273 | // NewGaugeFunc creates a new GaugeFunc based on the provided GaugeOpts. The |
| 274 | // value reported is determined by calling the given function from within the |
| 275 | // Write method. Take into account that metric collection may happen |
khenaidoo | d948f77 | 2021-08-11 17:49:24 -0400 | [diff] [blame] | 276 | // concurrently. Therefore, it must be safe to call the provided function |
| 277 | // concurrently. |
| 278 | // |
| 279 | // NewGaugeFunc is a good way to create an “info” style metric with a constant |
| 280 | // value of 1. Example: |
| 281 | // https://github.com/prometheus/common/blob/8558a5b7db3c84fa38b4766966059a7bd5bfa2ee/version/info.go#L36-L56 |
khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 282 | func NewGaugeFunc(opts GaugeOpts, function func() float64) GaugeFunc { |
| 283 | return newValueFunc(NewDesc( |
| 284 | BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), |
| 285 | opts.Help, |
| 286 | nil, |
| 287 | opts.ConstLabels, |
| 288 | ), GaugeValue, function) |
| 289 | } |