1
2
3
4
5
6
7
8
9
10
11
12
13
14 package expfmt
15
16 import (
17 "bufio"
18 "fmt"
19 "io"
20 "math"
21 "mime"
22 "net/http"
23
24 dto "github.com/prometheus/client_model/go"
25 "google.golang.org/protobuf/encoding/protodelim"
26
27 "github.com/prometheus/common/model"
28 )
29
30
31 type Decoder interface {
32 Decode(*dto.MetricFamily) error
33 }
34
35
36 type DecodeOptions struct {
37
38 Timestamp model.Time
39 }
40
41
42
43 func ResponseFormat(h http.Header) Format {
44 ct := h.Get(hdrContentType)
45
46 mediatype, params, err := mime.ParseMediaType(ct)
47 if err != nil {
48 return fmtUnknown
49 }
50
51 const textType = "text/plain"
52
53 switch mediatype {
54 case ProtoType:
55 if p, ok := params["proto"]; ok && p != ProtoProtocol {
56 return fmtUnknown
57 }
58 if e, ok := params["encoding"]; ok && e != "delimited" {
59 return fmtUnknown
60 }
61 return fmtProtoDelim
62
63 case textType:
64 if v, ok := params["version"]; ok && v != TextVersion {
65 return fmtUnknown
66 }
67 return fmtText
68 }
69
70 return fmtUnknown
71 }
72
73
74
75 func NewDecoder(r io.Reader, format Format) Decoder {
76 switch format.FormatType() {
77 case TypeProtoDelim:
78 return &protoDecoder{r: bufio.NewReader(r)}
79 }
80 return &textDecoder{r: r}
81 }
82
83
84 type protoDecoder struct {
85 r protodelim.Reader
86 }
87
88
89 func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
90 opts := protodelim.UnmarshalOptions{
91 MaxSize: -1,
92 }
93 if err := opts.UnmarshalFrom(d.r, v); err != nil {
94 return err
95 }
96 if !model.IsValidMetricName(model.LabelValue(v.GetName())) {
97 return fmt.Errorf("invalid metric name %q", v.GetName())
98 }
99 for _, m := range v.GetMetric() {
100 if m == nil {
101 continue
102 }
103 for _, l := range m.GetLabel() {
104 if l == nil {
105 continue
106 }
107 if !model.LabelValue(l.GetValue()).IsValid() {
108 return fmt.Errorf("invalid label value %q", l.GetValue())
109 }
110 if !model.LabelName(l.GetName()).IsValid() {
111 return fmt.Errorf("invalid label name %q", l.GetName())
112 }
113 }
114 }
115 return nil
116 }
117
118
119 type textDecoder struct {
120 r io.Reader
121 fams map[string]*dto.MetricFamily
122 err error
123 }
124
125
126 func (d *textDecoder) Decode(v *dto.MetricFamily) error {
127 if d.err == nil {
128
129 var p TextParser
130 d.fams, d.err = p.TextToMetricFamilies(d.r)
131
132 if d.err == nil {
133 d.err = io.EOF
134 }
135 }
136
137 for key, fam := range d.fams {
138 v.Name = fam.Name
139 v.Help = fam.Help
140 v.Type = fam.Type
141 v.Metric = fam.Metric
142 delete(d.fams, key)
143 return nil
144 }
145 return d.err
146 }
147
148
149
150 type SampleDecoder struct {
151 Dec Decoder
152 Opts *DecodeOptions
153
154 f dto.MetricFamily
155 }
156
157
158
159 func (sd *SampleDecoder) Decode(s *model.Vector) error {
160 err := sd.Dec.Decode(&sd.f)
161 if err != nil {
162 return err
163 }
164 *s, err = extractSamples(&sd.f, sd.Opts)
165 return err
166 }
167
168
169
170
171
172 func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
173 var (
174 all model.Vector
175 lastErr error
176 )
177 for _, f := range fams {
178 some, err := extractSamples(f, o)
179 if err != nil {
180 lastErr = err
181 continue
182 }
183 all = append(all, some...)
184 }
185 return all, lastErr
186 }
187
188 func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error) {
189 switch f.GetType() {
190 case dto.MetricType_COUNTER:
191 return extractCounter(o, f), nil
192 case dto.MetricType_GAUGE:
193 return extractGauge(o, f), nil
194 case dto.MetricType_SUMMARY:
195 return extractSummary(o, f), nil
196 case dto.MetricType_UNTYPED:
197 return extractUntyped(o, f), nil
198 case dto.MetricType_HISTOGRAM:
199 return extractHistogram(o, f), nil
200 }
201 return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
202 }
203
204 func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
205 samples := make(model.Vector, 0, len(f.Metric))
206
207 for _, m := range f.Metric {
208 if m.Counter == nil {
209 continue
210 }
211
212 lset := make(model.LabelSet, len(m.Label)+1)
213 for _, p := range m.Label {
214 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
215 }
216 lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
217
218 smpl := &model.Sample{
219 Metric: model.Metric(lset),
220 Value: model.SampleValue(m.Counter.GetValue()),
221 }
222
223 if m.TimestampMs != nil {
224 smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
225 } else {
226 smpl.Timestamp = o.Timestamp
227 }
228
229 samples = append(samples, smpl)
230 }
231
232 return samples
233 }
234
235 func extractGauge(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
236 samples := make(model.Vector, 0, len(f.Metric))
237
238 for _, m := range f.Metric {
239 if m.Gauge == nil {
240 continue
241 }
242
243 lset := make(model.LabelSet, len(m.Label)+1)
244 for _, p := range m.Label {
245 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
246 }
247 lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
248
249 smpl := &model.Sample{
250 Metric: model.Metric(lset),
251 Value: model.SampleValue(m.Gauge.GetValue()),
252 }
253
254 if m.TimestampMs != nil {
255 smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
256 } else {
257 smpl.Timestamp = o.Timestamp
258 }
259
260 samples = append(samples, smpl)
261 }
262
263 return samples
264 }
265
266 func extractUntyped(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
267 samples := make(model.Vector, 0, len(f.Metric))
268
269 for _, m := range f.Metric {
270 if m.Untyped == nil {
271 continue
272 }
273
274 lset := make(model.LabelSet, len(m.Label)+1)
275 for _, p := range m.Label {
276 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
277 }
278 lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
279
280 smpl := &model.Sample{
281 Metric: model.Metric(lset),
282 Value: model.SampleValue(m.Untyped.GetValue()),
283 }
284
285 if m.TimestampMs != nil {
286 smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
287 } else {
288 smpl.Timestamp = o.Timestamp
289 }
290
291 samples = append(samples, smpl)
292 }
293
294 return samples
295 }
296
297 func extractSummary(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
298 samples := make(model.Vector, 0, len(f.Metric))
299
300 for _, m := range f.Metric {
301 if m.Summary == nil {
302 continue
303 }
304
305 timestamp := o.Timestamp
306 if m.TimestampMs != nil {
307 timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
308 }
309
310 for _, q := range m.Summary.Quantile {
311 lset := make(model.LabelSet, len(m.Label)+2)
312 for _, p := range m.Label {
313 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
314 }
315
316 lset[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile()))
317 lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
318
319 samples = append(samples, &model.Sample{
320 Metric: model.Metric(lset),
321 Value: model.SampleValue(q.GetValue()),
322 Timestamp: timestamp,
323 })
324 }
325
326 lset := make(model.LabelSet, len(m.Label)+1)
327 for _, p := range m.Label {
328 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
329 }
330 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
331
332 samples = append(samples, &model.Sample{
333 Metric: model.Metric(lset),
334 Value: model.SampleValue(m.Summary.GetSampleSum()),
335 Timestamp: timestamp,
336 })
337
338 lset = make(model.LabelSet, len(m.Label)+1)
339 for _, p := range m.Label {
340 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
341 }
342 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
343
344 samples = append(samples, &model.Sample{
345 Metric: model.Metric(lset),
346 Value: model.SampleValue(m.Summary.GetSampleCount()),
347 Timestamp: timestamp,
348 })
349 }
350
351 return samples
352 }
353
354 func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
355 samples := make(model.Vector, 0, len(f.Metric))
356
357 for _, m := range f.Metric {
358 if m.Histogram == nil {
359 continue
360 }
361
362 timestamp := o.Timestamp
363 if m.TimestampMs != nil {
364 timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
365 }
366
367 infSeen := false
368
369 for _, q := range m.Histogram.Bucket {
370 lset := make(model.LabelSet, len(m.Label)+2)
371 for _, p := range m.Label {
372 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
373 }
374 lset[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound()))
375 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
376
377 if math.IsInf(q.GetUpperBound(), +1) {
378 infSeen = true
379 }
380
381 samples = append(samples, &model.Sample{
382 Metric: model.Metric(lset),
383 Value: model.SampleValue(q.GetCumulativeCount()),
384 Timestamp: timestamp,
385 })
386 }
387
388 lset := make(model.LabelSet, len(m.Label)+1)
389 for _, p := range m.Label {
390 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
391 }
392 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
393
394 samples = append(samples, &model.Sample{
395 Metric: model.Metric(lset),
396 Value: model.SampleValue(m.Histogram.GetSampleSum()),
397 Timestamp: timestamp,
398 })
399
400 lset = make(model.LabelSet, len(m.Label)+1)
401 for _, p := range m.Label {
402 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
403 }
404 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
405
406 count := &model.Sample{
407 Metric: model.Metric(lset),
408 Value: model.SampleValue(m.Histogram.GetSampleCount()),
409 Timestamp: timestamp,
410 }
411 samples = append(samples, count)
412
413 if !infSeen {
414
415 lset := make(model.LabelSet, len(m.Label)+2)
416 for _, p := range m.Label {
417 lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
418 }
419 lset[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf")
420 lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
421
422 samples = append(samples, &model.Sample{
423 Metric: model.Metric(lset),
424 Value: count.Value,
425 Timestamp: timestamp,
426 })
427 }
428 }
429
430 return samples
431 }
432
View as plain text