cubefs

Форк
0
465 строк · 10.9 Кб
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
	"bufio"
18
	"fmt"
19
	"io"
20
	"io/ioutil"
21
	"math"
22
	"strconv"
23
	"strings"
24
	"sync"
25

26
	"github.com/prometheus/common/model"
27

28
	dto "github.com/prometheus/client_model/go"
29
)
30

31
// enhancedWriter has all the enhanced write functions needed here. bufio.Writer
32
// implements it.
33
type enhancedWriter interface {
34
	io.Writer
35
	WriteRune(r rune) (n int, err error)
36
	WriteString(s string) (n int, err error)
37
	WriteByte(c byte) error
38
}
39

40
const (
41
	initialNumBufSize = 24
42
)
43

44
var (
45
	bufPool = sync.Pool{
46
		New: func() interface{} {
47
			return bufio.NewWriter(ioutil.Discard)
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
	// bufio.Writer from the sync.Pool.
79
	w, ok := out.(enhancedWriter)
80
	if !ok {
81
		b := bufPool.Get().(*bufio.Writer)
82
		b.Reset(out)
83
		w = b
84
		defer func() {
85
			bErr := b.Flush()
86
			if err == nil {
87
				err = bErr
88
			}
89
			bufPool.Put(b)
90
		}()
91
	}
92

93
	var n int
94

95
	// Comments, first HELP, then TYPE.
96
	if in.Help != nil {
97
		n, err = w.WriteString("# HELP ")
98
		written += n
99
		if err != nil {
100
			return
101
		}
102
		n, err = w.WriteString(name)
103
		written += n
104
		if err != nil {
105
			return
106
		}
107
		err = w.WriteByte(' ')
108
		written++
109
		if err != nil {
110
			return
111
		}
112
		n, err = writeEscapedString(w, *in.Help, false)
113
		written += n
114
		if err != nil {
115
			return
116
		}
117
		err = w.WriteByte('\n')
118
		written++
119
		if err != nil {
120
			return
121
		}
122
	}
123
	n, err = w.WriteString("# TYPE ")
124
	written += n
125
	if err != nil {
126
		return
127
	}
128
	n, err = w.WriteString(name)
129
	written += n
130
	if err != nil {
131
		return
132
	}
133
	metricType := in.GetType()
134
	switch metricType {
135
	case dto.MetricType_COUNTER:
136
		n, err = w.WriteString(" counter\n")
137
	case dto.MetricType_GAUGE:
138
		n, err = w.WriteString(" gauge\n")
139
	case dto.MetricType_SUMMARY:
140
		n, err = w.WriteString(" summary\n")
141
	case dto.MetricType_UNTYPED:
142
		n, err = w.WriteString(" untyped\n")
143
	case dto.MetricType_HISTOGRAM:
144
		n, err = w.WriteString(" histogram\n")
145
	default:
146
		return written, fmt.Errorf("unknown metric type %s", metricType.String())
147
	}
148
	written += n
149
	if err != nil {
150
		return
151
	}
152

153
	// Finally the samples, one line for each.
154
	for _, metric := range in.Metric {
155
		switch metricType {
156
		case dto.MetricType_COUNTER:
157
			if metric.Counter == nil {
158
				return written, fmt.Errorf(
159
					"expected counter in metric %s %s", name, metric,
160
				)
161
			}
162
			n, err = writeSample(
163
				w, name, "", metric, "", 0,
164
				metric.Counter.GetValue(),
165
			)
166
		case dto.MetricType_GAUGE:
167
			if metric.Gauge == nil {
168
				return written, fmt.Errorf(
169
					"expected gauge in metric %s %s", name, metric,
170
				)
171
			}
172
			n, err = writeSample(
173
				w, name, "", metric, "", 0,
174
				metric.Gauge.GetValue(),
175
			)
176
		case dto.MetricType_UNTYPED:
177
			if metric.Untyped == nil {
178
				return written, fmt.Errorf(
179
					"expected untyped in metric %s %s", name, metric,
180
				)
181
			}
182
			n, err = writeSample(
183
				w, name, "", metric, "", 0,
184
				metric.Untyped.GetValue(),
185
			)
186
		case dto.MetricType_SUMMARY:
187
			if metric.Summary == nil {
188
				return written, fmt.Errorf(
189
					"expected summary in metric %s %s", name, metric,
190
				)
191
			}
192
			for _, q := range metric.Summary.Quantile {
193
				n, err = writeSample(
194
					w, name, "", metric,
195
					model.QuantileLabel, q.GetQuantile(),
196
					q.GetValue(),
197
				)
198
				written += n
199
				if err != nil {
200
					return
201
				}
202
			}
203
			n, err = writeSample(
204
				w, name, "_sum", metric, "", 0,
205
				metric.Summary.GetSampleSum(),
206
			)
207
			written += n
208
			if err != nil {
209
				return
210
			}
211
			n, err = writeSample(
212
				w, name, "_count", metric, "", 0,
213
				float64(metric.Summary.GetSampleCount()),
214
			)
215
		case dto.MetricType_HISTOGRAM:
216
			if metric.Histogram == nil {
217
				return written, fmt.Errorf(
218
					"expected histogram in metric %s %s", name, metric,
219
				)
220
			}
221
			infSeen := false
222
			for _, b := range metric.Histogram.Bucket {
223
				n, err = writeSample(
224
					w, name, "_bucket", metric,
225
					model.BucketLabel, b.GetUpperBound(),
226
					float64(b.GetCumulativeCount()),
227
				)
228
				written += n
229
				if err != nil {
230
					return
231
				}
232
				if math.IsInf(b.GetUpperBound(), +1) {
233
					infSeen = true
234
				}
235
			}
236
			if !infSeen {
237
				n, err = writeSample(
238
					w, name, "_bucket", metric,
239
					model.BucketLabel, math.Inf(+1),
240
					float64(metric.Histogram.GetSampleCount()),
241
				)
242
				written += n
243
				if err != nil {
244
					return
245
				}
246
			}
247
			n, err = writeSample(
248
				w, name, "_sum", metric, "", 0,
249
				metric.Histogram.GetSampleSum(),
250
			)
251
			written += n
252
			if err != nil {
253
				return
254
			}
255
			n, err = writeSample(
256
				w, name, "_count", metric, "", 0,
257
				float64(metric.Histogram.GetSampleCount()),
258
			)
259
		default:
260
			return written, fmt.Errorf(
261
				"unexpected type in metric %s %s", name, metric,
262
			)
263
		}
264
		written += n
265
		if err != nil {
266
			return
267
		}
268
	}
269
	return
270
}
271

272
// writeSample writes a single sample in text format to w, given the metric
273
// name, the metric proto message itself, optionally an additional label name
274
// with a float64 value (use empty string as label name if not required), and
275
// the value. The function returns the number of bytes written and any error
276
// encountered.
277
func writeSample(
278
	w enhancedWriter,
279
	name, suffix string,
280
	metric *dto.Metric,
281
	additionalLabelName string, additionalLabelValue float64,
282
	value float64,
283
) (int, error) {
284
	var written int
285
	n, err := w.WriteString(name)
286
	written += n
287
	if err != nil {
288
		return written, err
289
	}
290
	if suffix != "" {
291
		n, err = w.WriteString(suffix)
292
		written += n
293
		if err != nil {
294
			return written, err
295
		}
296
	}
297
	n, err = writeLabelPairs(
298
		w, metric.Label, additionalLabelName, additionalLabelValue,
299
	)
300
	written += n
301
	if err != nil {
302
		return written, err
303
	}
304
	err = w.WriteByte(' ')
305
	written++
306
	if err != nil {
307
		return written, err
308
	}
309
	n, err = writeFloat(w, value)
310
	written += n
311
	if err != nil {
312
		return written, err
313
	}
314
	if metric.TimestampMs != nil {
315
		err = w.WriteByte(' ')
316
		written++
317
		if err != nil {
318
			return written, err
319
		}
320
		n, err = writeInt(w, *metric.TimestampMs)
321
		written += n
322
		if err != nil {
323
			return written, err
324
		}
325
	}
326
	err = w.WriteByte('\n')
327
	written++
328
	if err != nil {
329
		return written, err
330
	}
331
	return written, nil
332
}
333

334
// writeLabelPairs converts a slice of LabelPair proto messages plus the
335
// explicitly given additional label pair into text formatted as required by the
336
// text format and writes it to 'w'. An empty slice in combination with an empty
337
// string 'additionalLabelName' results in nothing being written. Otherwise, the
338
// label pairs are written, escaped as required by the text format, and enclosed
339
// in '{...}'. The function returns the number of bytes written and any error
340
// encountered.
341
func writeLabelPairs(
342
	w enhancedWriter,
343
	in []*dto.LabelPair,
344
	additionalLabelName string, additionalLabelValue float64,
345
) (int, error) {
346
	if len(in) == 0 && additionalLabelName == "" {
347
		return 0, nil
348
	}
349
	var (
350
		written   int
351
		separator byte = '{'
352
	)
353
	for _, lp := range in {
354
		err := w.WriteByte(separator)
355
		written++
356
		if err != nil {
357
			return written, err
358
		}
359
		n, err := w.WriteString(lp.GetName())
360
		written += n
361
		if err != nil {
362
			return written, err
363
		}
364
		n, err = w.WriteString(`="`)
365
		written += n
366
		if err != nil {
367
			return written, err
368
		}
369
		n, err = writeEscapedString(w, lp.GetValue(), true)
370
		written += n
371
		if err != nil {
372
			return written, err
373
		}
374
		err = w.WriteByte('"')
375
		written++
376
		if err != nil {
377
			return written, err
378
		}
379
		separator = ','
380
	}
381
	if additionalLabelName != "" {
382
		err := w.WriteByte(separator)
383
		written++
384
		if err != nil {
385
			return written, err
386
		}
387
		n, err := w.WriteString(additionalLabelName)
388
		written += n
389
		if err != nil {
390
			return written, err
391
		}
392
		n, err = w.WriteString(`="`)
393
		written += n
394
		if err != nil {
395
			return written, err
396
		}
397
		n, err = writeFloat(w, additionalLabelValue)
398
		written += n
399
		if err != nil {
400
			return written, err
401
		}
402
		err = w.WriteByte('"')
403
		written++
404
		if err != nil {
405
			return written, err
406
		}
407
	}
408
	err := w.WriteByte('}')
409
	written++
410
	if err != nil {
411
		return written, err
412
	}
413
	return written, nil
414
}
415

416
// writeEscapedString replaces '\' by '\\', new line character by '\n', and - if
417
// includeDoubleQuote is true - '"' by '\"'.
418
var (
419
	escaper       = strings.NewReplacer("\\", `\\`, "\n", `\n`)
420
	quotedEscaper = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
421
)
422

423
func writeEscapedString(w enhancedWriter, v string, includeDoubleQuote bool) (int, error) {
424
	if includeDoubleQuote {
425
		return quotedEscaper.WriteString(w, v)
426
	}
427
	return escaper.WriteString(w, v)
428
}
429

430
// writeFloat is equivalent to fmt.Fprint with a float64 argument but hardcodes
431
// a few common cases for increased efficiency. For non-hardcoded cases, it uses
432
// strconv.AppendFloat to avoid allocations, similar to writeInt.
433
func writeFloat(w enhancedWriter, f float64) (int, error) {
434
	switch {
435
	case f == 1:
436
		return 1, w.WriteByte('1')
437
	case f == 0:
438
		return 1, w.WriteByte('0')
439
	case f == -1:
440
		return w.WriteString("-1")
441
	case math.IsNaN(f):
442
		return w.WriteString("NaN")
443
	case math.IsInf(f, +1):
444
		return w.WriteString("+Inf")
445
	case math.IsInf(f, -1):
446
		return w.WriteString("-Inf")
447
	default:
448
		bp := numBufPool.Get().(*[]byte)
449
		*bp = strconv.AppendFloat((*bp)[:0], f, 'g', -1, 64)
450
		written, err := w.Write(*bp)
451
		numBufPool.Put(bp)
452
		return written, err
453
	}
454
}
455

456
// writeInt is equivalent to fmt.Fprint with an int64 argument but uses
457
// strconv.AppendInt with a byte slice taken from a sync.Pool to avoid
458
// allocations.
459
func writeInt(w enhancedWriter, i int64) (int, error) {
460
	bp := numBufPool.Get().(*[]byte)
461
	*bp = strconv.AppendInt((*bp)[:0], i, 10)
462
	written, err := w.Write(*bp)
463
	numBufPool.Put(bp)
464
	return written, err
465
}
466

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.