@@ -53,19 +53,16 @@ const (
53
53
acceptEncodingHeader = "Accept-Encoding"
54
54
)
55
55
56
- var bufPool sync.Pool
57
-
58
- func getBuf () * bytes.Buffer {
59
- buf := bufPool .Get ()
60
- if buf == nil {
61
- return & bytes.Buffer {}
62
- }
63
- return buf .(* bytes.Buffer )
56
+ var gzipPool = sync.Pool {
57
+ New : func () interface {} {
58
+ return gzip .NewWriter (nil )
59
+ },
64
60
}
65
61
66
- func giveBuf (buf * bytes.Buffer ) {
67
- buf .Reset ()
68
- bufPool .Put (buf )
62
+ var bufPool = sync.Pool {
63
+ New : func () interface {} {
64
+ return & bytes.Buffer {}
65
+ },
69
66
}
70
67
71
68
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
@@ -112,7 +109,6 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
112
109
return
113
110
}
114
111
}
115
-
116
112
mfs , err := reg .Gather ()
117
113
if err != nil {
118
114
if opts .ErrorLog != nil {
@@ -133,10 +129,12 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
133
129
}
134
130
135
131
contentType := expfmt .Negotiate (req .Header )
136
- buf := getBuf ()
137
- defer giveBuf (buf )
138
- writer , encoding := decorateWriter (req , buf , opts .DisableCompression )
139
- enc := expfmt .NewEncoder (writer , contentType )
132
+ buf := bufPool .Get ().(* bytes.Buffer )
133
+ buf .Reset ()
134
+ enc := expfmt .NewEncoder (buf , contentType )
135
+
136
+ defer bufPool .Put (buf )
137
+
140
138
var lastErr error
141
139
for _ , mf := range mfs {
142
140
if err := enc .Encode (mf ); err != nil {
@@ -155,22 +153,28 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
155
153
}
156
154
}
157
155
}
158
- if closer , ok := writer .(io.Closer ); ok {
159
- closer .Close ()
160
- }
156
+
161
157
if lastErr != nil && buf .Len () == 0 {
162
158
http .Error (w , "No metrics encoded, last error:\n \n " + lastErr .Error (), http .StatusInternalServerError )
163
159
return
164
160
}
165
161
header := w .Header ()
166
162
header .Set (contentTypeHeader , string (contentType ))
167
163
header .Set (contentLengthHeader , fmt .Sprint (buf .Len ()))
168
- if encoding != "" {
169
- header .Set (contentEncodingHeader , encoding )
170
- }
171
- if _ , err := w .Write (buf .Bytes ()); err != nil && opts .ErrorLog != nil {
172
- opts .ErrorLog .Println ("error while sending encoded metrics:" , err )
164
+
165
+ if ! opts .DisableCompression && gzipAccepted (req .Header ) {
166
+ header .Set (contentEncodingHeader , "gzip" )
167
+ gz := gzipPool .Get ().(* gzip.Writer )
168
+ defer gzipPool .Put (gz )
169
+
170
+ gz .Reset (w )
171
+ defer gz .Close ()
172
+
173
+ zipWriter := gzipResponseWriter {gz , w }
174
+ writeResult (zipWriter , buf , opts )
175
+ return
173
176
}
177
+ writeResult (w , buf , opts )
174
178
// TODO(beorn7): Consider streaming serving of metrics.
175
179
})
176
180
@@ -292,20 +296,36 @@ type HandlerOpts struct {
292
296
Timeout time.Duration
293
297
}
294
298
295
- // decorateWriter wraps a writer to handle gzip compression if requested. It
296
- // returns the decorated writer and the appropriate "Content-Encoding" header
297
- // (which is empty if no compression is enabled).
298
- func decorateWriter (request * http.Request , writer io.Writer , compressionDisabled bool ) (io.Writer , string ) {
299
- if compressionDisabled {
300
- return writer , ""
299
+ // gzipResponseWriter in charge of wrapping io.Writer and http.ReponseWriter
300
+ // together, allowing to get a single struct which implements both interface.
301
+ type gzipResponseWriter struct {
302
+ io.Writer
303
+ http.ResponseWriter
304
+ }
305
+
306
+ func (w gzipResponseWriter ) Write (b []byte ) (int , error ) {
307
+ return w .Writer .Write (b )
308
+ }
309
+
310
+ // writeResult to buf using http.ResponseWriter.
311
+ // If ErrorLog is enabled, err is logged in.
312
+ func writeResult (w http.ResponseWriter , buf * bytes.Buffer , opts HandlerOpts ) {
313
+ if _ , err := w .Write (buf .Bytes ()); err != nil && opts .ErrorLog != nil {
314
+ opts .ErrorLog .Println ("error while sending encoded metrics:" , err )
301
315
}
302
- header := request .Header .Get (acceptEncodingHeader )
303
- parts := strings .Split (header , "," )
316
+ }
317
+
318
+ // gzipHandler return a http.HandlerFunc in charge of compressing the content
319
+ // of the given http.HandlerFunc
320
+ func gzipAccepted (header http.Header ) bool {
321
+
322
+ a := header .Get (acceptEncodingHeader )
323
+ parts := strings .Split (a , "," )
304
324
for _ , part := range parts {
305
325
part = strings .TrimSpace (part )
306
326
if part == "gzip" || strings .HasPrefix (part , "gzip;" ) {
307
- return gzip . NewWriter ( writer ), "gzip"
327
+ return true
308
328
}
309
329
}
310
- return writer , ""
330
+ return false
311
331
}
0 commit comments