From ca55dfe1c6f03ca7f109c5faf38f268c9d458195 Mon Sep 17 00:00:00 2001 From: Dmitry Sharshakov Date: Wed, 9 Feb 2022 17:32:12 +0300 Subject: [PATCH] Support InfluxDB v2 metrics backend --- cmd/traefik/traefik.go | 10 + .../observability/metrics/influxdb2.md | 219 ++++++++++++++++++ .../content/observability/metrics/overview.md | 85 +++---- .../reference/static-configuration/cli-ref.md | 30 +++ .../reference/static-configuration/env-ref.md | 30 +++ .../reference/static-configuration/file.toml | 11 + .../reference/static-configuration/file.yaml | 12 + docs/mkdocs.yml | 1 + go.mod | 1 + go.sum | 8 +- pkg/metrics/influxdb.go | 2 +- pkg/metrics/influxdb2.go | 144 ++++++++++++ pkg/metrics/influxdb2_test.go | 145 ++++++++++++ pkg/metrics/influxdb_test.go | 9 +- pkg/server/server.go | 1 + pkg/types/metrics.go | 22 ++ 16 files changed, 681 insertions(+), 49 deletions(-) create mode 100644 docs/content/observability/metrics/influxdb2.md create mode 100644 pkg/metrics/influxdb2.go create mode 100644 pkg/metrics/influxdb2_test.go diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index eb57fad52..f03480bc0 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -448,6 +448,16 @@ func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry { metricsConfig.InfluxDB.Address, metricsConfig.InfluxDB.PushInterval) } + if metricsConfig.InfluxDB2 != nil { + ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb2")) + influxDB2Register := metrics.RegisterInfluxDB2(ctx, metricsConfig.InfluxDB2) + if influxDB2Register != nil { + registries = append(registries, influxDB2Register) + log.FromContext(ctx).Debugf("Configured InfluxDB v2 metrics: pushing to %s (%s org/%s bucket) once every %s", + metricsConfig.InfluxDB2.Address, metricsConfig.InfluxDB2.Org, metricsConfig.InfluxDB2.Bucket, metricsConfig.InfluxDB2.PushInterval) + } + } + return registries } diff --git a/docs/content/observability/metrics/influxdb2.md b/docs/content/observability/metrics/influxdb2.md new file mode 100644 index 000000000..a5ab61877 --- /dev/null +++ b/docs/content/observability/metrics/influxdb2.md @@ -0,0 +1,219 @@ +# InfluxDB v2 + +To enable the InfluxDB2: + +```yaml tab="File (YAML)" +metrics: + influxDB2: {} +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] +``` + +```bash tab="CLI" +--metrics.influxdb2=true +``` + +#### `address` + +_Required, Default="http://localhost:8086"_ + +Address of the InfluxDB v2 instance. + +```yaml tab="File (YAML)" +metrics: + influxDB2: + address: http://localhost:8086 +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] + address = "http://localhost:8086" +``` + +```bash tab="CLI" +--metrics.influxdb2.address=http://localhost:8086 +``` + +#### `token` + +_Required, Default=""_ + +Token with which to connect to InfluxDB v2. + +```yaml tab="File (YAML)" +metrics: + influxDB2: + token: secret +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] + token = "secret" +``` + +```bash tab="CLI" +--metrics.influxdb2.token=secret +``` + +#### `org` + +_Required, Default=""_ + +Organisation where metrics will be stored. + +```yaml tab="File (YAML)" +metrics: + influxDB2: + org: my-org +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] + org = "my-org" +``` + +```bash tab="CLI" +--metrics.influxdb2.org=my-org +``` + +#### `bucket` + +_Required, Default=""_ + +Bucket where metrics will be stored. + +```yaml tab="File (YAML)" +metrics: + influxDB2: + bucket: my-bucket +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] + bucket = "my-bucket" +``` + +```bash tab="CLI" +--metrics.influxdb2.bucket=my-bucket +``` + +#### `addEntryPointsLabels` + +_Optional, Default=true_ + +Enable metrics on entry points. + +```yaml tab="File (YAML)" +metrics: + influxDB2: + addEntryPointsLabels: true +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] + addEntryPointsLabels = true +``` + +```bash tab="CLI" +--metrics.influxdb2.addEntryPointsLabels=true +``` + +#### `addRoutersLabels` + +_Optional, Default=false_ + +Enable metrics on routers. + +```yaml tab="File (YAML)" +metrics: + influxDB2: + addRoutersLabels: true +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] + addRoutersLabels = true +``` + +```bash tab="CLI" +--metrics.influxdb2.addrouterslabels=true +``` + +#### `addServicesLabels` + +_Optional, Default=true_ + +Enable metrics on services. + +```yaml tab="File (YAML)" +metrics: + influxDB2: + addServicesLabels: true +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] + addServicesLabels = true +``` + +```bash tab="CLI" +--metrics.influxdb2.addServicesLabels=true +``` + +#### `pushInterval` + +_Optional, Default=10s_ + +The interval used by the exporter to push metrics to InfluxDB server. + +```yaml tab="File (YAML)" +metrics: + influxDB2: + pushInterval: 10s +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] + pushInterval = "10s" +``` + +```bash tab="CLI" +--metrics.influxdb2.pushInterval=10s +``` + +#### `additionalLabels` + +_Optional, Default={}_ + +Additional labels (InfluxDB tags) on all metrics. + +```yaml tab="File (YAML)" +metrics: + influxDB2: + additionalLabels: + host: example.com + environment: production +``` + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB2] + [metrics.influxDB2.additionalLabels] + host = "example.com" + environment = "production" +``` + +```bash tab="CLI" +--metrics.influxdb2.additionallabels.host=example.com --metrics.influxdb2.additionallabels.environment=production +``` diff --git a/docs/content/observability/metrics/overview.md b/docs/content/observability/metrics/overview.md index 13ef7aa79..002aa096a 100644 --- a/docs/content/observability/metrics/overview.md +++ b/docs/content/observability/metrics/overview.md @@ -4,16 +4,17 @@ Traefik supports 4 metrics backends: - [Datadog](./datadog.md) - [InfluxDB](./influxdb.md) +- [InfluxDB2](./influxdb2.md) - [Prometheus](./prometheus.md) - [StatsD](./statsd.md) ## Global Metrics -| Metric | DataDog | InfluxDB | Prometheus | StatsD | -|-------------------------------------------------------------------------|---------|----------|------------|--------| -| [Configuration reloads](#configuration-reloads) | ✓ | ✓ | ✓ | ✓ | -| [Last Configuration Reload Success](#last-configuration-reload-success) | ✓ | ✓ | ✓ | ✓ | -| [TLS certificates expiration](#tls-certificates-expiration) | ✓ | ✓ | ✓ | ✓ | +| Metric | DataDog | InfluxDB / InfluxDB2 | Prometheus | StatsD | +|-------------------------------------------------------------------------|---------|----------------------|------------|--------| +| [Configuration reloads](#configuration-reloads) | ✓ | ✓ | ✓ | ✓ | +| [Last Configuration Reload Success](#last-configuration-reload-success) | ✓ | ✓ | ✓ | ✓ | +| [TLS certificates expiration](#tls-certificates-expiration) | ✓ | ✓ | ✓ | ✓ | ### Configuration Reloads @@ -23,7 +24,7 @@ The total count of configuration reloads. config.reload.total ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.config.reload.total ``` @@ -44,7 +45,7 @@ The timestamp of the last configuration reload success. config.reload.lastSuccessTimestamp ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.config.reload.lastSuccessTimestamp ``` @@ -67,7 +68,7 @@ Available labels: `cn`, `sans`, `serial`. tls.certs.notAfterTimestamp ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.tls.certs.notAfterTimestamp ``` @@ -82,12 +83,12 @@ traefik_tls_certs_not_after ## EntryPoint Metrics -| Metric | DataDog | InfluxDB | Prometheus | StatsD | -|-----------------------------------------------------------|---------|----------|------------|--------| -| [HTTP Requests Count](#http-requests-count) | ✓ | ✓ | ✓ | ✓ | -| [HTTPS Requests Count](#https-requests-count) | ✓ | ✓ | ✓ | ✓ | -| [Request Duration Histogram](#request-duration-histogram) | ✓ | ✓ | ✓ | ✓ | -| [Open Connections Count](#open-connections-count) | ✓ | ✓ | ✓ | ✓ | +| Metric | DataDog | InfluxDB / InfluxDB2 | Prometheus | StatsD | +|-----------------------------------------------------------|---------|----------------------|------------|--------| +| [HTTP Requests Count](#http-requests-count) | ✓ | ✓ | ✓ | ✓ | +| [HTTPS Requests Count](#https-requests-count) | ✓ | ✓ | ✓ | ✓ | +| [Request Duration Histogram](#request-duration-histogram) | ✓ | ✓ | ✓ | ✓ | +| [Open Connections Count](#open-connections-count) | ✓ | ✓ | ✓ | ✓ | ### HTTP Requests Count @@ -99,7 +100,7 @@ Available labels: `code`, `method`, `protocol`, `entrypoint`. entrypoint.request.total ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.entrypoint.requests.total ``` @@ -122,7 +123,7 @@ Available labels: `tls_version`, `tls_cipher`, `entrypoint`. entrypoint.request.tls.total ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.entrypoint.requests.tls.total ``` @@ -145,7 +146,7 @@ Available labels: `code`, `method`, `protocol`, `entrypoint`. entrypoint.request.duration ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.entrypoint.request.duration ``` @@ -168,7 +169,7 @@ Available labels: `method`, `protocol`, `entrypoint`. entrypoint.connections.open ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.entrypoint.connections.open ``` @@ -183,12 +184,12 @@ traefik_entrypoint_open_connections ## Router Metrics -| Metric | DataDog | InfluxDB | Prometheus | StatsD | -|-------------------------------------------------------------|---------|----------|------------|--------| -| [HTTP Requests Count](#http-requests-count_1) | ✓ | ✓ | ✓ | ✓ | -| [HTTPS Requests Count](#https-requests-count_1) | ✓ | ✓ | ✓ | ✓ | -| [Request Duration Histogram](#request-duration-histogram_1) | ✓ | ✓ | ✓ | ✓ | -| [Open Connections Count](#open-connections-count_1) | ✓ | ✓ | ✓ | ✓ | +| Metric | DataDog | InfluxDB / InfluxDB2 | Prometheus | StatsD | +|-------------------------------------------------------------|---------|----------------------|------------|--------| +| [HTTP Requests Count](#http-requests-count_1) | ✓ | ✓ | ✓ | ✓ | +| [HTTPS Requests Count](#https-requests-count_1) | ✓ | ✓ | ✓ | ✓ | +| [Request Duration Histogram](#request-duration-histogram_1) | ✓ | ✓ | ✓ | ✓ | +| [Open Connections Count](#open-connections-count_1) | ✓ | ✓ | ✓ | ✓ | ### HTTP Requests Count @@ -200,7 +201,7 @@ Available labels: `code`, `method`, `protocol`, `router`, `service`. router.request.total ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.router.requests.total ``` @@ -223,7 +224,7 @@ Available labels: `tls_version`, `tls_cipher`, `router`, `service`. router.request.tls.total ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.router.requests.tls.total ``` @@ -246,7 +247,7 @@ Available labels: `code`, `method`, `protocol`, `router`, `service`. router.request.duration ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.router.request.duration ``` @@ -269,7 +270,7 @@ Available labels: `method`, `protocol`, `router`, `service`. router.connections.open ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.router.connections.open ``` @@ -284,14 +285,14 @@ traefik_router_open_connections ## Service Metrics -| Metric | DataDog | InfluxDB | Prometheus | StatsD | -|-------------------------------------------------------------|---------|----------|------------|--------| -| [HTTP Requests Count](#http-requests-count_2) | ✓ | ✓ | ✓ | ✓ | -| [HTTPS Requests Count](#https-requests-count_2) | ✓ | ✓ | ✓ | ✓ | -| [Request Duration Histogram](#request-duration-histogram_2) | ✓ | ✓ | ✓ | ✓ | -| [Open Connections Count](#open-connections-count_2) | ✓ | ✓ | ✓ | ✓ | -| [Requests Retries Count](#requests-retries-count) | ✓ | ✓ | ✓ | ✓ | -| [Service Server UP](#service-server-up) | ✓ | ✓ | ✓ | ✓ | +| Metric | DataDog | InfluxDB / InfluxDB2 | Prometheus | StatsD | +|-------------------------------------------------------------|---------|----------------------|------------|--------| +| [HTTP Requests Count](#http-requests-count_2) | ✓ | ✓ | ✓ | ✓ | +| [HTTPS Requests Count](#https-requests-count_2) | ✓ | ✓ | ✓ | ✓ | +| [Request Duration Histogram](#request-duration-histogram_2) | ✓ | ✓ | ✓ | ✓ | +| [Open Connections Count](#open-connections-count_2) | ✓ | ✓ | ✓ | ✓ | +| [Requests Retries Count](#requests-retries-count) | ✓ | ✓ | ✓ | ✓ | +| [Service Server UP](#service-server-up) | ✓ | ✓ | ✓ | ✓ | ### HTTP Requests Count @@ -303,7 +304,7 @@ Available labels: `code`, `method`, `protocol`, `service`. service.request.total ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.service.requests.total ``` @@ -326,7 +327,7 @@ Available labels: `tls_version`, `tls_cipher`, `service`. router.service.tls.total ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.service.requests.tls.total ``` @@ -349,7 +350,7 @@ Available labels: `code`, `method`, `protocol`, `service`. service.request.duration ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.service.request.duration ``` @@ -372,7 +373,7 @@ Available labels: `method`, `protocol`, `service`. service.connections.open ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.service.connections.open ``` @@ -395,7 +396,7 @@ Available labels: `service`. service.retries.total ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.service.retries.total ``` @@ -418,7 +419,7 @@ Available labels: `service`, `url`. service.server.up ``` -```influxdb tab="InfluxDB" +```influxdb tab="InfluxDB / InfluxDB2" traefik.service.server.up ``` diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index 198854d4b..462107a8c 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -285,6 +285,36 @@ InfluxDB retention policy used when protocol is http. `--metrics.influxdb.username`: InfluxDB username (only with http). +`--metrics.influxdb2`: +InfluxDB v2 metrics exporter type. (Default: ```false```) + +`--metrics.influxdb2.addentrypointslabels`: +Enable metrics on entry points. (Default: ```true```) + +`--metrics.influxdb2.additionallabels.`: +Additional labels (influxdb tags) on all metrics + +`--metrics.influxdb2.address`: +InfluxDB v2 address. (Default: ```http://localhost:8086```) + +`--metrics.influxdb2.addrouterslabels`: +Enable metrics on routers. (Default: ```false```) + +`--metrics.influxdb2.addserviceslabels`: +Enable metrics on services. (Default: ```true```) + +`--metrics.influxdb2.bucket`: +InfluxDB v2 bucket ID. + +`--metrics.influxdb2.org`: +InfluxDB v2 org ID. + +`--metrics.influxdb2.pushinterval`: +InfluxDB v2 push interval. (Default: ```10```) + +`--metrics.influxdb2.token`: +InfluxDB v2 access token. + `--metrics.prometheus`: Prometheus metrics exporter type. (Default: ```false```) diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 297ba299e..133152adf 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -252,6 +252,36 @@ Datadog push interval. (Default: ```10```) `TRAEFIK_METRICS_INFLUXDB`: InfluxDB metrics exporter type. (Default: ```false```) +`TRAEFIK_METRICS_INFLUXDB2`: +InfluxDB v2 metrics exporter type. (Default: ```false```) + +`TRAEFIK_METRICS_INFLUXDB2_ADDENTRYPOINTSLABELS`: +Enable metrics on entry points. (Default: ```true```) + +`TRAEFIK_METRICS_INFLUXDB2_ADDITIONALLABELS_`: +Additional labels (influxdb tags) on all metrics + +`TRAEFIK_METRICS_INFLUXDB2_ADDRESS`: +InfluxDB v2 address. (Default: ```http://localhost:8086```) + +`TRAEFIK_METRICS_INFLUXDB2_ADDROUTERSLABELS`: +Enable metrics on routers. (Default: ```false```) + +`TRAEFIK_METRICS_INFLUXDB2_ADDSERVICESLABELS`: +Enable metrics on services. (Default: ```true```) + +`TRAEFIK_METRICS_INFLUXDB2_BUCKET`: +InfluxDB v2 bucket ID. + +`TRAEFIK_METRICS_INFLUXDB2_ORG`: +InfluxDB v2 org ID. + +`TRAEFIK_METRICS_INFLUXDB2_PUSHINTERVAL`: +InfluxDB v2 push interval. (Default: ```10```) + +`TRAEFIK_METRICS_INFLUXDB2_TOKEN`: +InfluxDB v2 access token. + `TRAEFIK_METRICS_INFLUXDB_ADDENTRYPOINTSLABELS`: Enable metrics on entry points. (Default: ```true```) diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index 9de2bc365..7beea9f78 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -281,6 +281,17 @@ addServicesLabels = true [metrics.influxDB.additionalLabels] foobar = "foobar" + [metrics.influxDB2] + address = "foobar" + token = "foobar" + pushInterval = "42s" + org = "foobar" + bucket = "foobar" + addEntryPointsLabels = true + addRoutersLabels = true + addServicesLabels = true + [metrics.influxDB2.additionalLabels] + foobar = "foobar" [ping] entryPoint = "foobar" diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index f7068ab38..18cad958c 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -303,6 +303,18 @@ metrics: addServicesLabels: true additionalLabels: foobar: foobar + influxDB2: + address: foobar + token: foobar + pushInterval: 42s + org: foobar + bucket: foobar + addEntryPointsLabels: true + addRoutersLabels: true + addServicesLabels: true + additionalLabels: + foobar: foobar + ping: entryPoint: foobar manualRouting: true diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index e173d709e..cee6e7980 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -147,6 +147,7 @@ nav: - 'Overview': 'observability/metrics/overview.md' - 'Datadog': 'observability/metrics/datadog.md' - 'InfluxDB': 'observability/metrics/influxdb.md' + - 'InfluxDB2': 'observability/metrics/influxdb2.md' - 'Prometheus': 'observability/metrics/prometheus.md' - 'StatsD': 'observability/metrics/statsd.md' - 'Tracing': diff --git a/go.mod b/go.mod index 4c1e9e998..5eb2616d2 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( github.com/hashicorp/go-hclog v0.16.1 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.3.0 + github.com/influxdata/influxdb-client-go/v2 v2.7.0 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d github.com/instana/go-sensor v1.38.3 github.com/klauspost/compress v1.13.0 diff --git a/go.sum b/go.sum index 750bc5a7f..36a4acbc9 100644 --- a/go.sum +++ b/go.sum @@ -473,8 +473,9 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.6.1 h1:2BvsmRb6pogGNtr8Ann+esAbSKFXx2CZN18VpAMecnw= github.com/deepmap/oapi-codegen v1.6.1/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= +github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU= +github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= @@ -615,6 +616,7 @@ github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 h1:df6OFl8WNX github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2/go.mod h1:GLyXJD41gBO/NPKVPGQbhyyC06eugGy15QEZyUkE2/s= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -988,8 +990,12 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb-client-go/v2 v2.7.0 h1:QgP5mlBE9sGnzplpnf96pr+p7uqlIlL4W2GAP3n+XZg= +github.com/influxdata/influxdb-client-go/v2 v2.7.0/go.mod h1:Y/0W1+TZir7ypoQZYd2IrnVOKB3Tq6oegAQeSVN/+EU= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU= github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= github.com/instana/go-sensor v1.38.3 h1:/PdHEDveLmUCvK+O6REvSv8kKljX8vaj9JMjMeCHAWk= diff --git a/pkg/metrics/influxdb.go b/pkg/metrics/influxdb.go index 6fc50b1ce..cbb72b997 100644 --- a/pkg/metrics/influxdb.go +++ b/pkg/metrics/influxdb.go @@ -98,7 +98,7 @@ func RegisterInfluxDB(ctx context.Context, config *types.InfluxDB) Registry { return registry } -// initInfluxDBTicker creates a influxDBClient. +// initInfluxDBClient creates a influxDBClient. func initInfluxDBClient(ctx context.Context, config *types.InfluxDB) *influx.Influx { logger := log.FromContext(ctx) diff --git a/pkg/metrics/influxdb2.go b/pkg/metrics/influxdb2.go new file mode 100644 index 000000000..391499c65 --- /dev/null +++ b/pkg/metrics/influxdb2.go @@ -0,0 +1,144 @@ +package metrics + +import ( + "context" + "errors" + "time" + + kitlog "github.com/go-kit/kit/log" + "github.com/go-kit/kit/metrics/influx" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" + influxdb2api "github.com/influxdata/influxdb-client-go/v2/api" + "github.com/influxdata/influxdb-client-go/v2/api/write" + influxdb2log "github.com/influxdata/influxdb-client-go/v2/log" + influxdb "github.com/influxdata/influxdb1-client/v2" + "github.com/traefik/traefik/v2/pkg/log" + "github.com/traefik/traefik/v2/pkg/safe" + "github.com/traefik/traefik/v2/pkg/types" +) + +var ( + influxDB2Ticker *time.Ticker + influxDB2Store *influx.Influx + influxDB2Client influxdb2.Client +) + +// RegisterInfluxDB2 creates metrics exporter for InfluxDB2. +func RegisterInfluxDB2(ctx context.Context, config *types.InfluxDB2) Registry { + if influxDB2Client == nil { + var err error + if influxDB2Client, err = newInfluxDB2Client(config); err != nil { + log.FromContext(ctx).Error(err) + return nil + } + } + + if influxDB2Store == nil { + influxDB2Store = influx.New( + config.AdditionalLabels, + influxdb.BatchPointsConfig{}, + kitlog.LoggerFunc(func(kv ...interface{}) error { + log.FromContext(ctx).Error(kv) + return nil + }), + ) + + influxDB2Ticker = time.NewTicker(time.Duration(config.PushInterval)) + + safe.Go(func() { + wc := influxDB2Client.WriteAPIBlocking(config.Org, config.Bucket) + influxDB2Store.WriteLoop(ctx, influxDB2Ticker.C, influxDB2Writer{wc: wc}) + }) + } + + registry := &standardRegistry{ + configReloadsCounter: influxDB2Store.NewCounter(influxDBConfigReloadsName), + configReloadsFailureCounter: influxDB2Store.NewCounter(influxDBConfigReloadsFailureName), + lastConfigReloadSuccessGauge: influxDB2Store.NewGauge(influxDBLastConfigReloadSuccessName), + lastConfigReloadFailureGauge: influxDB2Store.NewGauge(influxDBLastConfigReloadFailureName), + tlsCertsNotAfterTimestampGauge: influxDB2Store.NewGauge(influxDBTLSCertsNotAfterTimestampName), + } + + if config.AddEntryPointsLabels { + registry.epEnabled = config.AddEntryPointsLabels + registry.entryPointReqsCounter = influxDB2Store.NewCounter(influxDBEntryPointReqsName) + registry.entryPointReqsTLSCounter = influxDB2Store.NewCounter(influxDBEntryPointReqsTLSName) + registry.entryPointReqDurationHistogram, _ = NewHistogramWithScale(influxDB2Store.NewHistogram(influxDBEntryPointReqDurationName), time.Second) + registry.entryPointOpenConnsGauge = influxDB2Store.NewGauge(influxDBEntryPointOpenConnsName) + } + + if config.AddRoutersLabels { + registry.routerEnabled = config.AddRoutersLabels + registry.routerReqsCounter = influxDB2Store.NewCounter(influxDBRouterReqsName) + registry.routerReqsTLSCounter = influxDB2Store.NewCounter(influxDBRouterReqsTLSName) + registry.routerReqDurationHistogram, _ = NewHistogramWithScale(influxDB2Store.NewHistogram(influxDBRouterReqsDurationName), time.Second) + registry.routerOpenConnsGauge = influxDB2Store.NewGauge(influxDBORouterOpenConnsName) + } + + if config.AddServicesLabels { + registry.svcEnabled = config.AddServicesLabels + registry.serviceReqsCounter = influxDB2Store.NewCounter(influxDBServiceReqsName) + registry.serviceReqsTLSCounter = influxDB2Store.NewCounter(influxDBServiceReqsTLSName) + registry.serviceReqDurationHistogram, _ = NewHistogramWithScale(influxDB2Store.NewHistogram(influxDBServiceReqsDurationName), time.Second) + registry.serviceRetriesCounter = influxDB2Store.NewCounter(influxDBServiceRetriesTotalName) + registry.serviceOpenConnsGauge = influxDB2Store.NewGauge(influxDBServiceOpenConnsName) + registry.serviceServerUpGauge = influxDB2Store.NewGauge(influxDBServiceServerUpName) + } + + return registry +} + +// StopInfluxDB2 stops and resets InfluxDB2 client, ticker and store. +func StopInfluxDB2() { + if influxDB2Client != nil { + influxDB2Client.Close() + } + influxDB2Client = nil + + if influxDB2Ticker != nil { + influxDB2Ticker.Stop() + } + influxDB2Ticker = nil + + influxDB2Store = nil +} + +// newInfluxDB2Client creates an influxdb2.Client. +func newInfluxDB2Client(config *types.InfluxDB2) (influxdb2.Client, error) { + if config.Token == "" || config.Org == "" || config.Bucket == "" { + return nil, errors.New("token, org or bucket property is missing") + } + + // Disable InfluxDB2 logs. + // See https://github.com/influxdata/influxdb-client-go/blob/v2.7.0/options.go#L128 + influxdb2log.Log = nil + + return influxdb2.NewClient(config.Address, config.Token), nil +} + +type influxDB2Writer struct { + wc influxdb2api.WriteAPIBlocking +} + +func (w influxDB2Writer) Write(bp influxdb.BatchPoints) error { + ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb2")) + logger := log.FromContext(ctx) + + wps := make([]*write.Point, 0, len(bp.Points())) + for _, p := range bp.Points() { + fields, err := p.Fields() + if err != nil { + logger.Errorf("Error while getting %s point fields: %s", p.Name(), err) + continue + } + + wps = append(wps, influxdb2.NewPoint( + p.Name(), + p.Tags(), + fields, + p.Time(), + )) + } + + return w.wc.WritePoint(ctx, wps...) +} diff --git a/pkg/metrics/influxdb2_test.go b/pkg/metrics/influxdb2_test.go new file mode 100644 index 000000000..272814b79 --- /dev/null +++ b/pkg/metrics/influxdb2_test.go @@ -0,0 +1,145 @@ +package metrics + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" + ptypes "github.com/traefik/paerser/types" + "github.com/traefik/traefik/v2/pkg/types" +) + +func TestInfluxDB2(t *testing.T) { + c := make(chan *string) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + require.NoError(t, err) + + bodyStr := string(body) + c <- &bodyStr + _, _ = fmt.Fprintln(w, "ok") + })) + defer ts.Close() + + influxDB2Registry := RegisterInfluxDB2(context.Background(), + &types.InfluxDB2{ + Address: ts.URL, + Token: "test-token", + PushInterval: ptypes.Duration(10 * time.Millisecond), + Org: "test-org", + Bucket: "test-bucket", + AddEntryPointsLabels: true, + AddRoutersLabels: true, + AddServicesLabels: true, + }) + defer StopInfluxDB2() + + if !influxDB2Registry.IsEpEnabled() || !influxDB2Registry.IsRouterEnabled() || !influxDB2Registry.IsSvcEnabled() { + t.Fatalf("InfluxDB2Registry should return true for IsEnabled(), IsRouterEnabled() and IsSvcEnabled()") + } + + expectedServer := []string{ + `(traefik\.config\.reload\.total count=1) [\d]{19}`, + `(traefik\.config\.reload\.total\.failure count=1) [\d]{19}`, + `(traefik\.config\.reload\.lastSuccessTimestamp value=1) [\d]{19}`, + `(traefik\.config\.reload\.lastFailureTimestamp value=1) [\d]{19}`, + } + + influxDB2Registry.ConfigReloadsCounter().Add(1) + influxDB2Registry.ConfigReloadsFailureCounter().Add(1) + influxDB2Registry.LastConfigReloadSuccessGauge().Set(1) + influxDB2Registry.LastConfigReloadFailureGauge().Set(1) + msgServer := <-c + + assertMessage(t, *msgServer, expectedServer) + + expectedTLS := []string{ + `(traefik\.tls\.certs\.notAfterTimestamp,key=value value=1) [\d]{19}`, + } + + influxDB2Registry.TLSCertsNotAfterTimestampGauge().With("key", "value").Set(1) + msgTLS := <-c + + assertMessage(t, *msgTLS, expectedTLS) + + expectedEntrypoint := []string{ + `(traefik\.entrypoint\.requests\.total,code=200,entrypoint=test,method=GET count=1) [\d]{19}`, + `(traefik\.entrypoint\.requests\.tls\.total,entrypoint=test,tls_cipher=bar,tls_version=foo count=1) [\d]{19}`, + `(traefik\.entrypoint\.request\.duration(?:,code=[\d]{3})?,entrypoint=test p50=10000,p90=10000,p95=10000,p99=10000) [\d]{19}`, + `(traefik\.entrypoint\.connections\.open,entrypoint=test value=1) [\d]{19}`, + } + + influxDB2Registry.EntryPointReqsCounter().With("entrypoint", "test", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1) + influxDB2Registry.EntryPointReqsTLSCounter().With("entrypoint", "test", "tls_version", "foo", "tls_cipher", "bar").Add(1) + influxDB2Registry.EntryPointReqDurationHistogram().With("entrypoint", "test").Observe(10000) + influxDB2Registry.EntryPointOpenConnsGauge().With("entrypoint", "test").Set(1) + msgEntrypoint := <-c + + assertMessage(t, *msgEntrypoint, expectedEntrypoint) + + expectedRouter := []string{ + `(traefik\.router\.requests\.total,code=200,method=GET,router=demo,service=test count=1) [\d]{19}`, + `(traefik\.router\.requests\.total,code=404,method=GET,router=demo,service=test count=1) [\d]{19}`, + `(traefik\.router\.requests\.tls\.total,router=demo,service=test,tls_cipher=bar,tls_version=foo count=1) [\d]{19}`, + `(traefik\.router\.request\.duration,code=200,router=demo,service=test p50=10000,p90=10000,p95=10000,p99=10000) [\d]{19}`, + `(traefik\.router\.connections\.open,router=demo,service=test value=1) [\d]{19}`, + } + + influxDB2Registry.RouterReqsCounter().With("router", "demo", "service", "test", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1) + influxDB2Registry.RouterReqsCounter().With("router", "demo", "service", "test", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1) + influxDB2Registry.RouterReqsTLSCounter().With("router", "demo", "service", "test", "tls_version", "foo", "tls_cipher", "bar").Add(1) + influxDB2Registry.RouterReqDurationHistogram().With("router", "demo", "service", "test", "code", strconv.Itoa(http.StatusOK)).Observe(10000) + influxDB2Registry.RouterOpenConnsGauge().With("router", "demo", "service", "test").Set(1) + msgRouter := <-c + + assertMessage(t, *msgRouter, expectedRouter) + + expectedService := []string{ + `(traefik\.service\.requests\.total,code=200,method=GET,service=test count=1) [\d]{19}`, + `(traefik\.service\.requests\.total,code=404,method=GET,service=test count=1) [\d]{19}`, + `(traefik\.service\.requests\.tls\.total,service=test,tls_cipher=bar,tls_version=foo count=1) [\d]{19}`, + `(traefik\.service\.request\.duration,code=200,service=test p50=10000,p90=10000,p95=10000,p99=10000) [\d]{19}`, + `(traefik\.service\.server\.up,service=test,url=http://127.0.0.1 value=1) [\d]{19}`, + } + + influxDB2Registry.ServiceReqsCounter().With("service", "test", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1) + influxDB2Registry.ServiceReqsCounter().With("service", "test", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1) + influxDB2Registry.ServiceReqsTLSCounter().With("service", "test", "tls_version", "foo", "tls_cipher", "bar").Add(1) + influxDB2Registry.ServiceReqDurationHistogram().With("service", "test", "code", strconv.Itoa(http.StatusOK)).Observe(10000) + influxDB2Registry.ServiceServerUpGauge().With("service", "test", "url", "http://127.0.0.1").Set(1) + msgService := <-c + + assertMessage(t, *msgService, expectedService) + + expectedServiceRetries := []string{ + `(traefik\.service\.retries\.total,service=test count=2) [\d]{19}`, + `(traefik\.service\.retries\.total,service=foobar count=1) [\d]{19}`, + } + + influxDB2Registry.ServiceRetriesCounter().With("service", "test").Add(1) + influxDB2Registry.ServiceRetriesCounter().With("service", "test").Add(1) + influxDB2Registry.ServiceRetriesCounter().With("service", "foobar").Add(1) + + msgServiceRetries := <-c + + assertMessage(t, *msgServiceRetries, expectedServiceRetries) + + expectedServiceOpenConns := []string{ + `(traefik\.service\.connections\.open,service=test value=2) [\d]{19}`, + `(traefik\.service\.connections\.open,service=foobar value=1) [\d]{19}`, + } + + influxDB2Registry.ServiceOpenConnsGauge().With("service", "test").Add(1) + influxDB2Registry.ServiceOpenConnsGauge().With("service", "test").Add(1) + influxDB2Registry.ServiceOpenConnsGauge().With("service", "foobar").Add(1) + + msgServiceOpenConns := <-c + + assertMessage(t, *msgServiceOpenConns, expectedServiceOpenConns) +} diff --git a/pkg/metrics/influxdb_test.go b/pkg/metrics/influxdb_test.go index 691452632..6889ffa9b 100644 --- a/pkg/metrics/influxdb_test.go +++ b/pkg/metrics/influxdb_test.go @@ -11,6 +11,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" "github.com/stvp/go-udp-testing" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v2/pkg/types" @@ -125,10 +126,8 @@ func TestInfluxDBHTTP(t *testing.T) { c := make(chan *string) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, "can't read body "+err.Error(), http.StatusBadRequest) - return - } + require.NoError(t, err) + bodyStr := string(body) c <- &bodyStr _, _ = fmt.Fprintln(w, "ok") @@ -140,7 +139,7 @@ func TestInfluxDBHTTP(t *testing.T) { &types.InfluxDB{ Address: ts.URL, Protocol: "http", - PushInterval: ptypes.Duration(time.Second), + PushInterval: ptypes.Duration(10 * time.Millisecond), Database: "test", RetentionPolicy: "autogen", AddEntryPointsLabels: true, diff --git a/pkg/server/server.go b/pkg/server/server.go index 9c585b7a3..1a3f136e8 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -111,4 +111,5 @@ func stopMetricsClients() { metrics.StopDatadog() metrics.StopStatsd() metrics.StopInfluxDB() + metrics.StopInfluxDB2() } diff --git a/pkg/types/metrics.go b/pkg/types/metrics.go index 3b4c6463c..dfce349ec 100644 --- a/pkg/types/metrics.go +++ b/pkg/types/metrics.go @@ -14,6 +14,7 @@ type Metrics struct { Datadog *Datadog `description:"Datadog metrics exporter type." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` StatsD *Statsd `description:"StatsD metrics exporter type." json:"statsD,omitempty" toml:"statsD,omitempty" yaml:"statsD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` InfluxDB *InfluxDB `description:"InfluxDB metrics exporter type." json:"influxDB,omitempty" toml:"influxDB,omitempty" yaml:"influxDB,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + InfluxDB2 *InfluxDB2 `description:"InfluxDB v2 metrics exporter type." json:"influxDB2,omitempty" toml:"influxDB2,omitempty" yaml:"influxDB2,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // Prometheus can contain specific configuration used by the Prometheus Metrics exporter. @@ -105,6 +106,27 @@ func (i *InfluxDB) SetDefaults() { i.AddServicesLabels = true } +// InfluxDB2 contains address, token and metrics pushing interval configuration. +type InfluxDB2 struct { + Address string `description:"InfluxDB v2 address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"` + Token string `description:"InfluxDB v2 access token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` + PushInterval types.Duration `description:"InfluxDB v2 push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"` + Org string `description:"InfluxDB v2 org ID." json:"org,omitempty" toml:"org,omitempty" yaml:"org,omitempty" export:"true"` + Bucket string `description:"InfluxDB v2 bucket ID." json:"bucket,omitempty" toml:"bucket,omitempty" yaml:"bucket,omitempty" export:"true"` + AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"` + AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"` + AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"` + AdditionalLabels map[string]string `description:"Additional labels (influxdb tags) on all metrics" json:"additionalLabels,omitempty" toml:"additionalLabels,omitEmpty" yaml:"additionalLabels,omitEmpty" export:"true"` +} + +// SetDefaults sets the default values. +func (i *InfluxDB2) SetDefaults() { + i.Address = "http://localhost:8086" + i.PushInterval = types.Duration(10 * time.Second) + i.AddEntryPointsLabels = true + i.AddServicesLabels = true +} + // Statistics provides options for monitoring request and response stats. type Statistics struct { RecentErrors int `description:"Number of recent errors logged." json:"recentErrors,omitempty" toml:"recentErrors,omitempty" yaml:"recentErrors,omitempty" export:"true"`