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 14package prometheus 15 16import ( 17 "errors" 18 "fmt" 19 "math" 20 "sort" 21 "sync/atomic" 22 23 "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto" 24 dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go" 25) 26 27// ValueType is an enumeration of metric types that represent a simple value. 28type ValueType int 29 30// Possible values for the ValueType enum. 31const ( 32 _ ValueType = iota 33 CounterValue 34 GaugeValue 35 UntypedValue 36) 37 38var errInconsistentCardinality = errors.New("inconsistent label cardinality") 39 40// value is a generic metric for simple values. It implements Metric, Collector, 41// Counter, Gauge, and Untyped. Its effective type is determined by 42// ValueType. This is a low-level building block used by the library to back the 43// implementations of Counter, Gauge, and Untyped. 44type value struct { 45 // valBits containst the bits of the represented float64 value. It has 46 // to go first in the struct to guarantee alignment for atomic 47 // operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG 48 valBits uint64 49 50 SelfCollector 51 52 desc *Desc 53 valType ValueType 54 labelPairs []*dto.LabelPair 55} 56 57// newValue returns a newly allocated value with the given Desc, ValueType, 58// sample value and label values. It panics if the number of label 59// values is different from the number of variable labels in Desc. 60func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...string) *value { 61 if len(labelValues) != len(desc.variableLabels) { 62 panic(errInconsistentCardinality) 63 } 64 result := &value{ 65 desc: desc, 66 valType: valueType, 67 valBits: math.Float64bits(val), 68 labelPairs: makeLabelPairs(desc, labelValues), 69 } 70 result.Init(result) 71 return result 72} 73 74func (v *value) Desc() *Desc { 75 return v.desc 76} 77 78func (v *value) Set(val float64) { 79 atomic.StoreUint64(&v.valBits, math.Float64bits(val)) 80} 81 82func (v *value) Inc() { 83 v.Add(1) 84} 85 86func (v *value) Dec() { 87 v.Add(-1) 88} 89 90func (v *value) Add(val float64) { 91 for { 92 oldBits := atomic.LoadUint64(&v.valBits) 93 newBits := math.Float64bits(math.Float64frombits(oldBits) + val) 94 if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) { 95 return 96 } 97 } 98} 99 100func (v *value) Sub(val float64) { 101 v.Add(val * -1) 102} 103 104func (v *value) Write(out *dto.Metric) error { 105 val := math.Float64frombits(atomic.LoadUint64(&v.valBits)) 106 return populateMetric(v.valType, val, v.labelPairs, out) 107} 108 109// valueFunc is a generic metric for simple values retrieved on collect time 110// from a function. It implements Metric and Collector. Its effective type is 111// determined by ValueType. This is a low-level building block used by the 112// library to back the implementations of CounterFunc, GaugeFunc, and 113// UntypedFunc. 114type valueFunc struct { 115 SelfCollector 116 117 desc *Desc 118 valType ValueType 119 function func() float64 120 labelPairs []*dto.LabelPair 121} 122 123// newValueFunc returns a newly allocated valueFunc with the given Desc and 124// ValueType. The value reported is determined by calling the given function 125// from within the Write method. Take into account that metric collection may 126// happen concurrently. If that results in concurrent calls to Write, like in 127// the case where a valueFunc is directly registered with Prometheus, the 128// provided function must be concurrency-safe. 129func newValueFunc(desc *Desc, valueType ValueType, function func() float64) *valueFunc { 130 result := &valueFunc{ 131 desc: desc, 132 valType: valueType, 133 function: function, 134 labelPairs: makeLabelPairs(desc, nil), 135 } 136 result.Init(result) 137 return result 138} 139 140func (v *valueFunc) Desc() *Desc { 141 return v.desc 142} 143 144func (v *valueFunc) Write(out *dto.Metric) error { 145 return populateMetric(v.valType, v.function(), v.labelPairs, out) 146} 147 148// NewConstMetric returns a metric with one fixed value that cannot be 149// changed. Users of this package will not have much use for it in regular 150// operations. However, when implementing custom Collectors, it is useful as a 151// throw-away metric that is generated on the fly to send it to Prometheus in 152// the Collect method. NewConstMetric returns an error if the length of 153// labelValues is not consistent with the variable labels in Desc. 154func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) { 155 if len(desc.variableLabels) != len(labelValues) { 156 return nil, errInconsistentCardinality 157 } 158 return &constMetric{ 159 desc: desc, 160 valType: valueType, 161 val: value, 162 labelPairs: makeLabelPairs(desc, labelValues), 163 }, nil 164} 165 166// MustNewConstMetric is a version of NewConstMetric that panics where 167// NewConstMetric would have returned an error. 168func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) Metric { 169 m, err := NewConstMetric(desc, valueType, value, labelValues...) 170 if err != nil { 171 panic(err) 172 } 173 return m 174} 175 176type constMetric struct { 177 desc *Desc 178 valType ValueType 179 val float64 180 labelPairs []*dto.LabelPair 181} 182 183func (m *constMetric) Desc() *Desc { 184 return m.desc 185} 186 187func (m *constMetric) Write(out *dto.Metric) error { 188 return populateMetric(m.valType, m.val, m.labelPairs, out) 189} 190 191func populateMetric( 192 t ValueType, 193 v float64, 194 labelPairs []*dto.LabelPair, 195 m *dto.Metric, 196) error { 197 m.Label = labelPairs 198 switch t { 199 case CounterValue: 200 m.Counter = &dto.Counter{Value: proto.Float64(v)} 201 case GaugeValue: 202 m.Gauge = &dto.Gauge{Value: proto.Float64(v)} 203 case UntypedValue: 204 m.Untyped = &dto.Untyped{Value: proto.Float64(v)} 205 default: 206 return fmt.Errorf("encountered unknown type %v", t) 207 } 208 return nil 209} 210 211func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair { 212 totalLen := len(desc.variableLabels) + len(desc.constLabelPairs) 213 if totalLen == 0 { 214 // Super fast path. 215 return nil 216 } 217 if len(desc.variableLabels) == 0 { 218 // Moderately fast path. 219 return desc.constLabelPairs 220 } 221 labelPairs := make([]*dto.LabelPair, 0, totalLen) 222 for i, n := range desc.variableLabels { 223 labelPairs = append(labelPairs, &dto.LabelPair{ 224 Name: proto.String(n), 225 Value: proto.String(labelValues[i]), 226 }) 227 } 228 for _, lp := range desc.constLabelPairs { 229 labelPairs = append(labelPairs, lp) 230 } 231 sort.Sort(LabelPairSorter(labelPairs)) 232 return labelPairs 233} 234