diff --git a/docs/content/observability/metrics/influxdb.md b/docs/content/observability/metrics/influxdb.md index b7ffee00b..447379821 100644 --- a/docs/content/observability/metrics/influxdb.md +++ b/docs/content/observability/metrics/influxdb.md @@ -235,3 +235,29 @@ metrics: ```bash tab="CLI" --metrics.influxdb.pushInterval=10s ``` + +#### `additionalLabels` + +_Optional, Default={}_ + +Additional labels (influxdb tags) on all metrics. + +```toml tab="File (TOML)" +[metrics] + [metrics.influxDB] + [metrics.influxDB.additionalLabels] + host = "example.com" + environment = "production" +``` + +```yaml tab="File (YAML)" +metrics: + influxDB: + additionalLabels: + host: example.com + environment: production +``` + +```bash tab="CLI" +--metrics.influxdb.additionallabels.host=example.com --metrics.influxdb.additionallabels.environment=production +``` diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index 5b9a2b87b..f112a143c 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -249,6 +249,9 @@ InfluxDB metrics exporter type. (Default: ```false```) `--metrics.influxdb.addentrypointslabels`: Enable metrics on entry points. (Default: ```true```) +`--metrics.influxdb.additionallabels.`: +Additional labels (influxdb tags) on all metrics + `--metrics.influxdb.address`: InfluxDB address. (Default: ```localhost:8089```) diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index d955cebbf..2bd2af846 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -249,6 +249,9 @@ InfluxDB metrics exporter type. (Default: ```false```) `TRAEFIK_METRICS_INFLUXDB_ADDENTRYPOINTSLABELS`: Enable metrics on entry points. (Default: ```true```) +`TRAEFIK_METRICS_INFLUXDB_ADDITIONALLABELS_`: +Additional labels (influxdb tags) on all metrics + `TRAEFIK_METRICS_INFLUXDB_ADDRESS`: InfluxDB address. (Default: ```localhost:8089```) diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index fed9cca5e..d197feab4 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -268,6 +268,8 @@ addEntryPointsLabels = true addRoutersLabels = true addServicesLabels = true + [metrics.influxDB.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 9b34d1bac..c6c607137 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -290,6 +290,8 @@ metrics: addEntryPointsLabels: true addRoutersLabels: true addServicesLabels: true + additionalLabels: + foobar: foobar ping: entryPoint: foobar manualRouting: true diff --git a/pkg/metrics/influxdb.go b/pkg/metrics/influxdb.go index 820d012ae..6fc50b1ce 100644 --- a/pkg/metrics/influxdb.go +++ b/pkg/metrics/influxdb.go @@ -16,14 +16,10 @@ import ( "github.com/traefik/traefik/v2/pkg/types" ) -var influxDBClient *influx.Influx - -type influxDBWriter struct { - buf bytes.Buffer - config *types.InfluxDB -} - -var influxDBTicker *time.Ticker +var ( + influxDBClient *influx.Influx + influxDBTicker *time.Ticker +) const ( influxDBConfigReloadsName = "traefik.config.reload.total" @@ -134,7 +130,7 @@ func initInfluxDBClient(ctx context.Context, config *types.InfluxDB) *influx.Inf } return influx.New( - map[string]string{}, + config.AdditionalLabels, influxdb.BatchPointsConfig{ Database: config.Database, RetentionPolicy: config.RetentionPolicy, @@ -165,6 +161,11 @@ func StopInfluxDB() { influxDBTicker = nil } +type influxDBWriter struct { + buf bytes.Buffer + config *types.InfluxDB +} + // Write creates a http or udp client and attempts to write BatchPoints. // If a "database not found" error is encountered, a CREATE DATABASE // query is attempted when using protocol http. diff --git a/pkg/metrics/influxdb_test.go b/pkg/metrics/influxdb_test.go index 125e5289c..691452632 100644 --- a/pkg/metrics/influxdb_test.go +++ b/pkg/metrics/influxdb_test.go @@ -21,7 +21,16 @@ func TestInfluxDB(t *testing.T) { // This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond udp.Timeout = 5 * time.Second - influxDBRegistry := RegisterInfluxDB(context.Background(), &types.InfluxDB{Address: ":8089", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true}) + influxDBClient = nil + influxDBRegistry := RegisterInfluxDB(context.Background(), + &types.InfluxDB{ + Address: ":8089", + PushInterval: ptypes.Duration(time.Second), + AddEntryPointsLabels: true, + AddRoutersLabels: true, + AddServicesLabels: true, + AdditionalLabels: map[string]string{"tag1": "val1"}, + }) defer StopInfluxDB() if !influxDBRegistry.IsEpEnabled() || !influxDBRegistry.IsRouterEnabled() || !influxDBRegistry.IsSvcEnabled() { @@ -29,10 +38,10 @@ func TestInfluxDB(t *testing.T) { } 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}`, + `(traefik\.config\.reload\.total,tag1=val1 count=1) [\d]{19}`, + `(traefik\.config\.reload\.total\.failure,tag1=val1 count=1) [\d]{19}`, + `(traefik\.config\.reload\.lastSuccessTimestamp,tag1=val1 value=1) [\d]{19}`, + `(traefik\.config\.reload\.lastFailureTimestamp,tag1=val1 value=1) [\d]{19}`, } msgServer := udp.ReceiveString(t, func() { @@ -45,7 +54,7 @@ func TestInfluxDB(t *testing.T) { assertMessage(t, msgServer, expectedServer) expectedTLS := []string{ - `(traefik\.tls\.certs\.notAfterTimestamp,key=value value=1) [\d]{19}`, + `(traefik\.tls\.certs\.notAfterTimestamp,key=value,tag1=val1 value=1) [\d]{19}`, } msgTLS := udp.ReceiveString(t, func() { @@ -55,10 +64,10 @@ func TestInfluxDB(t *testing.T) { 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}`, + `(traefik\.entrypoint\.requests\.total,code=200,entrypoint=test,method=GET,tag1=val1 count=1) [\d]{19}`, + `(traefik\.entrypoint\.requests\.tls\.total,entrypoint=test,tag1=val1,tls_cipher=bar,tls_version=foo count=1) [\d]{19}`, + `(traefik\.entrypoint\.request\.duration(?:,code=[\d]{3})?,entrypoint=test,tag1=val1 p50=10000,p90=10000,p95=10000,p99=10000) [\d]{19}`, + `(traefik\.entrypoint\.connections\.open,entrypoint=test,tag1=val1 value=1) [\d]{19}`, } msgEntrypoint := udp.ReceiveString(t, func() { @@ -71,11 +80,11 @@ func TestInfluxDB(t *testing.T) { 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}`, + `(traefik\.router\.requests\.total,code=200,method=GET,router=demo,service=test,tag1=val1 count=1) [\d]{19}`, + `(traefik\.router\.requests\.total,code=404,method=GET,router=demo,service=test,tag1=val1 count=1) [\d]{19}`, + `(traefik\.router\.requests\.tls\.total,router=demo,service=test,tag1=val1,tls_cipher=bar,tls_version=foo count=1) [\d]{19}`, + `(traefik\.router\.request\.duration,code=200,router=demo,service=test,tag1=val1 p50=10000,p90=10000,p95=10000,p99=10000) [\d]{19}`, + `(traefik\.router\.connections\.open,router=demo,service=test,tag1=val1 value=1) [\d]{19}`, } msgRouter := udp.ReceiveString(t, func() { @@ -89,13 +98,13 @@ func TestInfluxDB(t *testing.T) { 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\.retries\.total(?:,code=[\d]{3},method=GET)?,service=test count=2) [\d]{19}`, - `(traefik\.service\.server\.up,service=test,url=http://127.0.0.1 value=1) [\d]{19}`, - `(traefik\.service\.connections\.open,service=test value=1) [\d]{19}`, + `(traefik\.service\.requests\.total,code=200,method=GET,service=test,tag1=val1 count=1) [\d]{19}`, + `(traefik\.service\.requests\.total,code=404,method=GET,service=test,tag1=val1 count=1) [\d]{19}`, + `(traefik\.service\.requests\.tls\.total,service=test,tag1=val1,tls_cipher=bar,tls_version=foo count=1) [\d]{19}`, + `(traefik\.service\.request\.duration,code=200,service=test,tag1=val1 p50=10000,p90=10000,p95=10000,p99=10000) [\d]{19}`, + `(traefik\.service\.retries\.total(?:,code=[\d]{3},method=GET)?,service=test,tag1=val1 count=2) [\d]{19}`, + `(traefik\.service\.server\.up,service=test,tag1=val1,url=http://127.0.0.1 value=1) [\d]{19}`, + `(traefik\.service\.connections\.open,service=test,tag1=val1 value=1) [\d]{19}`, } msgService := udp.ReceiveString(t, func() { @@ -126,7 +135,18 @@ func TestInfluxDBHTTP(t *testing.T) { })) defer ts.Close() - influxDBRegistry := RegisterInfluxDB(context.Background(), &types.InfluxDB{Address: ts.URL, Protocol: "http", PushInterval: ptypes.Duration(time.Second), Database: "test", RetentionPolicy: "autogen", AddEntryPointsLabels: true, AddServicesLabels: true, AddRoutersLabels: true}) + influxDBClient = nil + influxDBRegistry := RegisterInfluxDB(context.Background(), + &types.InfluxDB{ + Address: ts.URL, + Protocol: "http", + PushInterval: ptypes.Duration(time.Second), + Database: "test", + RetentionPolicy: "autogen", + AddEntryPointsLabels: true, + AddServicesLabels: true, + AddRoutersLabels: true, + }) defer StopInfluxDB() if !influxDBRegistry.IsEpEnabled() || !influxDBRegistry.IsRouterEnabled() || !influxDBRegistry.IsSvcEnabled() { diff --git a/pkg/types/metrics.go b/pkg/types/metrics.go index 54b4f5bf3..888432803 100644 --- a/pkg/types/metrics.go +++ b/pkg/types/metrics.go @@ -81,16 +81,17 @@ func (s *Statsd) SetDefaults() { // InfluxDB contains address, login and metrics pushing interval configuration. type InfluxDB struct { - Address string `description:"InfluxDB address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"` - Protocol string `description:"InfluxDB address protocol (udp or http)." json:"protocol,omitempty" toml:"protocol,omitempty" yaml:"protocol,omitempty"` - PushInterval types.Duration `description:"InfluxDB push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"` - Database string `description:"InfluxDB database used when protocol is http." json:"database,omitempty" toml:"database,omitempty" yaml:"database,omitempty" export:"true"` - RetentionPolicy string `description:"InfluxDB retention policy used when protocol is http." json:"retentionPolicy,omitempty" toml:"retentionPolicy,omitempty" yaml:"retentionPolicy,omitempty" export:"true"` - Username string `description:"InfluxDB username (only with http)." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"` - Password string `description:"InfluxDB password (only with http)." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"` - 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"` + Address string `description:"InfluxDB address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"` + Protocol string `description:"InfluxDB address protocol (udp or http)." json:"protocol,omitempty" toml:"protocol,omitempty" yaml:"protocol,omitempty"` + PushInterval types.Duration `description:"InfluxDB push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"` + Database string `description:"InfluxDB database used when protocol is http." json:"database,omitempty" toml:"database,omitempty" yaml:"database,omitempty" export:"true"` + RetentionPolicy string `description:"InfluxDB retention policy used when protocol is http." json:"retentionPolicy,omitempty" toml:"retentionPolicy,omitempty" yaml:"retentionPolicy,omitempty" export:"true"` + Username string `description:"InfluxDB username (only with http)." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"` + Password string `description:"InfluxDB password (only with http)." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"` + 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.