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