blob: bd4e34745461e3e6ec13423c8506990d4ebc0bd5 [file] [log] [blame]
khenaidoo59ce9dd2019-11-11 13:05:32 -05001// 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"
22 "github.com/matttproud/golang_protobuf_extensions/pbutil"
23 "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg"
24
25 dto "github.com/prometheus/client_model/go"
26)
27
28// Encoder types encode metric families into an underlying wire protocol.
29type Encoder interface {
30 Encode(*dto.MetricFamily) error
31}
32
khenaidoo26721882021-08-11 17:42:52 -040033// 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 {
41 Close() error
khenaidoo59ce9dd2019-11-11 13:05:32 -050042}
43
khenaidoo26721882021-08-11 17:42:52 -040044type encoderCloser struct {
45 encode func(*dto.MetricFamily) error
46 close func() error
47}
48
49func (ec encoderCloser) Encode(v *dto.MetricFamily) error {
50 return ec.encode(v)
51}
52
53func (ec encoderCloser) Close() error {
54 return 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.
khenaidoo59ce9dd2019-11-11 13:05:32 -050062func Negotiate(h http.Header) Format {
63 for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
khenaidoo26721882021-08-11 17:42:52 -040064 ver := ac.Params["version"]
khenaidoo59ce9dd2019-11-11 13:05:32 -050065 if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
66 switch ac.Params["encoding"] {
67 case "delimited":
68 return FmtProtoDelim
69 case "text":
70 return FmtProtoText
71 case "compact-text":
72 return FmtProtoCompact
73 }
74 }
khenaidoo59ce9dd2019-11-11 13:05:32 -050075 if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
76 return FmtText
77 }
78 }
79 return FmtText
80}
81
khenaidoo26721882021-08-11 17:42:52 -040082// 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 {
87 for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
88 ver := ac.Params["version"]
89 if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
90 switch ac.Params["encoding"] {
91 case "delimited":
92 return FmtProtoDelim
93 case "text":
94 return FmtProtoText
95 case "compact-text":
96 return FmtProtoCompact
97 }
98 }
99 if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
100 return FmtText
101 }
102 if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion || ver == "") {
103 return FmtOpenMetrics
104 }
105 }
106 return 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.
khenaidoo59ce9dd2019-11-11 13:05:32 -0500115func NewEncoder(w io.Writer, format Format) Encoder {
116 switch format {
117 case FmtProtoDelim:
khenaidoo26721882021-08-11 17:42:52 -0400118 return encoderCloser{
119 encode: func(v *dto.MetricFamily) error {
120 _, err := pbutil.WriteDelimited(w, v)
121 return err
122 },
123 close: func() error { return nil },
124 }
khenaidoo59ce9dd2019-11-11 13:05:32 -0500125 case FmtProtoCompact:
khenaidoo26721882021-08-11 17:42:52 -0400126 return encoderCloser{
127 encode: func(v *dto.MetricFamily) error {
128 _, err := fmt.Fprintln(w, v.String())
129 return err
130 },
131 close: func() error { return nil },
132 }
khenaidoo59ce9dd2019-11-11 13:05:32 -0500133 case FmtProtoText:
khenaidoo26721882021-08-11 17:42:52 -0400134 return encoderCloser{
135 encode: func(v *dto.MetricFamily) error {
136 _, err := fmt.Fprintln(w, proto.MarshalTextString(v))
137 return err
138 },
139 close: func() error { return nil },
140 }
khenaidoo59ce9dd2019-11-11 13:05:32 -0500141 case FmtText:
khenaidoo26721882021-08-11 17:42:52 -0400142 return encoderCloser{
143 encode: func(v *dto.MetricFamily) error {
144 _, err := MetricFamilyToText(w, v)
145 return err
146 },
147 close: func() error { return nil },
148 }
149 case FmtOpenMetrics:
150 return encoderCloser{
151 encode: func(v *dto.MetricFamily) error {
152 _, err := MetricFamilyToOpenMetrics(w, v)
153 return err
154 },
155 close: func() error {
156 _, err := FinalizeOpenMetrics(w)
157 return err
158 },
159 }
khenaidoo59ce9dd2019-11-11 13:05:32 -0500160 }
khenaidoo26721882021-08-11 17:42:52 -0400161 panic(fmt.Errorf("expfmt.NewEncoder: unknown format %q", format))
khenaidoo59ce9dd2019-11-11 13:05:32 -0500162}