Skip to content

Improve promhttp middleware #302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 11, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion prometheus/promhttp/instrument_client_1_8_test.go
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@ func TestClientMiddlewareAPI(t *testing.T) {
client := http.DefaultClient
client.Timeout = 1 * time.Second

reg := prometheus.NewRegistry()

inFlightGauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "client_in_flight_requests",
Help: "A gauge of in-flight requests for the wrapped client.",
@@ -68,7 +70,7 @@ func TestClientMiddlewareAPI(t *testing.T) {
[]string{"method"},
)

prometheus.MustRegister(counter, tlsLatencyVec, dnsLatencyVec, histVec, inFlightGauge)
reg.MustRegister(counter, tlsLatencyVec, dnsLatencyVec, histVec, inFlightGauge)

trace := &InstrumentTrace{
DNSStart: func(t float64) {
42 changes: 27 additions & 15 deletions prometheus/promhttp/instrument_server.go
Original file line number Diff line number Diff line change
@@ -27,6 +27,9 @@ import (
"github.com/prometheus/client_golang/prometheus"
)

// magicString is used for the hacky label test in checkLabels. Remove once fixed.
const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa"

// InstrumentHandlerInFlight is a middleware that wraps the provided
// http.Handler. It sets the provided prometheus.Gauge to the number of
// requests currently handled by the wrapped http.Handler.
@@ -191,37 +194,46 @@ func checkLabels(c prometheus.Collector) (code bool, method bool) {

if _, err := prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0); err == nil {
return
} else if m, err := prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, ""); err == nil {
}
if m, err := prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, magicString); err == nil {
if err := m.Write(&pm); err != nil {
panic("error checking metric for labels")
}

name := *pm.Label[0].Name
if name == "code" {
code = true
} else if name == "method" {
method = true
} else {
panic("metric partitioned with non-supported labels")
for _, label := range pm.Label {
name, value := label.GetName(), label.GetValue()
if value != magicString {
continue
}
switch name {
case "code":
code = true
case "method":
method = true
default:
panic("metric partitioned with non-supported labels")
}
return
}
return
} else if m, err := prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, "", ""); err == nil {
panic("previously set label not found – this must never happen")
}
if m, err := prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, magicString, magicString); err == nil {
if err := m.Write(&pm); err != nil {
panic("error checking metric for labels")
}

for _, label := range pm.Label {
if *label.Name == "code" || *label.Name == "method" {
name, value := label.GetName(), label.GetValue()
if value != magicString {
continue
}
if name == "code" || name == "method" {
continue
}
panic("metric partitioned with non-supported labels")
}

code = true
method = true
return
}

panic("metric partitioned with non-supported labels")
}

45 changes: 22 additions & 23 deletions prometheus/promhttp/instrument_server_test.go
Original file line number Diff line number Diff line change
@@ -23,6 +23,8 @@ import (
)

func TestMiddlewareAPI(t *testing.T) {
reg := prometheus.NewRegistry()

inFlightGauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "in_flight_requests",
Help: "A gauge of requests currently being served by the wrapped handler.",
@@ -38,9 +40,10 @@ func TestMiddlewareAPI(t *testing.T) {

histVec := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "response_duration_seconds",
Help: "A histogram of request latencies.",
Buckets: prometheus.DefBuckets,
Name: "response_duration_seconds",
Help: "A histogram of request latencies.",
Buckets: prometheus.DefBuckets,
ConstLabels: prometheus.Labels{"handler": "api"},
},
[]string{"method"},
)
@@ -58,7 +61,7 @@ func TestMiddlewareAPI(t *testing.T) {
w.Write([]byte("OK"))
})

prometheus.MustRegister(inFlightGauge, counter, histVec, responseSize)
reg.MustRegister(inFlightGauge, counter, histVec, responseSize)

chain := InstrumentHandlerInFlight(inFlightGauge,
InstrumentHandlerCounter(counter,
@@ -87,29 +90,25 @@ func ExampleInstrumentHandlerDuration() {
[]string{"code", "method"},
)

// pushVec is partitioned by the HTTP method and uses custom buckets based on
// the expected request duration. It uses ConstLabels to set a handler label
// marking pushVec as tracking the durations for pushes.
// pushVec and pullVec are partitioned by the HTTP method and use custom
// buckets based on the expected request duration. ConstLabels are used
// to set a handler label to mark pushVec as tracking the durations for
// pushes and pullVec as tracking the durations for pulls. Note that
// Name, Help, and Buckets need to be the same for consistency, so we
// use the same HistogramOpts after just modifying the ConstLabels.
histogramOpts := prometheus.HistogramOpts{
Name: "request_duration_seconds",
Help: "A histogram of latencies for requests.",
Buckets: []float64{.25, .5, 1, 2.5, 5, 10},
ConstLabels: prometheus.Labels{"handler": "push"},
}
pushVec := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "request_duration_seconds",
Help: "A histogram of latencies for requests to the push handler.",
Buckets: []float64{.25, .5, 1, 2.5, 5, 10},
ConstLabels: prometheus.Labels{"handler": "push"},
},
histogramOpts,
[]string{"method"},
)

// pullVec is also partitioned by the HTTP method but uses custom buckets
// different from those for pushVec. It also has a different value for the
// constant "handler" label.
histogramOpts.ConstLabels = prometheus.Labels{"handler": "pull"}
pullVec := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "request_duration_seconds",
Help: "A histogram of latencies for requests to the pull handler.",
Buckets: []float64{.005, .01, .025, .05},
ConstLabels: prometheus.Labels{"handler": "pull"},
},
histogramOpts,
[]string{"method"},
)