Skip to content

Commit 791a46c

Browse files
authored
Merge pull request #15 from slok/inflight
Add requests inflights metric
2 parents e99c9d6 + fb07cb5 commit 791a46c

File tree

12 files changed

+80
-13
lines changed

12 files changed

+80
-13
lines changed

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.x.x / 2019-xx-xx
2+
3+
* [FEATURE] Add inflight requests metric per handler.
4+
15
## 0.2.0 / 2019-03-22
26

37
* [FEATURE] Add metrics of HTTP response size in bytes.

Readme.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ If you are using a framework that isn't directly compatible with go's `http.Hand
1616
- [Recorder](#recorder)
1717
- [GroupedStatus](#groupedstatus)
1818
- [DisableMeasureSize](#disablemeasuresize)
19+
- [DisableMeasureInflight](#disablemeasureinflight)
1920
- [Custom handler ID](#custom-handler-id)
2021
- [Prometheus recorder options](#prometheus-recorder-options)
2122
- [Prefix](#prefix)
@@ -31,6 +32,7 @@ The metrics obtained with this middleware are the [most important ones][red] for
3132
- Records the duration of the requests(with: code, handler, method).
3233
- Records the count of the requests(with: code, handler, method).
3334
- Records the size of the responses(with: code, handler, method).
35+
- Records the number requests being handled concurrently at a given time a.k.a inflight requests (with: handler).
3436

3537
## Metrics recorder implementations
3638

@@ -141,6 +143,10 @@ Storing all the status codes could increase the cardinality of the metrics, usua
141143

142144
This setting will disable measuring the size of the responses. By default measuring the size is enabled.
143145

146+
#### DisableMeasureInflight
147+
148+
This settings will disable measuring the number of requests being handled concurrently by the handlers.
149+
144150
#### Custom handler ID
145151

146152
One of the options that you need to pass when wrapping the handler with the middleware is `handlerID`, this has 2 working ways.
@@ -174,10 +180,11 @@ The label names of the Prometheus metrics can be configured using `HandlerIDLabe
174180
```text
175181
pkg: github.com/slok/go-http-metrics/middleware
176182
177-
BenchmarkMiddlewareHandler/benchmark_with_default_settings.-4 1000000 1062 ns/op 256 B/op 6 allocs/op
178-
BenchmarkMiddlewareHandler/benchmark_disabling_measuring_size.-4 1000000 1101 ns/op 256 B/op 6 allocs/op
179-
BenchmarkMiddlewareHandler/benchmark_with_grouped_status_code.-4 1000000 1324 ns/op 256 B/op 7 allocs/op
180-
BenchmarkMiddlewareHandler/benchmark_with_predefined_handler_ID-4 1000000 1155 ns/op 256 B/op 6 allocs/op
183+
BenchmarkMiddlewareHandler/benchmark_with_default_settings.-4 1000000 1206 ns/op 256 B/op 6 allocs/op
184+
BenchmarkMiddlewareHandler/benchmark_disabling_measuring_size.-4 1000000 1198 ns/op 256 B/op 6 allocs/op
185+
BenchmarkMiddlewareHandler/benchmark_disabling_inflights.-4 1000000 1139 ns/op 256 B/op 6 allocs/op
186+
BenchmarkMiddlewareHandler/benchmark_with_grouped_status_code.-4 1000000 1534 ns/op 256 B/op 7 allocs/op
187+
BenchmarkMiddlewareHandler/benchmark_with_predefined_handler_ID-4 1000000 1258 ns/op 256 B/op 6 allocs/op
181188
```
182189

183190
[travis-image]: https://travis-ci.org/slok/go-http-metrics.svg?branch=master

examples/default/main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77
"os/signal"
88
"syscall"
9+
"time"
910

1011
"github.com/prometheus/client_golang/prometheus/promhttp"
1112
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
@@ -32,7 +33,10 @@ func main() {
3233

3334
// Create our server.
3435
mux := http.NewServeMux()
35-
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
36+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
37+
time.Sleep(200 * time.Millisecond)
38+
w.WriteHeader(http.StatusOK)
39+
})
3640
mux.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) })
3741
mux.HandleFunc("/test1/test2", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusAccepted) })
3842
mux.HandleFunc("/test1/test4", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNonAuthoritativeInfo) })

internal/mocks/metrics/Recorder.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

metrics/metrics.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ type Recorder interface {
1212
ObserveHTTPRequestDuration(id string, duration time.Duration, method, code string)
1313
// ObserveHTTPResponseSize measures the size of an HTTP response in bytes.
1414
ObserveHTTPResponseSize(id string, sizeBytes int64, method, code string)
15+
// AddInflightRequests increments and decrements the number of inflight request being
16+
// processed.
17+
AddInflightRequests(id string, quantity int)
1518
}
1619

1720
// Dummy is a dummy recorder.
@@ -21,3 +24,4 @@ type dummy struct{}
2124

2225
func (dummy) ObserveHTTPRequestDuration(id string, duration time.Duration, method, code string) {}
2326
func (dummy) ObserveHTTPResponseSize(id string, sizeBytes int64, method, code string) {}
27+
func (dummy) AddInflightRequests(id string, quantity int) {}

metrics/prometheus/prometheus.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func (c *Config) defaults() {
5858
type recorder struct {
5959
httpRequestDurHistogram *prometheus.HistogramVec
6060
httpResponseSizeHistogram *prometheus.HistogramVec
61+
httpRequestsInflight *prometheus.GaugeVec
6162

6263
cfg Config
6364
}
@@ -82,6 +83,12 @@ func NewRecorder(cfg Config) metrics.Recorder {
8283
Help: "The size of the HTTP responses.",
8384
Buckets: cfg.SizeBuckets,
8485
}, []string{cfg.HandlerIDLabel, cfg.MethodLabel, cfg.StatusCodeLabel}),
86+
httpRequestsInflight: prometheus.NewGaugeVec(prometheus.GaugeOpts{
87+
Namespace: cfg.Prefix,
88+
Subsystem: "http",
89+
Name: "requests_inflight",
90+
Help: "The number of inflight requests being handled at the same time.",
91+
}, []string{cfg.HandlerIDLabel}),
8592

