Skip to content

Commit 91ef55a

Browse files
committed
Add time to write header handler middleware
1 parent 42552c1 commit 91ef55a

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

prometheus/promhttp/instrument_server.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,40 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler)
108108
})
109109
}
110110

111+
// InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided
112+
// http.Handler to observe with the provided ObserverVec the request duration
113+
// until the response headers are written. The ObserverVec must have zero, one,
114+
// or two labels. The only allowed label names are "code" and "method". The
115+
// function panics if any other instance labels are provided. The Observe
116+
// method of the Observer in the ObserverVec is called with the request
117+
// duration in seconds. Partitioning happens by HTTP status code and/or HTTP
118+
// method if the respective instance label names are present in the
119+
// ObserverVec. For unpartitioned observations, use an ObserverVec with zero
120+
// labels. Note that partitioning of Histograms is expensive and should be used
121+
// judiciously.
122+
//
123+
// If the wrapped Handler does not set a status code via WriteHeader, no value
124+
// is reported.
125+
//
126+
// If the wrapped Handler panics before calling WriteHeader, no value is
127+
// reported.
128+
//
129+
// See the example for InstrumentHandlerDuration for example usage.
130+
func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
131+
code, method := checkLabels(obs)
132+
133+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
134+
now := time.Now()
135+
d := &wroteHeaderDelegator{
136+
delegator: newDelegator(w),
137+
observeWriteHeader: func(status int) {
138+
obs.With(labels(code, method, r.Method, status)).Observe(time.Since(now).Seconds())
139+
},
140+
}
141+
next.ServeHTTP(d, r)
142+
})
143+
}
144+
111145
// InstrumentHandlerRequestSize is a middleware that wraps the provided
112146
// http.Handler to observe the request size with the provided ObserverVec.
113147
// The ObserverVec must have zero, one, or two labels. The only allowed label
@@ -415,6 +449,19 @@ type delegator interface {
415449
http.ResponseWriter
416450
}
417451

452+
type wroteHeaderDelegator struct {
453+
observeWriteHeader func(int)
454+
455+
delegator
456+
}
457+
458+
func (r *wroteHeaderDelegator) WriteHeader(code int) {
459+
r.delegator.WriteHeader(code)
460+
if r.observeWriteHeader != nil {
461+
r.observeWriteHeader(code)
462+
}
463+
}
464+
418465
type responseWriterDelegator struct {
419466
http.ResponseWriter
420467

prometheus/promhttp/instrument_server_test.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ func TestMiddlewareAPI(t *testing.T) {
4848
[]string{"method"},
4949
)
5050

51+
writeHeaderVec := prometheus.NewHistogramVec(
52+
prometheus.HistogramOpts{
53+
Name: "write_header_duration_seconds",
54+
Help: "A histogram of time to first write latencies.",
55+
Buckets: prometheus.DefBuckets,
56+
ConstLabels: prometheus.Labels{"handler": "api"},
57+
},
58+
[]string{},
59+
)
60+
5161
responseSize := prometheus.NewHistogramVec(
5262
prometheus.HistogramOpts{
5363
Name: "push_request_size_bytes",
@@ -61,12 +71,14 @@ func TestMiddlewareAPI(t *testing.T) {
6171
w.Write([]byte("OK"))
6272
})
6373

64-
reg.MustRegister(inFlightGauge, counter, histVec, responseSize)
74+
reg.MustRegister(inFlightGauge, counter, histVec, responseSize, writeHeaderVec)
6575

6676
chain := InstrumentHandlerInFlight(inFlightGauge,
6777
InstrumentHandlerCounter(counter,
6878
InstrumentHandlerDuration(histVec,
69-
InstrumentHandlerResponseSize(responseSize, handler),
79+
InstrumentHandlerTimeToWriteHeader(writeHeaderVec,
80+
InstrumentHandlerResponseSize(responseSize, handler),
81+
),
7082
),
7183
),
7284
)

0 commit comments

Comments
 (0)