cubefs
162 строки · 5.1 Кб
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
14package expfmt
15
16import (
17"fmt"
18"io"
19"net/http"
20
21"github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
22"github.com/matttproud/golang_protobuf_extensions/pbutil"
23"github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg"
24
25dto "github.com/prometheus/client_model/go"
26)
27
28// Encoder types encode metric families into an underlying wire protocol.
29type Encoder interface {
30Encode(*dto.MetricFamily) error
31}
32
33// Closer is implemented by Encoders that need to be closed to finalize
34// encoding. (For example, OpenMetrics needs a final `# EOF` line.)
35//
36// Note that all Encoder implementations returned from this package implement
37// Closer, too, even if the Close call is a no-op. This happens in preparation
38// for adding a Close method to the Encoder interface directly in a (mildly
39// breaking) release in the future.
40type Closer interface {
41Close() error
42}
43
44type encoderCloser struct {
45encode func(*dto.MetricFamily) error
46close func() error
47}
48
49func (ec encoderCloser) Encode(v *dto.MetricFamily) error {
50return ec.encode(v)
51}
52
53func (ec encoderCloser) Close() error {
54return ec.close()
55}
56
57// Negotiate returns the Content-Type based on the given Accept header. If no
58// appropriate accepted type is found, FmtText is returned (which is the
59// Prometheus text format). This function will never negotiate FmtOpenMetrics,
60// as the support is still experimental. To include the option to negotiate
61// FmtOpenMetrics, use NegotiateOpenMetrics.
62func Negotiate(h http.Header) Format {
63for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
64ver := ac.Params["version"]
65if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
66switch ac.Params["encoding"] {
67case "delimited":
68return FmtProtoDelim
69case "text":
70return FmtProtoText
71case "compact-text":
72return FmtProtoCompact
73}
74}
75if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
76return FmtText
77}
78}
79return FmtText
80}
81
82// NegotiateIncludingOpenMetrics works like Negotiate but includes
83// FmtOpenMetrics as an option for the result. Note that this function is
84// temporary and will disappear once FmtOpenMetrics is fully supported and as
85// such may be negotiated by the normal Negotiate function.
86func NegotiateIncludingOpenMetrics(h http.Header) Format {
87for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
88ver := ac.Params["version"]
89if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
90switch ac.Params["encoding"] {
91case "delimited":
92return FmtProtoDelim
93case "text":
94return FmtProtoText
95case "compact-text":
96return FmtProtoCompact
97}
98}
99if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
100return FmtText
101}
102if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion || ver == "") {
103return FmtOpenMetrics
104}
105}
106return FmtText
107}
108
109// NewEncoder returns a new encoder based on content type negotiation. All
110// Encoder implementations returned by NewEncoder also implement Closer, and
111// callers should always call the Close method. It is currently only required
112// for FmtOpenMetrics, but a future (breaking) release will add the Close method
113// to the Encoder interface directly. The current version of the Encoder
114// interface is kept for backwards compatibility.
115func NewEncoder(w io.Writer, format Format) Encoder {
116switch format {
117case FmtProtoDelim:
118return encoderCloser{
119encode: func(v *dto.MetricFamily) error {
120_, err := pbutil.WriteDelimited(w, v)
121return err
122},
123close: func() error { return nil },
124}
125case FmtProtoCompact:
126return encoderCloser{
127encode: func(v *dto.MetricFamily) error {
128_, err := fmt.Fprintln(w, v.String())
129return err
130},
131close: func() error { return nil },
132}
133case FmtProtoText:
134return encoderCloser{
135encode: func(v *dto.MetricFamily) error {
136_, err := fmt.Fprintln(w, proto.MarshalTextString(v))
137return err
138},
139close: func() error { return nil },
140}
141case FmtText:
142return encoderCloser{
143encode: func(v *dto.MetricFamily) error {
144_, err := MetricFamilyToText(w, v)
145return err
146},
147close: func() error { return nil },
148}
149case FmtOpenMetrics:
150return encoderCloser{
151encode: func(v *dto.MetricFamily) error {
152_, err := MetricFamilyToOpenMetrics(w, v)
153return err
154},
155close: func() error {
156_, err := FinalizeOpenMetrics(w)
157return err
158},
159}
160}
161panic(fmt.Errorf("expfmt.NewEncoder: unknown format %q", format))
162}
163