You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
6.7 KiB
239 lines
6.7 KiB
// Copyright 2014 The Prometheus Authors |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
package prometheus |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"math" |
|
"sort" |
|
"sync/atomic" |
|
"time" |
|
|
|
dto "github.com/prometheus/client_model/go" |
|
|
|
"github.com/golang/protobuf/proto" |
|
) |
|
|
|
// ValueType is an enumeration of metric types that represent a simple value. |
|
type ValueType int |
|
|
|
// Possible values for the ValueType enum. |
|
const ( |
|
_ ValueType = iota |
|
CounterValue |
|
GaugeValue |
|
UntypedValue |
|
) |
|
|
|
var errInconsistentCardinality = errors.New("inconsistent label cardinality") |
|
|
|
// value is a generic metric for simple values. It implements Metric, Collector, |
|
// Counter, Gauge, and Untyped. Its effective type is determined by |
|
// ValueType. This is a low-level building block used by the library to back the |
|
// implementations of Counter, Gauge, and Untyped. |
|
type value struct { |
|
// valBits containst the bits of the represented float64 value. It has |
|
// to go first in the struct to guarantee alignment for atomic |
|
// operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG |
|
valBits uint64 |
|
|
|
selfCollector |
|
|
|
desc *Desc |
|
valType ValueType |
|
labelPairs []*dto.LabelPair |
|
} |
|
|
|
// newValue returns a newly allocated value with the given Desc, ValueType, |
|
// sample value and label values. It panics if the number of label |
|
// values is different from the number of variable labels in Desc. |
|
func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...string) *value { |
|
if len(labelValues) != len(desc.variableLabels) { |
|
panic(errInconsistentCardinality) |
|
} |
|
result := &value{ |
|
desc: desc, |
|
valType: valueType, |
|
valBits: math.Float64bits(val), |
|
labelPairs: makeLabelPairs(desc, labelValues), |
|
} |
|
result.init(result) |
|
return result |
|
} |
|
|
|
func (v *value) Desc() *Desc { |
|
return v.desc |
|
} |
|
|
|
func (v *value) Set(val float64) { |
|
atomic.StoreUint64(&v.valBits, math.Float64bits(val)) |
|
} |
|
|
|
func (v *value) SetToCurrentTime() { |
|
v.Set(float64(time.Now().UnixNano()) / 1e9) |
|
} |
|
|
|
func (v *value) Inc() { |
|
v.Add(1) |
|
} |
|
|
|
func (v *value) Dec() { |
|
v.Add(-1) |
|
} |
|
|
|
func (v *value) Add(val float64) { |
|
for { |
|
oldBits := atomic.LoadUint64(&v.valBits) |
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + val) |
|
if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) { |
|
return |
|
} |
|
} |
|
} |
|
|
|
func (v *value) Sub(val float64) { |
|
v.Add(val * -1) |
|
} |
|
|
|
func (v *value) Write(out *dto.Metric) error { |
|
val := math.Float64frombits(atomic.LoadUint64(&v.valBits)) |
|
return populateMetric(v.valType, val, v.labelPairs, out) |
|
} |
|
|
|
// valueFunc is a generic metric for simple values retrieved on collect time |
|
// from a function. It implements Metric and Collector. Its effective type is |
|
// determined by ValueType. This is a low-level building block used by the |
|
// library to back the implementations of CounterFunc, GaugeFunc, and |
|
// UntypedFunc. |
|
type valueFunc struct { |
|
selfCollector |
|
|
|
desc *Desc |
|
valType ValueType |
|
function func() float64 |
|
labelPairs []*dto.LabelPair |
|
} |
|
|
|
// newValueFunc returns a newly allocated valueFunc with the given Desc and |
|
// ValueType. The value reported is determined by calling the given function |
|
// from within the Write method. Take into account that metric collection may |
|
// happen concurrently. If that results in concurrent calls to Write, like in |
|
// the case where a valueFunc is directly registered with Prometheus, the |
|
// provided function must be concurrency-safe. |
|
func newValueFunc(desc *Desc, valueType ValueType, function func() float64) *valueFunc { |
|
result := &valueFunc{ |
|
desc: desc, |
|
valType: valueType, |
|
function: function, |
|
labelPairs: makeLabelPairs(desc, nil), |
|
} |
|
result.init(result) |
|
return result |
|
} |
|
|
|
func (v *valueFunc) Desc() *Desc { |
|
return v.desc |
|
} |
|
|
|
func (v *valueFunc) Write(out *dto.Metric) error { |
|
return populateMetric(v.valType, v.function(), v.labelPairs, out) |
|
} |
|
|
|
// NewConstMetric returns a metric with one fixed value that cannot be |
|
// changed. Users of this package will not have much use for it in regular |
|
// operations. However, when implementing custom Collectors, it is useful as a |
|
// throw-away metric that is generated on the fly to send it to Prometheus in |
|
// the Collect method. NewConstMetric returns an error if the length of |
|
// labelValues is not consistent with the variable labels in Desc. |
|
func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) { |
|
if len(desc.variableLabels) != len(labelValues) { |
|
return nil, errInconsistentCardinality |
|
} |
|
return &constMetric{ |
|
desc: desc, |
|
valType: valueType, |
|
val: value, |
|
labelPairs: makeLabelPairs(desc, labelValues), |
|
}, nil |
|
} |
|
|
|
// MustNewConstMetric is a version of NewConstMetric that panics where |
|
// NewConstMetric would have returned an error. |
|
func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) Metric { |
|
m, err := NewConstMetric(desc, valueType, value, labelValues...) |
|
if err != nil { |
|
panic(err) |
|
} |
|
return m |
|
} |
|
|
|
type constMetric struct { |
|
desc *Desc |
|
valType ValueType |
|
val float64 |
|
labelPairs []*dto.LabelPair |
|
} |
|
|
|
func (m *constMetric) Desc() *Desc { |
|
return m.desc |
|
} |
|
|
|
func (m *constMetric) Write(out *dto.Metric) error { |
|
return populateMetric(m.valType, m.val, m.labelPairs, out) |
|
} |
|
|
|
func populateMetric( |
|
t ValueType, |
|
v float64, |
|
labelPairs []*dto.LabelPair, |
|
m *dto.Metric, |
|
) error { |
|
m.Label = labelPairs |
|
switch t { |
|
case CounterValue: |
|
m.Counter = &dto.Counter{Value: proto.Float64(v)} |
|
case GaugeValue: |
|
m.Gauge = &dto.Gauge{Value: proto.Float64(v)} |
|
case UntypedValue: |
|
m.Untyped = &dto.Untyped{Value: proto.Float64(v)} |
|
default: |
|
return fmt.Errorf("encountered unknown type %v", t) |
|
} |
|
return nil |
|
} |
|
|
|
func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair { |
|
totalLen := len(desc.variableLabels) + len(desc.constLabelPairs) |
|
if totalLen == 0 { |
|
// Super fast path. |
|
return nil |
|
} |
|
if len(desc.variableLabels) == 0 { |
|
// Moderately fast path. |
|
return desc.constLabelPairs |
|
} |
|
labelPairs := make([]*dto.LabelPair, 0, totalLen) |
|
for i, n := range desc.variableLabels { |
|
labelPairs = append(labelPairs, &dto.LabelPair{ |
|
Name: proto.String(n), |
|
Value: proto.String(labelValues[i]), |
|
}) |
|
} |
|
for _, lp := range desc.constLabelPairs { |
|
labelPairs = append(labelPairs, lp) |
|
} |
|
sort.Sort(LabelPairSorter(labelPairs)) |
|
return labelPairs |
|
}
|
|
|