@@ -15,9 +15,7 @@ package prometheus
15
15
16
16
import (
17
17
"bufio"
18
- "bytes"
19
18
"compress/gzip"
20
- "fmt"
21
19
"io"
22
20
"net"
23
21
"net/http"
@@ -41,19 +39,10 @@ const (
41
39
acceptEncodingHeader = "Accept-Encoding"
42
40
)
43
41
44
- var bufPool sync.Pool
45
-
46
- func getBuf () * bytes.Buffer {
47
- buf := bufPool .Get ()
48
- if buf == nil {
49
- return & bytes.Buffer {}
50
- }
51
- return buf .(* bytes.Buffer )
52
- }
53
-
54
- func giveBuf (buf * bytes.Buffer ) {
55
- buf .Reset ()
56
- bufPool .Put (buf )
42
+ var gzipPool = sync.Pool {
43
+ New : func () interface {} {
44
+ return gzip .NewWriter (nil )
45
+ },
57
46
}
58
47
59
48
// Handler returns an HTTP handler for the DefaultGatherer. It is
@@ -71,58 +60,40 @@ func Handler() http.Handler {
71
60
// Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{})
72
61
// instead. See there for further documentation.
73
62
func UninstrumentedHandler () http.Handler {
74
- return http .HandlerFunc (func (w http.ResponseWriter , req * http.Request ) {
63
+ return http .HandlerFunc (func (rsp http.ResponseWriter , req * http.Request ) {
75
64
mfs , err := DefaultGatherer .Gather ()
76
65
if err != nil {
77
- http . Error ( w , "An error has occurred during metrics collection: \n \n " + err . Error (), http . StatusInternalServerError )
66
+ httpError ( rsp , err )
78
67
return
79
68
}
80
69
81
70
contentType := expfmt .Negotiate (req .Header )
82
- buf := getBuf ()
83
- defer giveBuf (buf )
84
- writer , encoding := decorateWriter (req , buf )
85
- enc := expfmt .NewEncoder (writer , contentType )
86
- var lastErr error
71
+ header := rsp .Header ()
72
+ header .Set (contentTypeHeader , string (contentType ))
73
+
74
+ w := io .Writer (rsp )
75
+ if gzipAccepted (req .Header ) {
76
+ header .Set (contentEncodingHeader , "gzip" )
77
+ gz := gzipPool .Get ().(* gzip.Writer )
78
+ defer gzipPool .Put (gz )
79
+
80
+ gz .Reset (w )
81
+ defer gz .Close ()
82
+
83
+ w = gz
84
+ }
85
+
86
+ enc := expfmt .NewEncoder (w , contentType )
87
+
87
88
for _ , mf := range mfs {
88
89
if err := enc .Encode (mf ); err != nil {
89
- lastErr = err
90
- http .Error (w , "An error has occurred during metrics encoding:\n \n " + err .Error (), http .StatusInternalServerError )
90
+ httpError (rsp , err )
91
91
return
92
92
}
93
93
}
94
- if closer , ok := writer .(io.Closer ); ok {
95
- closer .Close ()
96
- }
97
- if lastErr != nil && buf .Len () == 0 {
98
- http .Error (w , "No metrics encoded, last error:\n \n " + lastErr .Error (), http .StatusInternalServerError )
99
- return
100
- }
101
- header := w .Header ()
102
- header .Set (contentTypeHeader , string (contentType ))
103
- header .Set (contentLengthHeader , fmt .Sprint (buf .Len ()))
104
- if encoding != "" {
105
- header .Set (contentEncodingHeader , encoding )
106
- }
107
- w .Write (buf .Bytes ())
108
94
})
109
95
}
110
96
111
- // decorateWriter wraps a writer to handle gzip compression if requested. It
112
- // returns the decorated writer and the appropriate "Content-Encoding" header
113
- // (which is empty if no compression is enabled).
114
- func decorateWriter (request * http.Request , writer io.Writer ) (io.Writer , string ) {
115
- header := request .Header .Get (acceptEncodingHeader )
116
- parts := strings .Split (header , "," )
117
- for _ , part := range parts {
118
- part = strings .TrimSpace (part )
119
- if part == "gzip" || strings .HasPrefix (part , "gzip;" ) {
120
- return gzip .NewWriter (writer ), "gzip"
121
- }
122
- }
123
- return writer , ""
124
- }
125
-
126
97
var instLabels = []string {"method" , "code" }
127
98
128
99
type nower interface {
@@ -503,3 +474,31 @@ func sanitizeCode(s int) string {
503
474
return strconv .Itoa (s )
504
475
}
505
476
}
477
+
478
+ // gzipAccepted returns whether the client will accept gzip-encoded content.
479
+ func gzipAccepted (header http.Header ) bool {
480
+ a := header .Get (acceptEncodingHeader )
481
+ parts := strings .Split (a , "," )
482
+ for _ , part := range parts {
483
+ part = strings .TrimSpace (part )
484
+ if part == "gzip" || strings .HasPrefix (part , "gzip;" ) {
485
+ return true
486
+ }
487
+ }
488
+ return false
489
+ }
490
+
491
+ // httpError removes any content-encoding header and then calls http.Error with
492
+ // the provided error and http.StatusInternalServerErrer. Error contents is
493
+ // supposed to be uncompressed plain text. However, same as with a plain
494
+ // http.Error, any header settings will be void if the header has already been
495
+ // sent. The error message will still be written to the writer, but it will
496
+ // probably be of limited use.
497
+ func httpError (rsp http.ResponseWriter , err error ) {
498
+ rsp .Header ().Del (contentEncodingHeader )
499
+ http .Error (
500
+ rsp ,
501
+ "An error has occurred while serving metrics:\n \n " + err .Error (),
502
+ http .StatusInternalServerError ,
503
+ )
504
+ }
0 commit comments