8693
cfg: cfg,
8794
}
@@ -95,6 +102,7 @@ func (r recorder) registerMetrics() {
95102
r.cfg.Registry.MustRegister(
96103
r.httpRequestDurHistogram,
97104
r.httpResponseSizeHistogram,
105+
r.httpRequestsInflight,
98106
)
99107
}
100108

@@ -105,3 +113,7 @@ func (r recorder) ObserveHTTPRequestDuration(id string, duration time.Duration,
105113
func (r recorder) ObserveHTTPResponseSize(id string, sizeBytes int64, method, code string) {
106114
r.httpResponseSizeHistogram.WithLabelValues(id, method, code).Observe(float64(sizeBytes))
107115
}
116+
117+
func (r recorder) AddInflightRequests(id string, quantity int) {
118+
r.httpRequestsInflight.WithLabelValues(id).Add(float64(quantity))
119+
}

metrics/prometheus/prometheus_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ func TestPrometheusRecorder(t *testing.T) {
3333
r.ObserveHTTPResponseSize("test4", 529930, http.MethodPost, "500")
3434
r.ObserveHTTPResponseSize("test4", 231, http.MethodPost, "500")
3535
r.ObserveHTTPResponseSize("test4", 99999999, http.MethodPatch, "429")
36+
r.AddInflightRequests("test1", 5)
37+
r.AddInflightRequests("test1", -3)
38+
r.AddInflightRequests("test2", 9)
3639
},
3740
expMetrics: []string{
3841
`http_request_duration_seconds_bucket{code="200",handler="test1",method="GET",le="0.005"} 0`,
@@ -98,6 +101,9 @@ func TestPrometheusRecorder(t *testing.T) {
98101
`http_response_size_bytes_bucket{code="500",handler="test4",method="POST",le="1e+09"} 2`,
99102
`http_response_size_bytes_bucket{code="500",handler="test4",method="POST",le="+Inf"} 2`,
100103
`http_response_size_bytes_count{code="500",handler="test4",method="POST"} 2`,
104+
105+
`http_requests_inflight{handler="test1"} 2`,
106+
`http_requests_inflight{handler="test2"} 9`,
101107
},
102108
},
103109
{

middleware/gorestful/gorestful_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ func TestMiddlewareIntegration(t *testing.T) {
4747

4848
// Mocks.
4949
mr := &mmetrics.Recorder{}
50-
mr.On("ObserveHTTPRequestDuration", test.expHandlerID, mock.Anything, test.expMethod, test.expStatusCode)
51-
mr.On("ObserveHTTPResponseSize", test.expHandlerID, mock.Anything, test.expMethod, test.expStatusCode)
50+
mr.On("ObserveHTTPRequestDuration", test.expHandlerID, mock.Anything, test.expMethod, test.expStatusCode).Once()
51+
mr.On("ObserveHTTPResponseSize", test.expHandlerID, mock.Anything, test.expMethod, test.expStatusCode).Once()
52+
mr.On("AddInflightRequests", test.expHandlerID, 1).Once()
53+
mr.On("AddInflightRequests", test.expHandlerID, -1).Once()
5254

5355
// Create our instance with the middleware.
5456
mdlw := middleware.New(middleware.Config{Recorder: mr})

middleware/httprouter/httprouter_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ func TestMiddlewareIntegration(t *testing.T) {
4747

4848
// Mocks.
4949
mr := &mmetrics.Recorder{}
50-
mr.On("ObserveHTTPRequestDuration", test.expHandlerID, mock.Anything, test.expMethod, test.expStatusCode)
51-
mr.On("ObserveHTTPResponseSize", test.expHandlerID, mock.Anything, test.expMethod, test.expStatusCode)
50+
mr.On("ObserveHTTPRequestDuration", test.expHandlerID, mock.Anything, test.expMethod, test.expStatusCode).Once()
51+
mr.On("ObserveHTTPResponseSize", test.expHandlerID, mock.Anything, test.expMethod, test.expStatusCode).Once()
52+
mr.On("AddInflightRequests", test.expHandlerID, 1).Once()
53+
mr.On("AddInflightRequests", test.expHandlerID, -1).Once()
5254

5355
// Create our instance with the middleware.
5456
mdlw := middleware.New(middleware.Config{Recorder: mr})

middleware/middleware.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ type Config struct {
2626
// DisableMeasureSize will disable the recording metrics about the response size,
2727
// by default measuring size is enabled (`DisableMeasureSize` is false).
2828
DisableMeasureSize bool
29+
30+
// DisableMeasureInflight will disable the recording metrics about the inflight requests number,
31+
// by default measuring inflights is enabled (`DisableMeasureInflight` is false).
32+
DisableMeasureInflight bool
2933
}
3034

3135
func (c *Config) validate() {
@@ -81,6 +85,12 @@ func (m *middleware) Handler(handlerID string, h http.Handler) http.Handler {
8185
hid = r.URL.Path
8286
}
8387

88+
// Measure inflights if required.
89+
if !m.cfg.DisableMeasureInflight {
90+
m.cfg.Recorder.AddInflightRequests(hid, 1)
91+
defer m.cfg.Recorder.AddInflightRequests(hid, -1)
92+
}
93+
8494
// Start the timer and when finishing measure the duration.
8595
start := time.Now()
8696
defer func() {

0 commit comments

Comments
 (0)