...

Source file src/github.com/prometheus/common/expfmt/encode.go

Documentation: github.com/prometheus/common/expfmt

     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  	"net/http"
    20  
    21  	"google.golang.org/protobuf/encoding/protodelim"
    22  	"google.golang.org/protobuf/encoding/prototext"
    23  
    24  	"github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg"
    25  	"github.com/prometheus/common/model"
    26  
    27  	dto "github.com/prometheus/client_model/go"
    28  )
    29  
    30  // Encoder types encode metric families into an underlying wire protocol.
    31  type Encoder interface {
    32  	Encode(*dto.MetricFamily) error
    33  }
    34  
    35  // Closer is implemented by Encoders that need to be closed to finalize
    36  // encoding. (For example, OpenMetrics needs a final `# EOF` line.)
    37  //
    38  // Note that all Encoder implementations returned from this package implement
    39  // Closer, too, even if the Close call is a no-op. This happens in preparation
    40  // for adding a Close method to the Encoder interface directly in a (mildly
    41  // breaking) release in the future.
    42  type Closer interface {
    43  	Close() error
    44  }
    45  
    46  type encoderCloser struct {
    47  	encode func(*dto.MetricFamily) error
    48  	close  func() error
    49  }
    50  
    51  func (ec encoderCloser) Encode(v *dto.MetricFamily) error {
    52  	return ec.encode(v)
    53  }
    54  
    55  func (ec encoderCloser) Close() error {
    56  	return ec.close()
    57  }
    58  
    59  // Negotiate returns the Content-Type based on the given Accept header. If no
    60  // appropriate accepted type is found, FmtText is returned (which is the
    61  // Prometheus text format). This function will never negotiate FmtOpenMetrics,
    62  // as the support is still experimental. To include the option to negotiate
    63  // FmtOpenMetrics, use NegotiateOpenMetrics.
    64  func Negotiate(h http.Header) Format {
    65  	escapingScheme := Format(fmt.Sprintf("; escaping=%s", Format(model.NameEscapingScheme.String())))
    66  	for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
    67  		if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" {
    68  			switch Format(escapeParam) {
    69  			case model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues:
    70  				escapingScheme = Format(fmt.Sprintf("; escaping=%s", escapeParam))
    71  			default:
    72  				// If the escaping parameter is unknown, ignore it.
    73  			}
    74  		}
    75  		ver := ac.Params["version"]
    76  		if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
    77  			switch ac.Params["encoding"] {
    78  			case "delimited":
    79  				return fmtProtoDelim + escapingScheme
    80  			case "text":
    81  				return fmtProtoText + escapingScheme
    82  			case "compact-text":
    83  				return fmtProtoCompact + escapingScheme
    84  			}
    85  		}
    86  		if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
    87  			return fmtText + escapingScheme
    88  		}
    89  	}
    90  	return fmtText + escapingScheme
    91  }
    92  
    93  // NegotiateIncludingOpenMetrics works like Negotiate but includes
    94  // FmtOpenMetrics as an option for the result. Note that this function is
    95  // temporary and will disappear once FmtOpenMetrics is fully supported and as
    96  // such may be negotiated by the normal Negotiate function.
    97  func NegotiateIncludingOpenMetrics(h http.Header) Format {
    98  	escapingScheme := Format(fmt.Sprintf("; escaping=%s", Format(model.NameEscapingScheme.String())))
    99  	for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
   100  		if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" {
   101  			switch Format(escapeParam) {
   102  			case model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues:
   103  				escapingScheme = Format(fmt.Sprintf("; escaping=%s", escapeParam))
   104  			default:
   105  				// If the escaping parameter is unknown, ignore it.
   106  			}
   107  		}
   108  		ver := ac.Params["version"]
   109  		if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
   110  			switch ac.Params["encoding"] {
   111  			case "delimited":
   112  				return fmtProtoDelim + escapingScheme
   113  			case "text":
   114  				return fmtProtoText + escapingScheme
   115  			case "compact-text":
   116  				return fmtProtoCompact + escapingScheme
   117  			}
   118  		}
   119  		if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
   120  			return fmtText + escapingScheme
   121  		}
   122  		if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion_0_0_1 || ver == OpenMetricsVersion_1_0_0 || ver == "") {
   123  			switch ver {
   124  			case OpenMetricsVersion_1_0_0:
   125  				return fmtOpenMetrics_1_0_0 + escapingScheme
   126  			default:
   127  				return fmtOpenMetrics_0_0_1 + escapingScheme
   128  			}
   129  		}
   130  	}
   131  	return fmtText + escapingScheme
   132  }
   133  
   134  // NewEncoder returns a new encoder based on content type negotiation. All
   135  // Encoder implementations returned by NewEncoder also implement Closer, and
   136  // callers should always call the Close method. It is currently only required
   137  // for FmtOpenMetrics, but a future (breaking) release will add the Close method
   138  // to the Encoder interface directly. The current version of the Encoder
   139  // interface is kept for backwards compatibility.
   140  // In cases where the Format does not allow for UTF-8 names, the global
   141  // NameEscapingScheme will be applied.
   142  //
   143  // NewEncoder can be called with additional options to customize the OpenMetrics text output.
   144  // For example:
   145  // NewEncoder(w, FmtOpenMetrics_1_0_0, WithCreatedLines())
   146  //
   147  // Extra options are ignored for all other formats.
   148  func NewEncoder(w io.Writer, format Format, options ...EncoderOption) Encoder {
   149  	escapingScheme := format.ToEscapingScheme()
   150  
   151  	switch format.FormatType() {
   152  	case TypeProtoDelim:
   153  		return encoderCloser{
   154  			encode: func(v *dto.MetricFamily) error {
   155  				_, err := protodelim.MarshalTo(w, v)
   156  				return err
   157  			},
   158  			close: func() error { return nil },
   159  		}
   160  	case TypeProtoCompact:
   161  		return encoderCloser{
   162  			encode: func(v *dto.MetricFamily) error {
   163  				_, err := fmt.Fprintln(w, model.EscapeMetricFamily(v, escapingScheme).String())
   164  				return err
   165  			},
   166  			close: func() error { return nil },
   167  		}
   168  	case TypeProtoText:
   169  		return encoderCloser{
   170  			encode: func(v *dto.MetricFamily) error {
   171  				_, err := fmt.Fprintln(w, prototext.Format(model.EscapeMetricFamily(v, escapingScheme)))
   172  				return err
   173  			},
   174  			close: func() error { return nil },
   175  		}
   176  	case TypeTextPlain:
   177  		return encoderCloser{
   178  			encode: func(v *dto.MetricFamily) error {
   179  				_, err := MetricFamilyToText(w, model.EscapeMetricFamily(v, escapingScheme))
   180  				return err
   181  			},
   182  			close: func() error { return nil },
   183  		}
   184  	case TypeOpenMetrics:
   185  		return encoderCloser{
   186  			encode: func(v *dto.MetricFamily) error {
   187  				_, err := MetricFamilyToOpenMetrics(w, model.EscapeMetricFamily(v, escapingScheme), options...)
   188  				return err
   189  			},
   190  			close: func() error {
   191  				_, err := FinalizeOpenMetrics(w)
   192  				return err
   193  			},
   194  		}
   195  	}
   196  	panic(fmt.Errorf("expfmt.NewEncoder: unknown format %q", format))
   197  }
   198  

View as plain text