Skip to content
Open
Show file tree
Hide file tree
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
12 changes: 6 additions & 6 deletions prometheus/promhttp/instrument_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou
resp, err := next.RoundTrip(r)
if err == nil {
l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)
for label, resolve := range rtOpts.extraLabelsFromCtx {
l[label] = resolve(resp.Request.Context())
for label, resolve := range rtOpts.extraLabelsFromRequest {
l[label] = resolve(resp.Request)
}
addWithExemplar(counter.With(l), 1, rtOpts.getExemplarFn(r.Context()))
addWithExemplar(counter.With(l), 1, rtOpts.getExemplarFn(r))
}
return resp, err
}
Expand Down Expand Up @@ -119,10 +119,10 @@ func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundT
resp, err := next.RoundTrip(r)
if err == nil {
l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)
for label, resolve := range rtOpts.extraLabelsFromCtx {
l[label] = resolve(resp.Request.Context())
for label, resolve := range rtOpts.extraLabelsFromRequest {
l[label] = resolve(resp.Request)
}
observeWithExemplar(obs.With(l), time.Since(start).Seconds(), rtOpts.getExemplarFn(r.Context()))
observeWithExemplar(obs.With(l), time.Since(start).Seconds(), rtOpts.getExemplarFn(r))
}
return resp, err
}
Expand Down
48 changes: 24 additions & 24 deletions prometheus/promhttp/instrument_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,21 +97,21 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op
next.ServeHTTP(d, r)

l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)
for label, resolve := range hOpts.extraLabelsFromCtx {
l[label] = resolve(r.Context())
for label, resolve := range hOpts.extraLabelsFromRequest {
l[label] = resolve(r)
}
observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context()))
observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r))
}
}

return func(w http.ResponseWriter, r *http.Request) {
now := time.Now()
next.ServeHTTP(w, r)
l := labels(code, method, r.Method, 0, hOpts.extraMethods...)
for label, resolve := range hOpts.extraLabelsFromCtx {
l[label] = resolve(r.Context())
for label, resolve := range hOpts.extraLabelsFromRequest {
l[label] = resolve(r)
}
observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context()))
observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r))
}
}

Expand Down Expand Up @@ -147,21 +147,21 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler,
next.ServeHTTP(d, r)

l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)
for label, resolve := range hOpts.extraLabelsFromCtx {
l[label] = resolve(r.Context())
for label, resolve := range hOpts.extraLabelsFromRequest {
l[label] = resolve(r)
}
addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context()))
addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r))
}
}

return func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)

l := labels(code, method, r.Method, 0, hOpts.extraMethods...)
for label, resolve := range hOpts.extraLabelsFromCtx {
l[label] = resolve(r.Context())
for label, resolve := range hOpts.extraLabelsFromRequest {
l[label] = resolve(r)
}
addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context()))
addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r))
}
}

Expand Down Expand Up @@ -200,10 +200,10 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha
now := time.Now()
d := newDelegator(w, func(status int) {
l := labels(code, method, r.Method, status, hOpts.extraMethods...)
for label, resolve := range hOpts.extraLabelsFromCtx {
l[label] = resolve(r.Context())
for label, resolve := range hOpts.extraLabelsFromRequest {
l[label] = resolve(r)
}
observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context()))
observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r))
})
next.ServeHTTP(d, r)
}
Expand Down Expand Up @@ -244,10 +244,10 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler,
size := computeApproximateRequestSize(r)

l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)
for label, resolve := range hOpts.extraLabelsFromCtx {
l[label] = resolve(r.Context())
for label, resolve := range hOpts.extraLabelsFromRequest {
l[label] = resolve(r)
}
observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context()))
observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r))
}
}

Expand All @@ -256,10 +256,10 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler,
size := computeApproximateRequestSize(r)

l := labels(code, method, r.Method, 0, hOpts.extraMethods...)
for label, resolve := range hOpts.extraLabelsFromCtx {
l[label] = resolve(r.Context())
for label, resolve := range hOpts.extraLabelsFromRequest {
l[label] = resolve(r)
}
observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context()))
observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r))
}
}

