From 4da33c2bc261b0691b2ede95a34716a12554a964 Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Wed, 9 Feb 2022 09:58:08 +0100 Subject: [PATCH] Fix metrics bucket key high cardinality --- docs/content/migration/v2.md | 8 +++ .../content/observability/metrics/overview.md | 55 ++++++++++++++----- pkg/middlewares/metrics/metrics.go | 19 ++++++- pkg/middlewares/metrics/metrics_test.go | 31 +++++++++++ 4 files changed, 96 insertions(+), 17 deletions(-) diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index 1793ddd4c..41329d7f4 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -438,3 +438,11 @@ To enable HTTP/3 on an EntryPoint, please check out the [HTTP/3 configuration](. In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/getting-started/) of the specification and [route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces. Therefore, the [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) and [CRD](../reference/dynamic-configuration/kubernetes-gateway.md#definitions) definitions must be updated. + +## v2.6.0 to v2.6.1 + +In `v2.6.1`, the metrics system does not support any more custom HTTP method verbs to prevent potential metrics cardinality overhead. +In consequence, for metrics having the method label, +if the HTTP method verb of a request is not one defined in the set of common methods for [`HTTP/1.1`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) +or the [`PRI`](https://datatracker.ietf.org/doc/html/rfc7540#section-11.6) verb (for `HTTP/2`), +the value for the method label becomes `EXTENSION_METHOD`, instead of the request's one. diff --git a/docs/content/observability/metrics/overview.md b/docs/content/observability/metrics/overview.md index 13ef7aa79..0d1eae97a 100644 --- a/docs/content/observability/metrics/overview.md +++ b/docs/content/observability/metrics/overview.md @@ -61,7 +61,7 @@ traefik_config_last_reload_success The expiration date of certificates. -Available labels: `cn`, `sans`, `serial`. +[Labels](#labels): `cn`, `sans`, `serial`. ```dd tab="Datadog" tls.certs.notAfterTimestamp @@ -93,7 +93,7 @@ traefik_tls_certs_not_after The total count of HTTP requests received by an entrypoint. -Available labels: `code`, `method`, `protocol`, `entrypoint`. +[Labels](#labels): `code`, `method`, `protocol`, `entrypoint`. ```dd tab="Datadog" entrypoint.request.total @@ -116,7 +116,7 @@ traefik_entrypoint_requests_total The total count of HTTPS requests received by an entrypoint. -Available labels: `tls_version`, `tls_cipher`, `entrypoint`. +[Labels](#labels): `tls_version`, `tls_cipher`, `entrypoint`. ```dd tab="Datadog" entrypoint.request.tls.total @@ -139,7 +139,7 @@ traefik_entrypoint_requests_tls_total Request processing duration histogram on an entrypoint. -Available labels: `code`, `method`, `protocol`, `entrypoint`. +[Labels](#labels): `code`, `method`, `protocol`, `entrypoint`. ```dd tab="Datadog" entrypoint.request.duration @@ -162,7 +162,7 @@ traefik_entrypoint_request_duration_seconds The current count of open connections on an entrypoint. -Available labels: `method`, `protocol`, `entrypoint`. +[Labels](#labels): `method`, `protocol`, `entrypoint`. ```dd tab="Datadog" entrypoint.connections.open @@ -194,7 +194,7 @@ traefik_entrypoint_open_connections The total count of HTTP requests handled by a router. -Available labels: `code`, `method`, `protocol`, `router`, `service`. +[Labels](#labels): `code`, `method`, `protocol`, `router`, `service`. ```dd tab="Datadog" router.request.total @@ -217,7 +217,7 @@ traefik_router_requests_total The total count of HTTPS requests handled by a router. -Available labels: `tls_version`, `tls_cipher`, `router`, `service`. +[Labels](#labels): `tls_version`, `tls_cipher`, `router`, `service`. ```dd tab="Datadog" router.request.tls.total @@ -240,7 +240,7 @@ traefik_router_requests_tls_total Request processing duration histogram on a router. -Available labels: `code`, `method`, `protocol`, `router`, `service`. +[Labels](#labels): `code`, `method`, `protocol`, `router`, `service`. ```dd tab="Datadog" router.request.duration @@ -263,7 +263,7 @@ traefik_router_request_duration_seconds The current count of open connections on a router. -Available labels: `method`, `protocol`, `router`, `service`. +[Labels](#labels): `method`, `protocol`, `router`, `service`. ```dd tab="Datadog" router.connections.open @@ -297,7 +297,7 @@ traefik_router_open_connections The total count of HTTP requests processed on a service. -Available labels: `code`, `method`, `protocol`, `service`. +[Labels](#labels): `code`, `method`, `protocol`, `service`. ```dd tab="Datadog" service.request.total @@ -320,7 +320,7 @@ traefik_service_requests_total The total count of HTTPS requests processed on a service. -Available labels: `tls_version`, `tls_cipher`, `service`. +[Labels](#labels): `tls_version`, `tls_cipher`, `service`. ```dd tab="Datadog" router.service.tls.total @@ -343,7 +343,7 @@ traefik_service_requests_tls_total Request processing duration histogram on a service. -Available labels: `code`, `method`, `protocol`, `service`. +[Labels](#labels): `code`, `method`, `protocol`, `service`. ```dd tab="Datadog" service.request.duration @@ -366,7 +366,7 @@ traefik_service_request_duration_seconds The current count of open connections on a service. -Available labels: `method`, `protocol`, `service`. +[Labels](#labels): `method`, `protocol`, `service`. ```dd tab="Datadog" service.connections.open @@ -389,7 +389,7 @@ traefik_service_open_connections The count of requests retries on a service. -Available labels: `service`. +[Labels](#labels): `service`. ```dd tab="Datadog" service.retries.total @@ -412,7 +412,7 @@ traefik_service_retries_total Current service's server status, described by a gauge with a value of 0 for a down server or a value of 1 for an up server. -Available labels: `service`, `url`. +[Labels](#labels): `service`, `url`. ```dd tab="Datadog" service.server.up @@ -430,3 +430,28 @@ traefik_service_server_up # Default prefix: "traefik" {prefix}.service.server.up ``` + +## Labels + +Here is a comprehensive list of labels that are provided by the metrics: + +| Label | Description | example | +|---------------|---------------------------------------|----------------------------| +| `cn` | Certificate Common Name | "example.com" | +| `code` | Request code | "200" | +| `entrypoint` | Entrypoint that handled the request | "example_entrypoint" | +| `method` | Request Method | "GET" | +| `protocol` | Request protocol | "http" | +| `router` | Router that handled the request | "example_router" | +| `sans` | Certificate Subject Alternative NameS | "example.com" | +| `serial` | Certificate Serial Number | "123..." | +| `service` | Service that handled the request | "example_service@provider" | +| `tls_cipher` | TLS cipher used for the request | "TLS_FALLBACK_SCSV" | +| `tls_version` | TLS version used for the request | "1.0" | +| `url` | Service server url | "http://example.com" | + +!!! info "`method` label value" + + If the HTTP method verb on a request is not one defined in the set of common methods for [`HTTP/1.1`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) + or the [`PRI`](https://datatracker.ietf.org/doc/html/rfc7540#section-11.6) verb (for `HTTP/2`), + then the value for the method label becomes `EXTENSION_METHOD`. diff --git a/pkg/middlewares/metrics/metrics.go b/pkg/middlewares/metrics/metrics.go index 1f216393b..a491401cb 100644 --- a/pkg/middlewares/metrics/metrics.go +++ b/pkg/middlewares/metrics/metrics.go @@ -159,12 +159,27 @@ func containsHeader(req *http.Request, name, value string) bool { return false } +// getMethod returns the request's method. +// It checks whether the method is a valid UTF-8 string. +// To restrict the (potentially infinite) number of accepted values for the method, +// and avoid unbounded memory issues, +// values that are not part of the set of HTTP verbs are replaced with EXTENSION_METHOD. +// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods +// https://datatracker.ietf.org/doc/html/rfc2616/#section-5.1.1. func getMethod(r *http.Request) string { if !utf8.ValidString(r.Method) { - log.Warnf("Invalid HTTP method encoding: %s", r.Method) + log.WithoutContext().Warnf("Invalid HTTP method encoding: %s", r.Method) return "NON_UTF8_HTTP_METHOD" } - return r.Method + + switch r.Method { + case "HEAD", "GET", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", // https://datatracker.ietf.org/doc/html/rfc7231#section-4 + "PATCH", // https://datatracker.ietf.org/doc/html/rfc5789#section-2 + "PRI": // https://datatracker.ietf.org/doc/html/rfc7540#section-11.6 + return r.Method + default: + return "EXTENSION_METHOD" + } } type retryMetrics interface { diff --git a/pkg/middlewares/metrics/metrics_test.go b/pkg/middlewares/metrics/metrics_test.go index 596ac6abe..9390e49f5 100644 --- a/pkg/middlewares/metrics/metrics_test.go +++ b/pkg/middlewares/metrics/metrics_test.go @@ -4,6 +4,7 @@ import ( "net/http" "net/http/httptest" "reflect" + "strings" "testing" "github.com/go-kit/kit/metrics" @@ -98,3 +99,33 @@ func TestCloseNotifier(t *testing.T) { }) } } + +func Test_getMethod(t *testing.T) { + testCases := []struct { + method string + expected string + }{ + { + method: http.MethodGet, + expected: http.MethodGet, + }, + { + method: strings.ToLower(http.MethodGet), + expected: "EXTENSION_METHOD", + }, + { + method: "THIS_IS_NOT_A_VALID_METHOD", + expected: "EXTENSION_METHOD", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.method, func(t *testing.T) { + t.Parallel() + + request := httptest.NewRequest(test.method, "http://example.com", nil) + assert.Equal(t, test.expected, getMethod(request)) + }) + } +}