// Copyright 2014 The Prometheus Authors// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.package expfmtimport (dto)// enhancedWriter has all the enhanced write functions needed here. bufio.Writer// implements it.type enhancedWriter interface {io.Writer WriteRune(r rune) (n int, err error) WriteString(s string) (n int, err error) WriteByte(c byte) error}const ( initialNumBufSize = 24)var ( bufPool = sync.Pool{New: func() interface{} {returnbufio.NewWriter(io.Discard) }, } numBufPool = sync.Pool{New: func() interface{} { := make([]byte, 0, initialNumBufSize)return & }, })// MetricFamilyToText converts a MetricFamily proto message into text format and// writes the resulting lines to 'out'. It returns the number of bytes written// and any error encountered. The output will have the same order as the input,// no further sorting is performed. Furthermore, this function assumes the input// is already sanitized and does not perform any sanity checks. If the input// contains duplicate metrics or invalid metric or label names, the conversion// will result in invalid text format output.//// If metric names conform to the legacy validation pattern, they will be placed// outside the brackets in the traditional way, like `foo{}`. If the metric name// fails the legacy validation check, it will be placed quoted inside the// brackets: `{"foo"}`. As stated above, the input is assumed to be santized and// no error will be thrown in this case.//// Similar to metric names, if label names conform to the legacy validation// pattern, they will be unquoted as normal, like `foo{bar="baz"}`. If the label// name fails the legacy validation check, it will be quoted:// `foo{"bar"="baz"}`. As stated above, the input is assumed to be santized and// no error will be thrown in this case.//// This method fulfills the type 'prometheus.encoder'.func ( io.Writer, *dto.MetricFamily) ( int, error) {// Fail-fast checks.iflen(.Metric) == 0 {return0, fmt.Errorf("MetricFamily has no metrics: %s", ) } := .GetName()if == "" {return0, fmt.Errorf("MetricFamily has no name: %s", ) }// Try the interface upgrade. If it doesn't work, we'll use a // bufio.Writer from the sync.Pool. , := .(enhancedWriter)if ! { := bufPool.Get().(*bufio.Writer) .Reset() = deferfunc() { := .Flush()if == nil { = }bufPool.Put() }() }varint// Comments, first HELP, then TYPE.if .Help != nil { , = .WriteString("# HELP ") += if != nil {return } , = writeName(, ) += if != nil {return } = .WriteByte(' ') ++if != nil {return } , = writeEscapedString(, *.Help, false) += if != nil {return } = .WriteByte('\n') ++if != nil {return } } , = .WriteString("# TYPE ") += if != nil {return } , = writeName(, ) += if != nil {return } := .GetType()switch {casedto.MetricType_COUNTER: , = .WriteString(" counter\n")casedto.MetricType_GAUGE: , = .WriteString(" gauge\n")casedto.MetricType_SUMMARY: , = .WriteString(" summary\n")casedto.MetricType_UNTYPED: , = .WriteString(" untyped\n")casedto.MetricType_HISTOGRAM: , = .WriteString(" histogram\n")default:return , fmt.Errorf("unknown metric type %s", .String()) } += if != nil {return }// Finally the samples, one line for each.for , := range .Metric {switch {casedto.MetricType_COUNTER:if .Counter == nil {return , fmt.Errorf("expected counter in metric %s %s", , , ) } , = writeSample( , , "", , "", 0, .Counter.GetValue(), )casedto.MetricType_GAUGE:if .Gauge == nil {return , fmt.Errorf("expected gauge in metric %s %s", , , ) } , = writeSample( , , "", , "", 0, .Gauge.GetValue(), )casedto.MetricType_UNTYPED:if .Untyped == nil {return , fmt.Errorf("expected untyped in metric %s %s", , , ) } , = writeSample( , , "", , "", 0, .Untyped.GetValue(), )casedto.MetricType_SUMMARY:if .Summary == nil {return , fmt.Errorf("expected summary in metric %s %s", , , ) }for , := range .Summary.Quantile { , = writeSample( , , "", ,model.QuantileLabel, .GetQuantile(), .GetValue(), ) += if != nil {return } } , = writeSample( , , "_sum", , "", 0, .Summary.GetSampleSum(), ) += if != nil {return } , = writeSample( , , "_count", , "", 0,float64(.Summary.GetSampleCount()), )casedto.MetricType_HISTOGRAM:if .Histogram == nil {return , fmt.Errorf("expected histogram in metric %s %s", , , ) } := falsefor , := range .Histogram.Bucket { , = writeSample( , , "_bucket", ,model.BucketLabel, .GetUpperBound(),float64(.GetCumulativeCount()), ) += if != nil {return }ifmath.IsInf(.GetUpperBound(), +1) { = true } }if ! { , = writeSample( , , "_bucket", ,model.BucketLabel, math.Inf(+1),float64(.Histogram.GetSampleCount()), ) += if != nil {return } } , = writeSample( , , "_sum", , "", 0, .Histogram.GetSampleSum(), ) += if != nil {return } , = writeSample( , , "_count", , "", 0,float64(.Histogram.GetSampleCount()), )default:return , fmt.Errorf("unexpected type in metric %s %s", , , ) } += if != nil {return } }return}// writeSample writes a single sample in text format to w, given the metric// name, the metric proto message itself, optionally an additional label name// with a float64 value (use empty string as label name if not required), and// the value. The function returns the number of bytes written and any error// encountered.func writeSample(enhancedWriter, , string, *dto.Metric,string, float64,float64,) (int, error) { := 0 , := writeNameAndLabelPairs( , +, .Label, , , ) += if != nil {return , } = .WriteByte(' ') ++if != nil {return , } , = writeFloat(, ) += if != nil {return , }if .TimestampMs != nil { = .WriteByte(' ') ++if != nil {return , } , = writeInt(, *.TimestampMs) += if != nil {return , } } = .WriteByte('\n') ++if != nil {return , }return , nil}// writeNameAndLabelPairs converts a slice of LabelPair proto messages plus the// explicitly given metric name and additional label pair into text formatted as// required by the text format and writes it to 'w'. An empty slice in// combination with an empty string 'additionalLabelName' results in nothing// being written. Otherwise, the label pairs are written, escaped as required by// the text format, and enclosed in '{...}'. The function returns the number of// bytes written and any error encountered. If the metric name is not// legacy-valid, it will be put inside the brackets as well. Legacy-invalid// label names will also be quoted.func writeNameAndLabelPairs(enhancedWriter,string, []*dto.LabelPair,string, float64,) (int, error) {var (intbyte = '{' = false )if != "" {// If the name does not pass the legacy validity check, we must put the // metric name inside the braces.if !model.IsValidLegacyMetricName() { = true := .WriteByte() ++if != nil {return , } = ',' } , := writeName(, ) += if != nil {return , } }iflen() == 0 && == "" {if { := .WriteByte('}') ++if != nil {return , } }return , nil }for , := range { := .WriteByte() ++if != nil {return , } , := writeName(, .GetName()) += if != nil {return , } , = .WriteString(`="`) += if != nil {return , } , = writeEscapedString(, .GetValue(), true) += if != nil {return , } = .WriteByte('"') ++if != nil {return , } = ',' }if != "" { := .WriteByte() ++if != nil {return , } , := .WriteString() += if != nil {return , } , = .WriteString(`="`) += if != nil {return , } , = writeFloat(, ) += if != nil {return , } = .WriteByte('"') ++if != nil {return , } } := .WriteByte('}') ++if != nil {return , }return , nil}// writeEscapedString replaces '\' by '\\', new line character by '\n', and - if// includeDoubleQuote is true - '"' by '\"'.var ( escaper = strings.NewReplacer("\\", `\\`, "\n", `\n`) quotedEscaper = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`))func writeEscapedString( enhancedWriter, string, bool) (int, error) {if {returnquotedEscaper.WriteString(, ) }returnescaper.WriteString(, )}// writeFloat is equivalent to fmt.Fprint with a float64 argument but hardcodes// a few common cases for increased efficiency. For non-hardcoded cases, it uses// strconv.AppendFloat to avoid allocations, similar to writeInt.func writeFloat( enhancedWriter, float64) (int, error) {switch {case == 1:return1, .WriteByte('1')case == 0:return1, .WriteByte('0')case == -1:return .WriteString("-1")casemath.IsNaN():return .WriteString("NaN")casemath.IsInf(, +1):return .WriteString("+Inf")casemath.IsInf(, -1):return .WriteString("-Inf")default: := numBufPool.Get().(*[]byte) * = strconv.AppendFloat((*)[:0], , 'g', -1, 64) , := .Write(*)numBufPool.Put()return , }}// writeInt is equivalent to fmt.Fprint with an int64 argument but uses// strconv.AppendInt with a byte slice taken from a sync.Pool to avoid// allocations.func writeInt( enhancedWriter, int64) (int, error) { := numBufPool.Get().(*[]byte) * = strconv.AppendInt((*)[:0], , 10) , := .Write(*)numBufPool.Put()return , }// writeName writes a string as-is if it complies with the legacy naming// scheme, or escapes it in double quotes if not.func writeName( enhancedWriter, string) (int, error) {ifmodel.IsValidLegacyMetricName() {return .WriteString() }varintvarerror = .WriteByte('"') ++if != nil {return , }varint , = writeEscapedString(, , true) += if != nil {return , } = .WriteByte('"') ++return , }
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.