Expand Down Expand Up @@ -296,10 +296,10 @@ func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler
next.ServeHTTP(d, r)

l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...)
for label, resolve := range hOpts.extraLabelsFromCtx {
l[label] = resolve(r.Context())
for label, resolve := range hOpts.extraLabelsFromRequest {
l[label] = resolve(r)
}
observeWithExemplar(obs.With(l), float64(d.Written()), hOpts.getExemplarFn(r.Context()))
observeWithExemplar(obs.With(l), float64(d.Written()), hOpts.getExemplarFn(r))
})
}

Expand Down
40 changes: 32 additions & 8 deletions prometheus/promhttp/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package promhttp

import (
"context"
"net/http"

"github.com/prometheus/client_golang/prometheus"
)
Expand All @@ -24,28 +25,31 @@ type Option interface {
apply(*options)
}

// LabelValueFromRequest is used to compute the label value from request.
type LabelValueFromRequest func(request *http.Request) string

// LabelValueFromCtx are used to compute the label value from request context.
// Context can be filled with values from request through middleware.
type LabelValueFromCtx func(ctx context.Context) string

// options store options for both a handler or round tripper.
type options struct {
extraMethods []string
getExemplarFn func(requestCtx context.Context) prometheus.Labels
extraLabelsFromCtx map[string]LabelValueFromCtx
extraMethods []string
getExemplarFn func(req *http.Request) prometheus.Labels
extraLabelsFromRequest map[string]LabelValueFromRequest
}

func defaultOptions() *options {
return &options{
getExemplarFn: func(ctx context.Context) prometheus.Labels { return nil },
extraLabelsFromCtx: map[string]LabelValueFromCtx{},
getExemplarFn: func(req *http.Request) prometheus.Labels { return nil },
extraLabelsFromRequest: map[string]LabelValueFromRequest{},
}
}

func (o *options) emptyDynamicLabels() prometheus.Labels {
labels := prometheus.Labels{}

for label := range o.extraLabelsFromCtx {
for label := range o.extraLabelsFromRequest {
labels[label] = ""
}

Expand All @@ -66,19 +70,39 @@ func WithExtraMethods(methods ...string) Option {
})
}

// WithExemplarFromRequest allows to inject function that will get exemplar from request that will be put to counter and histogram metrics.
// If the function returns nil labels or the metric does not support exemplars, no exemplar will be added (noop), but
// metric will continue to observe/increment.
func WithExemplarFromRequest(getExemplarFn func(req *http.Request) prometheus.Labels) Option {
return optionApplyFunc(func(o *options) {
o.getExemplarFn = getExemplarFn
})
}

// WithExemplarFromContext allows to inject function that will get exemplar from context that will be put to counter and histogram metrics.
// If the function returns nil labels or the metric does not support exemplars, no exemplar will be added (noop), but
// metric will continue to observe/increment.
func WithExemplarFromContext(getExemplarFn func(requestCtx context.Context) prometheus.Labels) Option {
return optionApplyFunc(func(o *options) {
o.getExemplarFn = getExemplarFn
o.getExemplarFn = func(req *http.Request) prometheus.Labels {
return getExemplarFn(req.Context())
}
})
}

// WithLabelFromRequest registers a label for dynamic resolution with access to the request.
func WithLabelFromRequest(name string, valueFn LabelValueFromRequest) Option {
return optionApplyFunc(func(o *options) {
o.extraLabelsFromRequest[name] = valueFn
})
}

// WithLabelFromCtx registers a label for dynamic resolution with access to context.
// See the example for ExampleInstrumentHandlerWithLabelResolver for example usage
func WithLabelFromCtx(name string, valueFn LabelValueFromCtx) Option {
return optionApplyFunc(func(o *options) {
o.extraLabelsFromCtx[name] = valueFn
o.extraLabelsFromRequest[name] = func(req *http.Request) string {
return valueFn(req.Context())
}
})
}
Loading