Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / prometheus / common / expfmt / text_create.go
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
14 package expfmt
15
16 import (
17         "bytes"
18         "fmt"
19         "io"
20         "math"
21         "strconv"
22         "strings"
23         "sync"
24
25         "github.com/prometheus/common/model"
26
27         dto "github.com/prometheus/client_model/go"
28 )
29
30 // enhancedWriter has all the enhanced write functions needed here. bytes.Buffer
31 // implements it.
32 type enhancedWriter interface {
33         io.Writer
34         WriteRune(r rune) (n int, err error)
35         WriteString(s string) (n int, err error)
36         WriteByte(c byte) error
37 }
38
39 const (
40         initialBufSize    = 512
41         initialNumBufSize = 24
42 )
43
44 var (
45         bufPool = sync.Pool{
46                 New: func() interface{} {
47                         return bytes.NewBuffer(make([]byte, 0, initialBufSize))
48                 },
49         }
50         numBufPool = sync.Pool{
51                 New: func() interface{} {
52                         b := make([]byte, 0, initialNumBufSize)
53                         return &b
54                 },
55         }
56 )
57
58 // MetricFamilyToText converts a MetricFamily proto message into text format and
59 // writes the resulting lines to 'out'. It returns the number of bytes written
60 // and any error encountered. The output will have the same order as the input,
61 // no further sorting is performed. Furthermore, this function assumes the input
62 // is already sanitized and does not perform any sanity checks. If the input
63 // contains duplicate metrics or invalid metric or label names, the conversion
64 // will result in invalid text format output.
65 //
66 // This method fulfills the type 'prometheus.encoder'.
67 func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err error) {
68         // Fail-fast checks.
69         if len(in.Metric) == 0 {
70                 return 0, fmt.Errorf("MetricFamily has no metrics: %s", in)
71         }
72         name := in.GetName()
73         if name == "" {
74                 return 0, fmt.Errorf("MetricFamily has no name: %s", in)
75         }
76
77         // Try the interface upgrade. If it doesn't work, we'll use a
78         // bytes.Buffer from the sync.Pool and write out its content to out in a
79         // single go in the end.
80         w, ok := out.(enhancedWriter)
81         if !ok {
82                 b := bufPool.Get().(*bytes.Buffer)
83                 b.Reset()
84                 w = b
85                 defer func() {
86                         bWritten, bErr := out.Write(b.Bytes())
87                         written = bWritten
88                         if err == nil {
89                                 err = bErr
90                         }
91                         bufPool.Put(b)
92                 }()
93         }
94
95         var n int
96
97         // Comments, first HELP, then TYPE.
98         if in.Help != nil {
99                 n, err = w.WriteString("# HELP ")
100                 written += n
101                 if err != nil {
102                         return
103                 }
104                 n, err = w.WriteString(name)
105                 written += n
106                 if err != nil {
107                         return
108                 }
109                 err = w.WriteByte(' ')
110                 written++
111                 if err != nil {
112                         return
113                 }
114                 n, err = writeEscapedString(w, *in.Help, false)
115                 written += n
116                 if err != nil {
117                         return
118                 }
119                 err = w.WriteByte('\n')
120                 written++
121                 if err != nil {
122                         return
123                 }
124         }
125         n, err = w.WriteString("# TYPE ")
126         written += n
127         if err != nil {
128                 return
129         }
130         n, err = w.WriteString(name)
131         written += n
132         if err != nil {
133                 return
134         }
135         metricType := in.GetType()
136         switch metricType {
137         case dto.MetricType_COUNTER:
138                 n, err = w.WriteString(" counter\n")
139         case dto.MetricType_GAUGE:
140                 n, err = w.WriteString(" gauge\n")
141         case dto.MetricType_SUMMARY:
142                 n, err = w.WriteString(" summary\n")
143         case dto.MetricType_UNTYPED:
144                 n, err = w.WriteString(" untyped\n")
145         case dto.MetricType_HISTOGRAM:
146                 n, err = w.WriteString(" histogram\n")
147         default:
148                 return written, fmt.Errorf("unknown metric type %s", metricType.String())
149         }
150         written += n
151         if err != nil {
152                 return
153         }
154
155         // Finally the samples, one line for each.
156         for _, metric := range in.Metric {
157                 switch metricType {
158                 case dto.MetricType_COUNTER:
159                         if metric.Counter == nil {
160                                 return written, fmt.Errorf(
161                                         "expected counter in metric %s %s", name, metric,
162                                 )
163                         }
164                         n, err = writeSample(
165                                 w, name, "", metric, "", 0,
166                                 metric.Counter.GetValue(),
167                         )
168                 case dto.MetricType_GAUGE:
169                         if metric.Gauge == nil {
170                                 return written, fmt.Errorf(
171                                         "expected gauge in metric %s %s", name, metric,
172                                 )
173                         }
174                         n, err = writeSample(
175                                 w, name, "", metric, "", 0,
176                                 metric.Gauge.GetValue(),
177                         )
178                 case dto.MetricType_UNTYPED:
179                         if metric.Untyped == nil {
180                                 return written, fmt.Errorf(
181                                         "expected untyped in metric %s %s", name, metric,
182                                 )
183                         }
184                         n, err = writeSample(
185                                 w, name, "", metric, "", 0,
186                                 metric.Untyped.GetValue(),
187                         )
188                 case dto.MetricType_SUMMARY:
189                         if metric.Summary == nil {
190                                 return written, fmt.Errorf(
191                                         "expected summary in metric %s %s", name, metric,
192                                 )
193                         }
194                         for _, q := range metric.Summary.Quantile {
195                                 n, err = writeSample(
196                                         w, name, "", metric,
197                                         model.QuantileLabel, q.GetQuantile(),
198                                         q.GetValue(),
199                                 )
200                                 written += n
201                                 if err != nil {
202                                         return
203                                 }
204                         }
205                         n, err = writeSample(
206                                 w, name, "_sum", metric, "", 0,
207                                 metric.Summary.GetSampleSum(),
208                         )
209                         written += n
210                         if err != nil {
211                                 return
212                         }
213                         n, err = writeSample(
214                                 w, name, "_count", metric, "", 0,
215                                 float64(metric.Summary.GetSampleCount()),
216                         )
217                 case dto.MetricType_HISTOGRAM:
218                         if metric.Histogram == nil {
219                                 return written, fmt.Errorf(
220                                         "expected histogram in metric %s %s", name, metric,
221                                 )
222                         }
223                         infSeen := false
224                         for _, b := range metric.Histogram.Bucket {
225                                 n, err = writeSample(
226                                         w, name, "_bucket", metric,
227                                         model.BucketLabel, b.GetUpperBound(),
228                                         float64(b.GetCumulativeCount()),
229                                 )
230                                 written += n
231                                 if err != nil {
232                                         return
233                                 }
234                                 if math.IsInf(b.GetUpperBound(), +1) {
235                                         infSeen = true
236                                 }
237                         }
238                         if !infSeen {
239                                 n, err = writeSample(
240                                         w, name, "_bucket", metric,
241                                         model.BucketLabel, math.Inf(+1),
242                                         float64(metric.Histogram.GetSampleCount()),
243                                 )
244                                 written += n
245                                 if err != nil {
246                                         return
247                                 }
248                         }
249                         n, err = writeSample(
250                                 w, name, "_sum", metric, "", 0,
251                                 metric.Histogram.GetSampleSum(),
252                         )
253                         written += n
254                         if err != nil {
255                                 return
256                         }
257                         n, err = writeSample(
258                                 w, name, "_count", metric, "", 0,
259                                 float64(metric.Histogram.GetSampleCount()),
260                         )
261                 default:
262                         return written, fmt.Errorf(
263                                 "unexpected type in metric %s %s", name, metric,
264                         )
265                 }
266                 written += n
267                 if err != nil {
268                         return
269                 }
270         }
271         return
272 }
273
274 // writeSample writes a single sample in text format to w, given the metric
275 // name, the metric proto message itself, optionally an additional label name
276 // with a float64 value (use empty string as label name if not required), and
277 // the value. The function returns the number of bytes written and any error
278 // encountered.
279 func writeSample(
280         w enhancedWriter,
281         name, suffix string,
282         metric *dto.Metric,
283         additionalLabelName string, additionalLabelValue float64,
284         value float64,
285 ) (int, error) {
286         var written int
287         n, err := w.WriteString(name)
288         written += n
289         if err != nil {
290                 return written, err
291         }
292         if suffix != "" {
293                 n, err = w.WriteString(suffix)
294                 written += n
295                 if err != nil {
296                         return written, err
297                 }
298         }
299         n, err = writeLabelPairs(
300                 w, metric.Label, additionalLabelName, additionalLabelValue,
301         )
302         written += n
303         if err != nil {
304                 return written, err
305         }
306         err = w.WriteByte(' ')
307         written++
308         if err != nil {
309                 return written, err
310         }
311         n, err = writeFloat(w, value)
312         written += n
313         if err != nil {
314                 return written, err
315         }
316         if metric.TimestampMs != nil {
317                 err = w.WriteByte(' ')
318                 written++
319                 if err != nil {
320                         return written, err
321                 }
322                 n, err = writeInt(w, *metric.TimestampMs)
323                 written += n
324                 if err != nil {
325                         return written, err
326                 }
327         }
328         err = w.WriteByte('\n')
329         written++
330         if err != nil {
331                 return written, err
332         }
333         return written, nil
334 }
335
336 // writeLabelPairs converts a slice of LabelPair proto messages plus the
337 // explicitly given additional label pair into text formatted as required by the
338 // text format and writes it to 'w'. An empty slice in combination with an empty
339 // string 'additionalLabelName' results in nothing being written. Otherwise, the
340 // label pairs are written, escaped as required by the text format, and enclosed
341 // in '{...}'. The function returns the number of bytes written and any error
342 // encountered.
343 func writeLabelPairs(
344         w enhancedWriter,
345         in []*dto.LabelPair,
346         additionalLabelName string, additionalLabelValue float64,
347 ) (int, error) {
348         if len(in) == 0 && additionalLabelName == "" {
349                 return 0, nil
350         }
351         var (
352                 written   int
353                 separator byte = '{'
354         )
355         for _, lp := range in {
356                 err := w.WriteByte(separator)
357                 written++
358                 if err != nil {
359                         return written, err
360                 }
361                 n, err := w.WriteString(lp.GetName())
362                 written += n
363                 if err != nil {
364                         return written, err
365                 }
366                 n, err = w.WriteString(`="`)
367                 written += n
368                 if err != nil {
369                         return written, err
370                 }
371                 n, err = writeEscapedString(w, lp.GetValue(), true)
372                 written += n
373                 if err != nil {
374                         return written, err
375                 }
376                 err = w.WriteByte('"')
377                 written++
378                 if err != nil {
379                         return written, err
380                 }
381                 separator = ','
382         }
383         if additionalLabelName != "" {
384                 err := w.WriteByte(separator)
385                 written++
386                 if err != nil {
387                         return written, err
388                 }
389                 n, err := w.WriteString(additionalLabelName)
390                 written += n
391                 if err != nil {
392                         return written, err
393                 }
394                 n, err = w.WriteString(`="`)
395                 written += n
396                 if err != nil {
397                         return written, err
398                 }
399                 n, err = writeFloat(w, additionalLabelValue)
400                 written += n
401                 if err != nil {
402                         return written, err
403                 }
404                 err = w.WriteByte('"')
405                 written++
406                 if err != nil {
407                         return written, err
408                 }
409         }
410         err := w.WriteByte('}')
411         written++
412         if err != nil {
413                 return written, err
414         }
415         return written, nil
416 }
417
418 // writeEscapedString replaces '\' by '\\', new line character by '\n', and - if
419 // includeDoubleQuote is true - '"' by '\"'.
420 var (
421         escaper       = strings.NewReplacer("\\", `\\`, "\n", `\n`)
422         quotedEscaper = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
423 )
424
425 func writeEscapedString(w enhancedWriter, v string, includeDoubleQuote bool) (int, error) {
426         if includeDoubleQuote {
427                 return quotedEscaper.WriteString(w, v)
428         } else {
429                 return escaper.WriteString(w, v)
430         }
431 }
432
433 // writeFloat is equivalent to fmt.Fprint with a float64 argument but hardcodes
434 // a few common cases for increased efficiency. For non-hardcoded cases, it uses
435 // strconv.AppendFloat to avoid allocations, similar to writeInt.
436 func writeFloat(w enhancedWriter, f float64) (int, error) {
437         switch {
438         case f == 1:
439                 return 1, w.WriteByte('1')
440         case f == 0:
441                 return 1, w.WriteByte('0')
442         case f == -1:
443                 return w.WriteString("-1")
444         case math.IsNaN(f):
445                 return w.WriteString("NaN")
446         case math.IsInf(f, +1):
447                 return w.WriteString("+Inf")
448         case math.IsInf(f, -1):
449                 return w.WriteString("-Inf")
450         default:
451                 bp := numBufPool.Get().(*[]byte)
452                 *bp = strconv.AppendFloat((*bp)[:0], f, 'g', -1, 64)
453                 written, err := w.Write(*bp)
454                 numBufPool.Put(bp)
455                 return written, err
456         }
457 }
458
459 // writeInt is equivalent to fmt.Fprint with an int64 argument but uses
460 // strconv.AppendInt with a byte slice taken from a sync.Pool to avoid
461 // allocations.
462 func writeInt(w enhancedWriter, i int64) (int, error) {
463         bp := numBufPool.Get().(*[]byte)
464         *bp = strconv.AppendInt((*bp)[:0], i, 10)
465         written, err := w.Write(*bp)
466         numBufPool.Put(bp)
467         return written, err
468 }