Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / prometheus / common / expfmt / decode.go
1 // Copyright 2015 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 expfmt
15
16 import (
17         "fmt"
18         "io"
19         "math"
20         "mime"
21         "net/http"
22
23         dto "github.com/prometheus/client_model/go"
24
25         "github.com/matttproud/golang_protobuf_extensions/pbutil"
26         "github.com/prometheus/common/model"
27 )
28
29 // Decoder types decode an input stream into metric families.
30 type Decoder interface {
31         Decode(*dto.MetricFamily) error
32 }
33
34 // DecodeOptions contains options used by the Decoder and in sample extraction.
35 type DecodeOptions struct {
36         // Timestamp is added to each value from the stream that has no explicit timestamp set.
37         Timestamp model.Time
38 }
39
40 // ResponseFormat extracts the correct format from a HTTP response header.
41 // If no matching format can be found FormatUnknown is returned.
42 func ResponseFormat(h http.Header) Format {
43         ct := h.Get(hdrContentType)
44
45         mediatype, params, err := mime.ParseMediaType(ct)
46         if err != nil {
47                 return FmtUnknown
48         }
49
50         const textType = "text/plain"
51
52         switch mediatype {
53         case ProtoType:
54                 if p, ok := params["proto"]; ok && p != ProtoProtocol {
55                         return FmtUnknown
56                 }
57                 if e, ok := params["encoding"]; ok && e != "delimited" {
58                         return FmtUnknown
59                 }
60                 return FmtProtoDelim
61
62         case textType:
63                 if v, ok := params["version"]; ok && v != TextVersion {
64                         return FmtUnknown
65                 }
66                 return FmtText
67         }
68
69         return FmtUnknown
70 }
71
72 // NewDecoder returns a new decoder based on the given input format.
73 // If the input format does not imply otherwise, a text format decoder is returned.
74 func NewDecoder(r io.Reader, format Format) Decoder {
75         switch format {
76         case FmtProtoDelim:
77                 return &protoDecoder{r: r}
78         }
79         return &textDecoder{r: r}
80 }
81
82 // protoDecoder implements the Decoder interface for protocol buffers.
83 type protoDecoder struct {
84         r io.Reader
85 }
86
87 // Decode implements the Decoder interface.
88 func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
89         _, err := pbutil.ReadDelimited(d.r, v)
90         if err != nil {
91                 return err
92         }
93         if !model.IsValidMetricName(model.LabelValue(v.GetName())) {
94                 return fmt.Errorf("invalid metric name %q", v.GetName())
95         }
96         for _, m := range v.GetMetric() {
97                 if m == nil {
98                         continue
99                 }
100                 for _, l := range m.GetLabel() {
101                         if l == nil {
102                                 continue
103                         }
104                         if !model.LabelValue(l.GetValue()).IsValid() {
105                                 return fmt.Errorf("invalid label value %q", l.GetValue())
106                         }
107                         if !model.LabelName(l.GetName()).IsValid() {
108                                 return fmt.Errorf("invalid label name %q", l.GetName())
109                         }
110                 }
111         }
112         return nil
113 }
114
115 // textDecoder implements the Decoder interface for the text protocol.
116 type textDecoder struct {
117         r    io.Reader
118         p    TextParser
119         fams []*dto.MetricFamily
120 }
121
122 // Decode implements the Decoder interface.
123 func (d *textDecoder) Decode(v *dto.MetricFamily) error {
124         // TODO(fabxc): Wrap this as a line reader to make streaming safer.
125         if len(d.fams) == 0 {
126                 // No cached metric families, read everything and parse metrics.
127                 fams, err := d.p.TextToMetricFamilies(d.r)
128                 if err != nil {
129                         return err
130                 }
131                 if len(fams) == 0 {
132                         return io.EOF
133                 }
134                 d.fams = make([]*dto.MetricFamily, 0, len(fams))
135                 for _, f := range fams {
136                         d.fams = append(d.fams, f)
137                 }
138         }
139
140         *v = *d.fams[0]
141         d.fams = d.fams[1:]
142
143         return nil
144 }
145
146 // SampleDecoder wraps a Decoder to extract samples from the metric families
147 // decoded by the wrapped Decoder.
148 type SampleDecoder struct {
149         Dec  Decoder
150         Opts *DecodeOptions
151
152         f dto.MetricFamily
153 }
154
155 // Decode calls the Decode method of the wrapped Decoder and then extracts the
156 // samples from the decoded MetricFamily into the provided model.Vector.
157 func (sd *SampleDecoder) Decode(s *model.Vector) error {
158         err := sd.Dec.Decode(&sd.f)
159         if err != nil {
160                 return err
161         }
162         *s, err = extractSamples(&sd.f, sd.Opts)
163         return err
164 }
165
166 // ExtractSamples builds a slice of samples from the provided metric
167 // families. If an error occurrs during sample extraction, it continues to
168 // extract from the remaining metric families. The returned error is the last
169 // error that has occurred.
170 func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
171         var (
172                 all     model.Vector
173                 lastErr error
174         )
175         for _, f := range fams {
176                 some, err := extractSamples(f, o)
177                 if err != nil {
178                         lastErr = err
179                         continue
180                 }
181                 all = append(all, some...)
182         }
183         return all, lastErr
184 }
185
186 func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error) {
187         switch f.GetType() {
188         case dto.MetricType_COUNTER:
189                 return extractCounter(o, f), nil
190         case dto.MetricType_GAUGE:
191                 return extractGauge(o, f), nil
192         case dto.MetricType_SUMMARY:
193                 return extractSummary(o, f), nil
194         case dto.MetricType_UNTYPED:
195                 return extractUntyped(o, f), nil
196         case dto.MetricType_HISTOGRAM:
197                 return extractHistogram(o, f), nil
198         }
199         return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
200 }
201
202 func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
203         samples := make(model.Vector, 0, len(f.Metric))
204
205         for _, m := range f.Metric {
206                 if m.Counter == nil {
207                         continue
208                 }
209
210                 lset := make(model.LabelSet, len(m.Label)+1)
211                 for _, p := range m.Label {
212                         lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
213                 }
214                 lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
215
216                 smpl := &model.Sample{
217                         Metric: model.Metric(lset),
218                         Value:  model.SampleValue(m.Counter.GetValue()),
219                 }
220
221                 if m.TimestampMs != nil {
222                         smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
223                 } else {
224                         smpl.Timestamp = o.Timestamp
225                 }
226
227                 samples = append(samples, smpl)
228         }
229
230         return samples
231 }
232
233 func extractGauge(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
234         samples := make(model.Vector, 0, len(f.Metric))
235
236         for _, m := range f.Metric {
237                 if m.Gauge == nil {
238                         continue
239                 }
240
241                 lset := make(model.LabelSet, len(m.Label)+1)
242                 for _, p := range m.Label {
243                         lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
244                 }
245                 lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
246
247                 smpl := &model.Sample{
248                         Metric: model.Metric(lset),
249                         Value:  model.SampleValue(m.Gauge.GetValue()),
250                 }
251
252                 if m.TimestampMs != nil {
253                         smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
254                 } else {
255                         smpl.Timestamp = o.Timestamp
256                 }
257
258                 samples = append(samples, smpl)
259         }
260
261         return samples
262 }
263
264 func extractUntyped(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
265         samples := make(model.Vector, 0, len(f.Metric))
266
267         for _, m := range f.Metric {
268                 if m.Untyped == nil {
269                         continue
270                 }
271
272                 lset := make(model.LabelSet, len(m.Label)+1)
273                 for _, p := range m.Label {
274                         lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
275                 }
276                 lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
277
278                 smpl := &model.Sample{
279                         Metric: model.Metric(lset),
280                         Value:  model.SampleValue(m.Untyped.GetValue()),
281                 }
282
283                 if m.TimestampMs != nil {
284                         smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
285                 } else {
286                         smpl.Timestamp = o.Timestamp
287                 }
288
289                 samples = append(samples, smpl)
290         }
291
292         return samples
293 }
294
295 func extractSummary(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
296         samples := make(model.Vector, 0, len(f.Metric))
297
298         for _, m := range f.Metric {
299                 if m.Summary == nil {
300                         continue
301                 }
302
303                 timestamp := o.Timestamp
304                 if m.TimestampMs != nil {
305                         timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
306                 }
307
308                 for _, q := range m.Summary.Quantile {
309                         lset := make(model.LabelSet, len(m.Label)+2)
310                         for _, p := range m.Label {
311                                 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
312                         }
313                         // BUG(matt): Update other names to "quantile".
314                         lset[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile()))
315                         lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
316
317                         samples = append(samples, &model.Sample{
318                                 Metric:    model.Metric(lset),
319                                 Value:     model.SampleValue(q.GetValue()),
320                                 Timestamp: timestamp,
321                         })
322                 }
323
324                 lset := make(model.LabelSet, len(m.Label)+1)
325                 for _, p := range m.Label {
326                         lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
327                 }
328                 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
329
330                 samples = append(samples, &model.Sample{
331                         Metric:    model.Metric(lset),
332                         Value:     model.SampleValue(m.Summary.GetSampleSum()),
333                         Timestamp: timestamp,
334                 })
335
336                 lset = make(model.LabelSet, len(m.Label)+1)
337                 for _, p := range m.Label {
338                         lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
339                 }
340                 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
341
342                 samples = append(samples, &model.Sample{
343                         Metric:    model.Metric(lset),
344                         Value:     model.SampleValue(m.Summary.GetSampleCount()),
345                         Timestamp: timestamp,
346                 })
347         }
348
349         return samples
350 }
351
352 func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
353         samples := make(model.Vector, 0, len(f.Metric))
354
355         for _, m := range f.Metric {
356                 if m.Histogram == nil {
357                         continue
358                 }
359
360                 timestamp := o.Timestamp
361                 if m.TimestampMs != nil {
362                         timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
363                 }
364
365                 infSeen := false
366
367                 for _, q := range m.Histogram.Bucket {
368                         lset := make(model.LabelSet, len(m.Label)+2)
369                         for _, p := range m.Label {
370                                 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
371                         }
372                         lset[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound()))
373                         lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
374
375                         if math.IsInf(q.GetUpperBound(), +1) {
376                                 infSeen = true
377                         }
378
379                         samples = append(samples, &model.Sample{
380                                 Metric:    model.Metric(lset),
381                                 Value:     model.SampleValue(q.GetCumulativeCount()),
382                                 Timestamp: timestamp,
383                         })
384                 }
385
386                 lset := make(model.LabelSet, len(m.Label)+1)
387                 for _, p := range m.Label {
388                         lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
389                 }
390                 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
391
392                 samples = append(samples, &model.Sample{
393                         Metric:    model.Metric(lset),
394                         Value:     model.SampleValue(m.Histogram.GetSampleSum()),
395                         Timestamp: timestamp,
396                 })
397
398                 lset = make(model.LabelSet, len(m.Label)+1)
399                 for _, p := range m.Label {
400                         lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
401                 }
402                 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
403
404                 count := &model.Sample{
405                         Metric:    model.Metric(lset),
406                         Value:     model.SampleValue(m.Histogram.GetSampleCount()),
407                         Timestamp: timestamp,
408                 }
409                 samples = append(samples, count)
410
411                 if !infSeen {
412                         // Append an infinity bucket sample.
413                         lset := make(model.LabelSet, len(m.Label)+2)
414                         for _, p := range m.Label {
415                                 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
416                         }
417                         lset[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf")
418                         lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
419
420                         samples = append(samples, &model.Sample{
421                                 Metric:    model.Metric(lset),
422                                 Value:     count.Value,
423                                 Timestamp: timestamp,
424                         })
425                 }
426         }
427
428         return samples
429 }