From 96297bcbae71909b2d289d465fae1620561bb5e1 Mon Sep 17 00:00:00 2001 From: Bjoern Rabenstein Date: Tue, 15 Jul 2014 15:34:52 +0200 Subject: [PATCH] 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 --- prometheus/http.go | 113 ++++++++++++++++++++++-------------- prometheus/http_test.go | 40 +++++++++++-- prometheus/registry.go | 6 +- prometheus/registry_test.go | 14 ++--- 4 files changed, 114 insertions(+), 59 deletions(-) diff --git a/prometheus/http.go b/prometheus/http.go index e927fe5..1b3b592 100644 --- a/prometheus/http.go +++ b/prometheus/http.go @@ -20,45 +20,7 @@ import ( "time" ) -var ( - 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, - ) -) +var instLabels = []string{"method", "code"} type nower interface { 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 // handler label is set by the handlerName parameter of this function. 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) regReqDur := MustRegisterOrGet(reqDur).(*SummaryVec) regReqSz := MustRegisterOrGet(reqSz).(*SummaryVec) @@ -120,10 +147,10 @@ func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWri method := sanitizeMethod(r.Method) code := sanitizeCode(delegate.status) - regReqCnt.WithLabelValues(handlerName, method, code).Inc() - regReqDur.WithLabelValues(handlerName, method, code).Observe(elapsed) - regResSz.WithLabelValues(handlerName, method, code).Observe(float64(delegate.written)) - regReqSz.WithLabelValues(handlerName, method, code).Observe(float64(<-out)) + regReqCnt.WithLabelValues(method, code).Inc() + regReqDur.WithLabelValues(method, code).Observe(elapsed) + regResSz.WithLabelValues(method, code).Observe(float64(delegate.written)) + regReqSz.WithLabelValues(method, code).Observe(float64(<-out)) }) } diff --git a/prometheus/http_test.go b/prometheus/http_test.go index 5c106b9..da3b75d 100644 --- a/prometheus/http_test.go +++ b/prometheus/http_test.go @@ -37,15 +37,43 @@ func TestInstrumentHandler(t *testing.T) { instant := time.Now() end := instant.Add(30 * time.Second) 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() reqDur.Reset() reqSz.Reset() resSz.Reset() - respBody := respBody("Howdy there!") - - hndlr := InstrumentHandler("test-handler", respBody) - resp := httptest.NewRecorder() req := &http.Request{ Method: "GET", @@ -63,7 +91,7 @@ func TestInstrumentHandler(t *testing.T) { if want, got := 1, len(reqDur.children); 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 { t.Fatal(err) } @@ -86,7 +114,7 @@ func TestInstrumentHandler(t *testing.T) { if want, got := 1, len(reqCnt.children); 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 { t.Fatal(err) } diff --git a/prometheus/registry.go b/prometheus/registry.go index 3ef3fcc..a161211 100644 --- a/prometheus/registry.go +++ b/prometheus/registry.go @@ -55,17 +55,17 @@ const ( // DelimitedTelemetryContentType is the content type set on telemetry // 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 // responses in text format. TextTelemetryContentType = `text/plain; version=` + APIVersion // ProtoTextTelemetryContentType is the content type set on telemetry // 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 // telemetry data responses in protobuf compact text format. (Only used // 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. numBufs = 4 diff --git a/prometheus/registry_test.go b/prometheus/registry_test.go index 9d5d4ce..a0e9a80 100644 --- a/prometheus/registry_test.go +++ b/prometheus/registry_test.go @@ -266,7 +266,7 @@ metric: < }, out: output{ 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{}, }, @@ -289,7 +289,7 @@ metric: < }, out: output{ 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, }, @@ -313,7 +313,7 @@ metric: < }, out: output{ 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, }, @@ -325,7 +325,7 @@ metric: < }, out: output{ 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( [][]byte{ @@ -386,7 +386,7 @@ metric: < }, out: output{ 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( [][]byte{ @@ -405,7 +405,7 @@ metric: < }, out: output{ 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( [][]byte{ @@ -424,7 +424,7 @@ metric: < }, out: output{ 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( [][]byte{