Add a configurable version of InstrumentHandler and InstrumentHandlerFunc.
Also, remove quotes from the Content-type header. It's not illegal to have quotes there, but they are not needed, and at other places, we are not using them. So fewer characters and more consistency. Change-Id: If7a78bde85154163e4426daec493d973213e83e9
This commit is contained in:
parent
23e5e5fefd
commit
96297bcbae
|
@ -20,45 +20,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var instLabels = []string{"method", "code"}
|
||||||
instLabels = []string{"handler", "method", "code"}
|
|
||||||
|
|
||||||
reqCnt = NewCounterVec(
|
|
||||||
CounterOpts{
|
|
||||||
Subsystem: "http",
|
|
||||||
Name: "requests_total",
|
|
||||||
Help: "Total number of HTTP requests made.",
|
|
||||||
},
|
|
||||||
instLabels,
|
|
||||||
)
|
|
||||||
|
|
||||||
reqDur = NewSummaryVec(
|
|
||||||
SummaryOpts{
|
|
||||||
Subsystem: "http",
|
|
||||||
Name: "request_duration_microseconds",
|
|
||||||
Help: "The HTTP request latencies in microseconds.",
|
|
||||||
},
|
|
||||||
instLabels,
|
|
||||||
)
|
|
||||||
|
|
||||||
reqSz = NewSummaryVec(
|
|
||||||
SummaryOpts{
|
|
||||||
Subsystem: "http",
|
|
||||||
Name: "request_size_bytes",
|
|
||||||
Help: "The HTTP request sizes in bytes.",
|
|
||||||
},
|
|
||||||
instLabels,
|
|
||||||
)
|
|
||||||
|
|
||||||
resSz = NewSummaryVec(
|
|
||||||
SummaryOpts{
|
|
||||||
Subsystem: "http",
|
|
||||||
Name: "response_size_bytes",
|
|
||||||
Help: "The HTTP response sizes in bytes.",
|
|
||||||
},
|
|
||||||
instLabels,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
type nower interface {
|
type nower interface {
|
||||||
Now() time.Time
|
Now() time.Time
|
||||||
|
@ -103,6 +65,71 @@ func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFun
|
||||||
// (SummaryVec). Each has three labels: handler, method, code. The value of the
|
// (SummaryVec). Each has three labels: handler, method, code. The value of the
|
||||||
// handler label is set by the handlerName parameter of this function.
|
// handler label is set by the handlerName parameter of this function.
|
||||||
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
||||||
|
return InstrumentHandlerFuncWithOpts(
|
||||||
|
SummaryOpts{
|
||||||
|
Subsystem: "http",
|
||||||
|
ConstLabels: Labels{"handler": handlerName},
|
||||||
|
},
|
||||||
|
handlerFunc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerWithOpts works like InstrumentHandler but provides more
|
||||||
|
// flexibility (at the cost of a more complex call syntax). As
|
||||||
|
// InstrumentHandler, this function registers four metric vector collectors, but
|
||||||
|
// it uses the provided SummaryOpts to create them. However, the fields "Name"
|
||||||
|
// and "Help" in the SummaryOpts are ignored. "Name" is replaced by
|
||||||
|
// "requests_total", "request_duration_microseconds", "request_size_bytes", and
|
||||||
|
// "response_size_bytes", respectively. "Help" is replaced by an appropriate
|
||||||
|
// help string. The names of the variable labels of the vector collectors are
|
||||||
|
// "method" (get, post, etc.), and "code" (HTTP status code).
|
||||||
|
//
|
||||||
|
// If InstrumentHandlerWithOpts is called as follows, it mimics exactly the
|
||||||
|
// behavior of InstrumentHandler:
|
||||||
|
//
|
||||||
|
// prometheus.InstrumentHandlerWithOpts(
|
||||||
|
// prometheus.SummaryOpts{
|
||||||
|
// Subsystem: "http",
|
||||||
|
// ConstLabels: prometheus.Labels{"handler": handlerName},
|
||||||
|
// },
|
||||||
|
// handler,
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it
|
||||||
|
// cannot use SummaryOpts. Instead, a CounterOpts struct is created internally,
|
||||||
|
// and all its fields are set to the equally named fields in the provided
|
||||||
|
// SummaryOpts.
|
||||||
|
func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
|
||||||
|
return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc but provides
|
||||||
|
// more flexibility (at the cost of a more complex call syntax). See
|
||||||
|
// InstrumentHandlerWithOpts for details how the provided SummaryOpts are used.
|
||||||
|
func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
||||||
|
reqCnt := NewCounterVec(
|
||||||
|
CounterOpts{
|
||||||
|
Namespace: opts.Namespace,
|
||||||
|
Subsystem: opts.Subsystem,
|
||||||
|
Name: "requests_total",
|
||||||
|
Help: "Total number of HTTP requests made.",
|
||||||
|
ConstLabels: opts.ConstLabels,
|
||||||
|
},
|
||||||
|
instLabels,
|
||||||
|
)
|
||||||
|
|
||||||
|
opts.Name = "request_duration_microseconds"
|
||||||
|
opts.Help = "The HTTP request latencies in microseconds."
|
||||||
|
reqDur := NewSummaryVec(opts, instLabels)
|
||||||
|
|
||||||
|
opts.Name = "request_size_bytes"
|
||||||
|
opts.Help = "The HTTP request sizes in bytes."
|
||||||
|
reqSz := NewSummaryVec(opts, instLabels)
|
||||||
|
|
||||||
|
opts.Name = "response_size_bytes"
|
||||||
|
opts.Help = "The HTTP response sizes in bytes."
|
||||||
|
resSz := NewSummaryVec(opts, instLabels)
|
||||||
|
|
||||||
regReqCnt := MustRegisterOrGet(reqCnt).(*CounterVec)
|
regReqCnt := MustRegisterOrGet(reqCnt).(*CounterVec)
|
||||||
regReqDur := MustRegisterOrGet(reqDur).(*SummaryVec)
|
regReqDur := MustRegisterOrGet(reqDur).(*SummaryVec)
|
||||||
regReqSz := MustRegisterOrGet(reqSz).(*SummaryVec)
|
regReqSz := MustRegisterOrGet(reqSz).(*SummaryVec)
|
||||||
|
@ -120,10 +147,10 @@ func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWri
|
||||||
|
|
||||||
method := sanitizeMethod(r.Method)
|
method := sanitizeMethod(r.Method)
|
||||||
code := sanitizeCode(delegate.status)
|
code := sanitizeCode(delegate.status)
|
||||||
regReqCnt.WithLabelValues(handlerName, method, code).Inc()
|
regReqCnt.WithLabelValues(method, code).Inc()
|
||||||
regReqDur.WithLabelValues(handlerName, method, code).Observe(elapsed)
|
regReqDur.WithLabelValues(method, code).Observe(elapsed)
|
||||||
regResSz.WithLabelValues(handlerName, method, code).Observe(float64(delegate.written))
|
regResSz.WithLabelValues(method, code).Observe(float64(delegate.written))
|
||||||
regReqSz.WithLabelValues(handlerName, method, code).Observe(float64(<-out))
|
regReqSz.WithLabelValues(method, code).Observe(float64(<-out))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,15 +37,43 @@ func TestInstrumentHandler(t *testing.T) {
|
||||||
instant := time.Now()
|
instant := time.Now()
|
||||||
end := instant.Add(30 * time.Second)
|
end := instant.Add(30 * time.Second)
|
||||||
now = nowSeries(instant, end)
|
now = nowSeries(instant, end)
|
||||||
|
respBody := respBody("Howdy there!")
|
||||||
|
|
||||||
|
hndlr := InstrumentHandler("test-handler", respBody)
|
||||||
|
|
||||||
|
opts := SummaryOpts{
|
||||||
|
Subsystem: "http",
|
||||||
|
ConstLabels: Labels{"handler": "test-handler"},
|
||||||
|
}
|
||||||
|
|
||||||
|
reqCnt := MustRegisterOrGet(NewCounterVec(
|
||||||
|
CounterOpts{
|
||||||
|
Namespace: opts.Namespace,
|
||||||
|
Subsystem: opts.Subsystem,
|
||||||
|
Name: "requests_total",
|
||||||
|
Help: "Total number of HTTP requests made.",
|
||||||
|
ConstLabels: opts.ConstLabels,
|
||||||
|
},
|
||||||
|
instLabels,
|
||||||
|
)).(*CounterVec)
|
||||||
|
|
||||||
|
opts.Name = "request_duration_microseconds"
|
||||||
|
opts.Help = "The HTTP request latencies in microseconds."
|
||||||
|
reqDur := MustRegisterOrGet(NewSummaryVec(opts, instLabels)).(*SummaryVec)
|
||||||
|
|
||||||
|
opts.Name = "request_size_bytes"
|
||||||
|
opts.Help = "The HTTP request sizes in bytes."
|
||||||
|
reqSz := MustRegisterOrGet(NewSummaryVec(opts, instLabels)).(*SummaryVec)
|
||||||
|
|
||||||
|
opts.Name = "response_size_bytes"
|
||||||
|
opts.Help = "The HTTP response sizes in bytes."
|
||||||
|
resSz := MustRegisterOrGet(NewSummaryVec(opts, instLabels)).(*SummaryVec)
|
||||||
|
|
||||||
reqCnt.Reset()
|
reqCnt.Reset()
|
||||||
reqDur.Reset()
|
reqDur.Reset()
|
||||||
reqSz.Reset()
|
reqSz.Reset()
|
||||||
resSz.Reset()
|
resSz.Reset()
|
||||||
|
|
||||||
respBody := respBody("Howdy there!")
|
|
||||||
|
|
||||||
hndlr := InstrumentHandler("test-handler", respBody)
|
|
||||||
|
|
||||||
resp := httptest.NewRecorder()
|
resp := httptest.NewRecorder()
|
||||||
req := &http.Request{
|
req := &http.Request{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
|
@ -63,7 +91,7 @@ func TestInstrumentHandler(t *testing.T) {
|
||||||
if want, got := 1, len(reqDur.children); want != got {
|
if want, got := 1, len(reqDur.children); want != got {
|
||||||
t.Errorf("want %d children in reqDur, got %d", want, got)
|
t.Errorf("want %d children in reqDur, got %d", want, got)
|
||||||
}
|
}
|
||||||
sum, err := reqDur.GetMetricWithLabelValues("test-handler", "get", "418")
|
sum, err := reqDur.GetMetricWithLabelValues("get", "418")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -86,7 +114,7 @@ func TestInstrumentHandler(t *testing.T) {
|
||||||
if want, got := 1, len(reqCnt.children); want != got {
|
if want, got := 1, len(reqCnt.children); want != got {
|
||||||
t.Errorf("want %d children in reqCnt, got %d", want, got)
|
t.Errorf("want %d children in reqCnt, got %d", want, got)
|
||||||
}
|
}
|
||||||
cnt, err := reqCnt.GetMetricWithLabelValues("test-handler", "get", "418")
|
cnt, err := reqCnt.GetMetricWithLabelValues("get", "418")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,17 +55,17 @@ const (
|
||||||
|
|
||||||
// DelimitedTelemetryContentType is the content type set on telemetry
|
// DelimitedTelemetryContentType is the content type set on telemetry
|
||||||
// data responses in delimited protobuf format.
|
// data responses in delimited protobuf format.
|
||||||
DelimitedTelemetryContentType = `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`
|
DelimitedTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`
|
||||||
// TextTelemetryContentType is the content type set on telemetry data
|
// TextTelemetryContentType is the content type set on telemetry data
|
||||||
// responses in text format.
|
// responses in text format.
|
||||||
TextTelemetryContentType = `text/plain; version=` + APIVersion
|
TextTelemetryContentType = `text/plain; version=` + APIVersion
|
||||||
// ProtoTextTelemetryContentType is the content type set on telemetry
|
// ProtoTextTelemetryContentType is the content type set on telemetry
|
||||||
// data responses in protobuf text format. (Only used for debugging.)
|
// data responses in protobuf text format. (Only used for debugging.)
|
||||||
ProtoTextTelemetryContentType = `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="text"`
|
ProtoTextTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=text`
|
||||||
// ProtoCompactTextTelemetryContentType is the content type set on
|
// ProtoCompactTextTelemetryContentType is the content type set on
|
||||||
// telemetry data responses in protobuf compact text format. (Only used
|
// telemetry data responses in protobuf compact text format. (Only used
|
||||||
// for debugging.)
|
// for debugging.)
|
||||||
ProtoCompactTextTelemetryContentType = `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="compact-text"`
|
ProtoCompactTextTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`
|
||||||
|
|
||||||
// Constants for object pools.
|
// Constants for object pools.
|
||||||
numBufs = 4
|
numBufs = 4
|
||||||
|
|
|
@ -266,7 +266,7 @@ metric: <
|
||||||
},
|
},
|
||||||
out: output{
|
out: output{
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`,
|
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||||
},
|
},
|
||||||
body: []byte{},
|
body: []byte{},
|
||||||
},
|
},
|
||||||
|
@ -289,7 +289,7 @@ metric: <
|
||||||
},
|
},
|
||||||
out: output{
|
out: output{
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`,
|
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||||
},
|
},
|
||||||
body: expectedMetricFamilyAsBytes,
|
body: expectedMetricFamilyAsBytes,
|
||||||
},
|
},
|
||||||
|
@ -313,7 +313,7 @@ metric: <
|
||||||
},
|
},
|
||||||
out: output{
|
out: output{
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`,
|
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||||
},
|
},
|
||||||
body: externalMetricFamilyAsBytes,
|
body: externalMetricFamilyAsBytes,
|
||||||
},
|
},
|
||||||
|
@ -325,7 +325,7 @@ metric: <
|
||||||
},
|
},
|
||||||
out: output{
|
out: output{
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`,
|
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||||
},
|
},
|
||||||
body: bytes.Join(
|
body: bytes.Join(
|
||||||
[][]byte{
|
[][]byte{
|
||||||
|
@ -386,7 +386,7 @@ metric: <
|
||||||
},
|
},
|
||||||
out: output{
|
out: output{
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`,
|
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||||
},
|
},
|
||||||
body: bytes.Join(
|
body: bytes.Join(
|
||||||
[][]byte{
|
[][]byte{
|
||||||
|
@ -405,7 +405,7 @@ metric: <
|
||||||
},
|
},
|
||||||
out: output{
|
out: output{
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="text"`,
|
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=text`,
|
||||||
},
|
},
|
||||||
body: bytes.Join(
|
body: bytes.Join(
|
||||||
[][]byte{
|
[][]byte{
|
||||||
|
@ -424,7 +424,7 @@ metric: <
|
||||||
},
|
},
|
||||||
out: output{
|
out: output{
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="compact-text"`,
|
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`,
|
||||||
},
|
},
|
||||||
body: bytes.Join(
|
body: bytes.Join(
|
||||||
[][]byte{
|
[][]byte{
|
||||||
|
|
Loading…
Reference in New Issue