From aa8191ab24a0bbcf4ed10f5d86d9fe9990700e10 Mon Sep 17 00:00:00 2001 From: prombot Date: Fri, 23 Oct 2020 00:08:36 +0000 Subject: [PATCH 1/9] Update common Prometheus files Signed-off-by: prombot --- Makefile.common | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.common b/Makefile.common index 3f3d02cba..4e325f582 100644 --- a/Makefile.common +++ b/Makefile.common @@ -245,10 +245,12 @@ common-docker-publish: $(PUBLISH_DOCKER_ARCHS) $(PUBLISH_DOCKER_ARCHS): common-docker-publish-%: docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" +DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION))) .PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS) common-docker-tag-latest: $(TAG_DOCKER_ARCHS) $(TAG_DOCKER_ARCHS): common-docker-tag-latest-%: docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest" + docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)" .PHONY: common-docker-manifest common-docker-manifest: From 469ec2747b6f7c53600f2b6df40cf3e46e582eb1 Mon Sep 17 00:00:00 2001 From: sbookworm Date: Sat, 10 Oct 2020 16:37:17 +0800 Subject: [PATCH 2/9] add the NewPidFileFn to helper Signed-off-by: sbookworm --- prometheus/process_collector.go | 21 +++++++++ prometheus/process_collector_test.go | 64 ++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/prometheus/process_collector.go b/prometheus/process_collector.go index 9b8097942..c46702d60 100644 --- a/prometheus/process_collector.go +++ b/prometheus/process_collector.go @@ -15,7 +15,11 @@ package prometheus import ( "errors" + "fmt" + "io/ioutil" "os" + "strconv" + "strings" ) type processCollector struct { @@ -149,3 +153,20 @@ func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) } ch <- NewInvalidMetric(desc, err) } + +// NewPidFileFn returns a function that retrieves a pid from the specified file. +// It is meant to be used for the PidFn field in ProcessCollectorOpts. +func NewPidFileFn(pidFilePath string) func() (int, error) { + return func() (int, error) { + content, err := ioutil.ReadFile(pidFilePath) + if err != nil { + return 0, fmt.Errorf("can't read pid file %q: %+v", pidFilePath, err) + } + pid, err := strconv.Atoi(strings.TrimSpace(string(content))) + if err != nil { + return 0, fmt.Errorf("can't parse pid file %q: %+v", pidFilePath, err) + } + + return pid, nil + } +} diff --git a/prometheus/process_collector_test.go b/prometheus/process_collector_test.go index 8651d4f13..7b19c5e05 100644 --- a/prometheus/process_collector_test.go +++ b/prometheus/process_collector_test.go @@ -18,8 +18,11 @@ package prometheus import ( "bytes" "errors" + "fmt" "os" + "path/filepath" "regexp" + "strings" "testing" "github.com/prometheus/common/expfmt" @@ -101,3 +104,64 @@ func TestProcessCollector(t *testing.T) { t.Errorf("%d metrics collected, want 1", n) } } + +func TestNewPidFileFn(t *testing.T) { + folderPath, err := os.Getwd() + if err != nil { + t.Error("failed to get current path") + } + mockPidFilePath := filepath.Join(folderPath, "mockPidFile") + defer os.Remove(mockPidFilePath) + + testCases := []struct { + mockPidFile func() + expectedErrPrefix string + expectedPid int + desc string + }{ + { + mockPidFile: func() { + os.Remove(mockPidFilePath) + }, + expectedErrPrefix: "can't read pid file", + expectedPid: 0, + desc: "no existed pid file", + }, + { + mockPidFile: func() { + os.Remove(mockPidFilePath) + f, _ := os.Create(mockPidFilePath) + f.Write([]byte("abc")) + f.Close() + }, + expectedErrPrefix: "can't parse pid file", + expectedPid: 0, + desc: "existed pid file, error pid number", + }, + { + mockPidFile: func() { + os.Remove(mockPidFilePath) + f, _ := os.Create(mockPidFilePath) + f.Write([]byte("123")) + f.Close() + }, + expectedErrPrefix: "", + expectedPid: 123, + desc: "existed pid file, correct pid number", + }, + } + + for _, tc := range testCases { + fn := NewPidFileFn(mockPidFilePath) + if fn == nil { + t.Error("Should not get nil PidFileFn") + } + + tc.mockPidFile() + + if pid, err := fn(); pid != tc.expectedPid || (err != nil && !strings.HasPrefix(err.Error(), tc.expectedErrPrefix)) { + fmt.Println(err.Error()) + t.Error(tc.desc) + } + } +} From c2caa7a1e99fb1a6c9ec1d5de0ddfbd38c05e410 Mon Sep 17 00:00:00 2001 From: prombot Date: Wed, 4 Nov 2020 00:08:58 +0000 Subject: [PATCH 3/9] Update common Prometheus files Signed-off-by: prombot --- Makefile.common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.common b/Makefile.common index 4e325f582..3ac29c636 100644 --- a/Makefile.common +++ b/Makefile.common @@ -78,7 +78,7 @@ ifneq ($(shell which gotestsum),) endif endif -PROMU_VERSION ?= 0.6.0 +PROMU_VERSION ?= 0.7.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz GOLANGCI_LINT := From 39b478e90c0bbd869817725d4553687513b7cf01 Mon Sep 17 00:00:00 2001 From: Bartlomiej Plotka Date: Fri, 20 Nov 2020 20:58:16 +0100 Subject: [PATCH 4/9] Added example api code showing how to add auth tokens and user agents to prom client. (#817) * Added example api code showing how to add auth tokens and user agents to prom client. Signed-off-by: Bartlomiej Plotka * Ran go mod tidy. Signed-off-by: Bartlomiej Plotka --- api/prometheus/v1/example_test.go | 114 ++++++++++++++++++++++++++++++ go.sum | 4 ++ 2 files changed, 118 insertions(+) diff --git a/api/prometheus/v1/example_test.go b/api/prometheus/v1/example_test.go index ac7189afe..1ff5d109a 100644 --- a/api/prometheus/v1/example_test.go +++ b/api/prometheus/v1/example_test.go @@ -18,11 +18,13 @@ package v1_test import ( "context" "fmt" + "net/http" "os" "time" "github.com/prometheus/client_golang/api" v1 "github.com/prometheus/client_golang/api/prometheus/v1" + "github.com/prometheus/common/config" ) func ExampleAPI_query() { @@ -76,6 +78,118 @@ func ExampleAPI_queryRange() { fmt.Printf("Result:\n%v\n", result) } +type userAgentRoundTripper struct { + name string + rt http.RoundTripper +} + +// RoundTrip implements the http.RoundTripper interface. +func (u userAgentRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { + if r.UserAgent() == "" { + // The specification of http.RoundTripper says that it shouldn't mutate + // the request so make a copy of req.Header since this is all that is + // modified. + r2 := new(http.Request) + *r2 = *r + r2.Header = make(http.Header) + for k, s := range r.Header { + r2.Header[k] = s + } + r2.Header.Set("User-Agent", u.name) + r = r2 + } + return u.rt.RoundTrip(r) +} + +func ExampleAPI_queryRangeWithUserAgent() { + client, err := api.NewClient(api.Config{ + Address: "http://demo.robustperception.io:9090", + RoundTripper: userAgentRoundTripper{name: "Client-Golang", rt: api.DefaultRoundTripper}, + }) + if err != nil { + fmt.Printf("Error creating client: %v\n", err) + os.Exit(1) + } + + v1api := v1.NewAPI(client) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + r := v1.Range{ + Start: time.Now().Add(-time.Hour), + End: time.Now(), + Step: time.Minute, + } + result, warnings, err := v1api.QueryRange(ctx, "rate(prometheus_tsdb_head_samples_appended_total[5m])", r) + if err != nil { + fmt.Printf("Error querying Prometheus: %v\n", err) + os.Exit(1) + } + if len(warnings) > 0 { + fmt.Printf("Warnings: %v\n", warnings) + } + fmt.Printf("Result:\n%v\n", result) +} + +func ExampleAPI_queryRangeWithBasicAuth() { + client, err := api.NewClient(api.Config{ + Address: "http://demo.robustperception.io:9090", + // We can use amazing github.com/prometheus/common/config helper! + RoundTripper: config.NewBasicAuthRoundTripper("me", "defintely_me", "", api.DefaultRoundTripper), + }) + if err != nil { + fmt.Printf("Error creating client: %v\n", err) + os.Exit(1) + } + + v1api := v1.NewAPI(client) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + r := v1.Range{ + Start: time.Now().Add(-time.Hour), + End: time.Now(), + Step: time.Minute, + } + result, warnings, err := v1api.QueryRange(ctx, "rate(prometheus_tsdb_head_samples_appended_total[5m])", r) + if err != nil { + fmt.Printf("Error querying Prometheus: %v\n", err) + os.Exit(1) + } + if len(warnings) > 0 { + fmt.Printf("Warnings: %v\n", warnings) + } + fmt.Printf("Result:\n%v\n", result) +} + +func ExampleAPI_queryRangeWithAuthBearerToken() { + client, err := api.NewClient(api.Config{ + Address: "http://demo.robustperception.io:9090", + // We can use amazing github.com/prometheus/common/config helper! + RoundTripper: config.NewBearerAuthRoundTripper("secret_token", api.DefaultRoundTripper), + }) + if err != nil { + fmt.Printf("Error creating client: %v\n", err) + os.Exit(1) + } + + v1api := v1.NewAPI(client) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + r := v1.Range{ + Start: time.Now().Add(-time.Hour), + End: time.Now(), + Step: time.Minute, + } + result, warnings, err := v1api.QueryRange(ctx, "rate(prometheus_tsdb_head_samples_appended_total[5m])", r) + if err != nil { + fmt.Printf("Error querying Prometheus: %v\n", err) + os.Exit(1) + } + if len(warnings) > 0 { + fmt.Printf("Warnings: %v\n", warnings) + } + fmt.Printf("Result:\n%v\n", result) +} + func ExampleAPI_series() { client, err := api.NewClient(api.Config{ Address: "http://demo.robustperception.io:9090", diff --git a/go.sum b/go.sum index b6a460afb..4d4fa8599 100644 --- a/go.sum +++ b/go.sum @@ -143,6 +143,7 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -189,6 +190,7 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -333,6 +335,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -370,6 +373,7 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From cf6dc827807a19a925656652203f34a788f1c035 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Sun, 29 Nov 2020 11:59:33 +0100 Subject: [PATCH 5/9] Correct spelling: possibilites -> possibilities Signed-off-by: Michael Vetter --- prometheus/promhttp/http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prometheus/promhttp/http.go b/prometheus/promhttp/http.go index 5e1c4546c..c95f9c8cd 100644 --- a/prometheus/promhttp/http.go +++ b/prometheus/promhttp/http.go @@ -99,7 +99,7 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight) } if opts.Registry != nil { - // Initialize all possibilites that can occur below. + // Initialize all possibilities that can occur below. errCnt.WithLabelValues("gathering") errCnt.WithLabelValues("encoding") if err := opts.Registry.Register(errCnt); err != nil { From 34ca1203775369991cf963e1b71fe1f230064dc5 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Wed, 2 Dec 2020 19:53:38 +0100 Subject: [PATCH 6/9] Be more explicit about the multi-line properties of MultiError Signed-off-by: beorn7 --- prometheus/promhttp/http.go | 8 ++++++-- prometheus/registry.go | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/prometheus/promhttp/http.go b/prometheus/promhttp/http.go index c95f9c8cd..d86d0cf4b 100644 --- a/prometheus/promhttp/http.go +++ b/prometheus/promhttp/http.go @@ -303,8 +303,12 @@ type Logger interface { // HandlerOpts specifies options how to serve metrics via an http.Handler. The // zero value of HandlerOpts is a reasonable default. type HandlerOpts struct { - // ErrorLog specifies an optional logger for errors collecting and - // serving metrics. If nil, errors are not logged at all. + // ErrorLog specifies an optional Logger for errors collecting and + // serving metrics. If nil, errors are not logged at all. Note that the + // type of a reported error is often prometheus.MultiError, which + // formats into a multi-line error string. If you want to avoid the + // latter, create a Logger implementation that detects a + // prometheus.MultiError and formats the contained errors into one line. ErrorLog Logger // ErrorHandling defines how errors are handled. Note that errors are // logged regardless of the configured ErrorHandling provided ErrorLog diff --git a/prometheus/registry.go b/prometheus/registry.go index ba94405af..48f5ef9d7 100644 --- a/prometheus/registry.go +++ b/prometheus/registry.go @@ -215,6 +215,8 @@ func (err AlreadyRegisteredError) Error() string { // by a Gatherer to report multiple errors during MetricFamily gathering. type MultiError []error +// Error formats the contained errors as a bullet point list, preceded by the +// total number of errors. Note that this results in a multi-line string. func (errs MultiError) Error() string { if len(errs) == 0 { return "" From 98eb6cbf7ccf0487b869d70efeebbb09dab53be1 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Wed, 9 Dec 2020 17:58:53 +0100 Subject: [PATCH 7/9] promhttp: Correctly detect invalid metric and label names Without this fix, the `InstrumentHandler...` middlewares get locked in an endless loop in case of an invalid Collector, eating all the memory. Signed-off-by: beorn7 --- prometheus/promhttp/instrument_server.go | 91 +++++++++++-------- prometheus/promhttp/instrument_server_test.go | 31 ++++++- 2 files changed, 79 insertions(+), 43 deletions(-) diff --git a/prometheus/promhttp/instrument_server.go b/prometheus/promhttp/instrument_server.go index 9db243805..ab037db86 100644 --- a/prometheus/promhttp/instrument_server.go +++ b/prometheus/promhttp/instrument_server.go @@ -43,14 +43,14 @@ func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handl // InstrumentHandlerDuration is a middleware that wraps the provided // http.Handler to observe the request duration with the provided ObserverVec. -// The ObserverVec must have zero, one, or two non-const non-curried labels. For -// those, the only allowed label names are "code" and "method". The function -// panics otherwise. The Observe method of the Observer in the ObserverVec is -// called with the request duration in seconds. Partitioning happens by HTTP -// status code and/or HTTP method if the respective instance label names are -// present in the ObserverVec. For unpartitioned observations, use an -// ObserverVec with zero labels. Note that partitioning of Histograms is -// expensive and should be used judiciously. +// The ObserverVec must have valid metric and label names and must have zero, +// one, or two non-const non-curried labels. For those, the only allowed label +// names are "code" and "method". The function panics otherwise. The Observe +// method of the Observer in the ObserverVec is called with the request duration +// in seconds. Partitioning happens by HTTP status code and/or HTTP method if +// the respective instance label names are present in the ObserverVec. For +// unpartitioned observations, use an ObserverVec with zero labels. Note that +// partitioning of Histograms is expensive and should be used judiciously. // // If the wrapped Handler does not set a status code, a status code of 200 is assumed. // @@ -79,12 +79,13 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) ht } // InstrumentHandlerCounter is a middleware that wraps the provided http.Handler -// to observe the request result with the provided CounterVec. The CounterVec -// must have zero, one, or two non-const non-curried labels. For those, the only -// allowed label names are "code" and "method". The function panics -// otherwise. Partitioning of the CounterVec happens by HTTP status code and/or -// HTTP method if the respective instance label names are present in the -// CounterVec. For unpartitioned counting, use a CounterVec with zero labels. +// to observe the request result with the provided CounterVec. The CounterVec +// must have valid metric and label names and must have zero, one, or two +// non-const non-curried labels. For those, the only allowed label names are +// "code" and "method". The function panics otherwise. Partitioning of the +// CounterVec happens by HTTP status code and/or HTTP method if the respective +// instance label names are present in the CounterVec. For unpartitioned +// counting, use a CounterVec with zero labels. // // If the wrapped Handler does not set a status code, a status code of 200 is assumed. // @@ -110,14 +111,15 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) // InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided // http.Handler to observe with the provided ObserverVec the request duration -// until the response headers are written. The ObserverVec must have zero, one, -// or two non-const non-curried labels. For those, the only allowed label names -// are "code" and "method". The function panics otherwise. The Observe method of -// the Observer in the ObserverVec is called with the request duration in -// seconds. Partitioning happens by HTTP status code and/or HTTP method if the -// respective instance label names are present in the ObserverVec. For -// unpartitioned observations, use an ObserverVec with zero labels. Note that -// partitioning of Histograms is expensive and should be used judiciously. +// until the response headers are written. The ObserverVec must have valid +// metric and label names and must have zero, one, or two non-const non-curried +// labels. For those, the only allowed label names are "code" and "method". The +// function panics otherwise. The Observe method of the Observer in the +// ObserverVec is called with the request duration in seconds. Partitioning +// happens by HTTP status code and/or HTTP method if the respective instance +// label names are present in the ObserverVec. For unpartitioned observations, +// use an ObserverVec with zero labels. Note that partitioning of Histograms is +// expensive and should be used judiciously. // // If the wrapped Handler panics before calling WriteHeader, no value is // reported. @@ -139,15 +141,15 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha } // InstrumentHandlerRequestSize is a middleware that wraps the provided -// http.Handler to observe the request size with the provided ObserverVec. The -// ObserverVec must have zero, one, or two non-const non-curried labels. For -// those, the only allowed label names are "code" and "method". The function -// panics otherwise. The Observe method of the Observer in the ObserverVec is -// called with the request size in bytes. Partitioning happens by HTTP status -// code and/or HTTP method if the respective instance label names are present in -// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero -// labels. Note that partitioning of Histograms is expensive and should be used -// judiciously. +// http.Handler to observe the request size with the provided ObserverVec. The +// ObserverVec must have valid metric and label names and must have zero, one, +// or two non-const non-curried labels. For those, the only allowed label names +// are "code" and "method". The function panics otherwise. The Observe method of +// the Observer in the ObserverVec is called with the request size in +// bytes. Partitioning happens by HTTP status code and/or HTTP method if the +// respective instance label names are present in the ObserverVec. For +// unpartitioned observations, use an ObserverVec with zero labels. Note that +// partitioning of Histograms is expensive and should be used judiciously. // // If the wrapped Handler does not set a status code, a status code of 200 is assumed. // @@ -174,15 +176,15 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) } // InstrumentHandlerResponseSize is a middleware that wraps the provided -// http.Handler to observe the response size with the provided ObserverVec. The -// ObserverVec must have zero, one, or two non-const non-curried labels. For -// those, the only allowed label names are "code" and "method". The function -// panics otherwise. The Observe method of the Observer in the ObserverVec is -// called with the response size in bytes. Partitioning happens by HTTP status -// code and/or HTTP method if the respective instance label names are present in -// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero -// labels. Note that partitioning of Histograms is expensive and should be used -// judiciously. +// http.Handler to observe the response size with the provided ObserverVec. The +// ObserverVec must have valid metric and label names and must have zero, one, +// or two non-const non-curried labels. For those, the only allowed label names +// are "code" and "method". The function panics otherwise. The Observe method of +// the Observer in the ObserverVec is called with the response size in +// bytes. Partitioning happens by HTTP status code and/or HTTP method if the +// respective instance label names are present in the ObserverVec. For +// unpartitioned observations, use an ObserverVec with zero labels. Note that +// partitioning of Histograms is expensive and should be used judiciously. // // If the wrapped Handler does not set a status code, a status code of 200 is assumed. // @@ -198,6 +200,11 @@ func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler }) } +// checkLabels returns whether the provided Collector has a non-const, +// non-curried label named "code" and/or "method". It panics if the provided +// Collector does not have a Desc or has more than one Desc or its Desc is +// invalid. It also panics if the Collector has any non-const, non-curried +// labels that are not named "code" or "method". func checkLabels(c prometheus.Collector) (code bool, method bool) { // TODO(beorn7): Remove this hacky way to check for instance labels // once Descriptors can have their dimensionality queried. @@ -225,6 +232,10 @@ func checkLabels(c prometheus.Collector) (code bool, method bool) { close(descc) + // Make sure the Collector has a valid Desc by registering it with a + // temporary registry. + prometheus.NewRegistry().MustRegister(c) + // Create a ConstMetric with the Desc. Since we don't know how many // variable labels there are, try for as long as it needs. for err := errors.New("dummy"); err != nil; lvs = append(lvs, magicString) { diff --git a/prometheus/promhttp/instrument_server_test.go b/prometheus/promhttp/instrument_server_test.go index 11e42f2b8..07387f323 100644 --- a/prometheus/promhttp/instrument_server_test.go +++ b/prometheus/promhttp/instrument_server_test.go @@ -25,6 +25,7 @@ import ( func TestLabelCheck(t *testing.T) { scenarios := map[string]struct { + metricName string // Defaults to "c". varLabels []string constLabels []string curriedLabels []string @@ -48,7 +49,7 @@ func TestLabelCheck(t *testing.T) { curriedLabels: []string{}, ok: true, }, - "cade and method as var labels": { + "code and method as var labels": { varLabels: []string{"method", "code"}, constLabels: []string{}, curriedLabels: []string{}, @@ -60,6 +61,12 @@ func TestLabelCheck(t *testing.T) { curriedLabels: []string{"dings", "bums"}, ok: true, }, + "all labels used with an invalid const label name": { + varLabels: []string{"code", "method"}, + constLabels: []string{"in-valid", "bar"}, + curriedLabels: []string{"dings", "bums"}, + ok: false, + }, "unsupported var label": { varLabels: []string{"foo"}, constLabels: []string{}, @@ -96,17 +103,35 @@ func TestLabelCheck(t *testing.T) { curriedLabels: []string{"method"}, ok: false, }, + "invalid name and otherwise empty": { + metricName: "in-valid", + varLabels: []string{}, + constLabels: []string{}, + curriedLabels: []string{}, + ok: false, + }, + "invalid name with all the otherwise valid labels": { + metricName: "in-valid", + varLabels: []string{"code", "method"}, + constLabels: []string{"foo", "bar"}, + curriedLabels: []string{"dings", "bums"}, + ok: false, + }, } for name, sc := range scenarios { t.Run(name, func(t *testing.T) { + metricName := sc.metricName + if metricName == "" { + metricName = "c" + } constLabels := prometheus.Labels{} for _, l := range sc.constLabels { constLabels[l] = "dummy" } c := prometheus.NewCounterVec( prometheus.CounterOpts{ - Name: "c", + Name: metricName, Help: "c help", ConstLabels: constLabels, }, @@ -114,7 +139,7 @@ func TestLabelCheck(t *testing.T) { ) o := prometheus.ObserverVec(prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Name: "c", + Name: metricName, Help: "c help", ConstLabels: constLabels, }, From 8d16199dea38f0d689454d6e56ffaeb1d381f6bb Mon Sep 17 00:00:00 2001 From: beorn7 Date: Wed, 16 Dec 2020 20:46:34 +0100 Subject: [PATCH 8/9] Update dependencies Signed-off-by: beorn7 --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 40be933cc..f22bf0e17 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ require ( github.com/golang/protobuf v1.4.3 github.com/json-iterator/go v1.1.10 github.com/prometheus/client_model v0.2.0 - github.com/prometheus/common v0.14.0 + github.com/prometheus/common v0.15.0 github.com/prometheus/procfs v0.2.0 - golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 + golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e ) go 1.11 diff --git a/go.sum b/go.sum index 4d4fa8599..b1338c6b5 100644 --- a/go.sum +++ b/go.sum @@ -248,8 +248,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= -github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= @@ -370,8 +370,8 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= From 80ca9cdc4e154cf193f8b1b8fddb1f24a3bcc475 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Wed, 16 Dec 2020 20:46:50 +0100 Subject: [PATCH 9/9] Cut release 1.9.0 Signed-off-by: beorn7 --- CHANGELOG.md | 5 +++++ VERSION | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c995e9c3..e3dc6aa5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.9.0 / 2020-12-17 + +* [FEATURE] `NewPidFileFn` helper to create process collectors for processes whose PID is read from a file. #804 +* [BUGFIX] promhttp: Prevent endless loop in `InstrumentHandler...` middlewares with invalid metric or label names. #823 + ## 1.8.0 / 2020-10-15 * [CHANGE] API client: Use `time.Time` rather than `string` for timestamps in `RuntimeinfoResult`. #777 diff --git a/VERSION b/VERSION index 27f9cd322..f8e233b27 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.8.0 +1.9.0