diff --git a/docs/content/observability/tracing/opentelemetry.md b/docs/content/observability/tracing/opentelemetry.md index 107cbe2d2..3844882c3 100644 --- a/docs/content/observability/tracing/opentelemetry.md +++ b/docs/content/observability/tracing/opentelemetry.md @@ -5,6 +5,8 @@ description: "Traefik supports several tracing backends, including OpenTelemetry # OpenTelemetry +Traefik Proxy follows [official OpenTelemetry semantic conventions v1.26.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md). + To enable the OpenTelemetry tracer: ```yaml tab="File (YAML)" diff --git a/docs/content/observability/tracing/overview.md b/docs/content/observability/tracing/overview.md index e81f63edf..fcea5c548 100644 --- a/docs/content/observability/tracing/overview.md +++ b/docs/content/observability/tracing/overview.md @@ -160,3 +160,28 @@ tracing: ```bash tab="CLI" --tracing.capturedResponseHeaders[0]=X-CustomHeader ``` + +#### `safeQueryParams` + +_Optional, Default={}_ + +By default, all query parameters are redacted. +Defines the list of query parameters to not redact. + +```yaml tab="File (YAML)" +tracing: + otlp: + safeQueryParams: + - bar + - buz +``` + +```toml tab="File (TOML)" +[tracing] + [tracing.otlp] + safeQueryParams = ["bar", "buz"] +``` + +```bash tab="CLI" +--tracing.otlp.safeQueryParams=bar,buz +``` diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index c72bced81..a261ffa27 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -1125,6 +1125,9 @@ TLS insecure skip verify (Default: ```false```) `--tracing.otlp.http.tls.key`: TLS key +`--tracing.safequeryparams`: +Query params to not redact. + `--tracing.samplerate`: Sets the rate between 0.0 and 1.0 of requests to trace. (Default: ```1.000000```) diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 2f20c7946..43882e52f 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -1125,6 +1125,9 @@ TLS insecure skip verify (Default: ```false```) `TRAEFIK_TRACING_OTLP_HTTP_TLS_KEY`: TLS key +`TRAEFIK_TRACING_SAFEQUERYPARAMS`: +Query params to not redact. + `TRAEFIK_TRACING_SAMPLERATE`: Sets the rate between 0.0 and 1.0 of requests to trace. (Default: ```1.000000```) diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index b173f8eba..f171b9e86 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -392,6 +392,7 @@ serviceName = "foobar" capturedRequestHeaders = ["foobar", "foobar"] capturedResponseHeaders = ["foobar", "foobar"] + safeQueryParams = ["foobar", "foobar"] sampleRate = 42.0 addInternals = true [tracing.globalAttributes] diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index bc9343655..1c053dfaf 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -434,6 +434,9 @@ tracing: capturedResponseHeaders: - foobar - foobar + safeQueryParams: + - foobar + - foobar sampleRate: 42 addInternals: true otlp: diff --git a/go.mod b/go.mod index 9b12bf084..7a73b5194 100644 --- a/go.mod +++ b/go.mod @@ -70,18 +70,18 @@ require ( github.com/unrolled/secure v1.0.9 github.com/vulcand/oxy/v2 v2.0.0-20230427132221-be5cf38f3c1c github.com/vulcand/predicate v1.2.0 - go.opentelemetry.io/collector/pdata v1.2.0 + go.opentelemetry.io/collector/pdata v1.10.0 go.opentelemetry.io/contrib/propagators/autoprop v0.52.0 - go.opentelemetry.io/otel v1.27.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 - go.opentelemetry.io/otel/metric v1.27.0 - go.opentelemetry.io/otel/sdk v1.27.0 - go.opentelemetry.io/otel/sdk/metric v1.27.0 - go.opentelemetry.io/otel/trace v1.27.0 + go.opentelemetry.io/otel v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. + go.opentelemetry.io/otel/metric v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. + go.opentelemetry.io/otel/sdk v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. + go.opentelemetry.io/otel/sdk/metric v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. + go.opentelemetry.io/otel/trace v1.27.1-0.20240624175855-921eb701b175 // For security reason we need to follow semconv v1.26.0 and we can't wait for opentelemetry-go-sdk v1.28.0. golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f golang.org/x/mod v0.18.0 golang.org/x/net v0.26.0 @@ -180,7 +180,7 @@ require ( github.com/go-errors/errors v1.0.1 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect @@ -329,7 +329,7 @@ require ( go.opentelemetry.io/contrib/propagators/b3 v1.27.0 // indirect go.opentelemetry.io/contrib/propagators/jaeger v1.27.0 // indirect go.opentelemetry.io/contrib/propagators/ot v1.27.0 // indirect - go.opentelemetry.io/proto/otlp v1.2.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -342,9 +342,9 @@ require ( golang.org/x/term v0.21.0 // indirect google.golang.org/api v0.172.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect - google.golang.org/protobuf v1.34.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/h2non/gock.v1 v1.0.16 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 0112e07e3..6d843c1cf 100644 --- a/go.sum +++ b/go.sum @@ -348,8 +348,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -1182,8 +1182,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/collector/pdata v1.2.0 h1:N6VdyEFYJyoHIKqHd0F372eNVD5b+AbH0ZQf7Z2jJ9I= -go.opentelemetry.io/collector/pdata v1.2.0/go.mod h1:mKXb6527Syb8PT4P9CZOJNbkuHOHjjGTZNNwSKESJhc= +go.opentelemetry.io/collector/pdata v1.10.0 h1:oLyPLGvPTQrcRT64ZVruwvmH/u3SHTfNo01pteS4WOE= +go.opentelemetry.io/collector/pdata v1.10.0/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/propagators/autoprop v0.52.0 h1:xyRih6jMB0vroMSRdBE+uyKx20BclB/bybJt/LaCxmY= @@ -1196,29 +1196,29 @@ go.opentelemetry.io/contrib/propagators/jaeger v1.27.0 h1:tJPpZAEsihJgRTnXrPjY3r go.opentelemetry.io/contrib/propagators/jaeger v1.27.0/go.mod h1:5uPAMHJnlTktQbCCdWSX5PfK8CocD25mycIsZV/iFiU= go.opentelemetry.io/contrib/propagators/ot v1.27.0 h1:xFPqk7ntRR87dqvl6RfeHiq9UlE8mPSuL6Dtr/zysL8= go.opentelemetry.io/contrib/propagators/ot v1.27.0/go.mod h1:nVLTPrDlSZPoVdeWRmpWBwxA73TYL6XLkC4bj72jvmg= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= -go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= -go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/otel v1.27.1-0.20240624175855-921eb701b175 h1:2aQYN3ZllvztGtSIFNT2xGMrwsbPkGXj0O+KgPVsq5A= +go.opentelemetry.io/otel v1.27.1-0.20240624175855-921eb701b175/go.mod h1:sAYY1kDqq6Qgmu9OdUrtIhBJAVKevM27kmSzEkEyyBg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.1-0.20240624175855-921eb701b175 h1:awu+mcY8Zh17gkaxch+AY59ZEeZHsct61kce/JUa/Ho= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.1-0.20240624175855-921eb701b175/go.mod h1:wwHMT54YgLNroOkp4+XK7PRqCez5DtQayTBR9Wf1CEA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.1-0.20240624175855-921eb701b175 h1:nNu+y3xs12BTRqNVciOBdq1bwUpqHJFs26lhXXnO8/0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.1-0.20240624175855-921eb701b175/go.mod h1:/TESptXGTu8vZX8i19t1TSU4PGo9+gT9pGK/UNAea9I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.1-0.20240624175855-921eb701b175 h1:ihrS8VlMvzPDy1QJ9yIsrKpcso6XnADVrj33wWxRG6k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.1-0.20240624175855-921eb701b175/go.mod h1:O6p37S657iklDAWQZrzkHeyOooL0TRG7ADP6jZ1RpF8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.1-0.20240624175855-921eb701b175 h1:qk6WZWPTf1MDvgLRCECyzbUzugNp2Ybc11fnrj6L/t4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.1-0.20240624175855-921eb701b175/go.mod h1:A5CvQA6TBgKbMXLU3YrV9WB2m5xQYB50/PLkW1nJA8k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.1-0.20240624175855-921eb701b175 h1:l47wCNFvbEdVi0U3Ojf5yD6QhrNjuK2N46N9tiCzpNE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.1-0.20240624175855-921eb701b175/go.mod h1:vS4rxlVsjAdnvN34PmMKVPKZdQrKVxRw6Tz6qQ+NsY8= +go.opentelemetry.io/otel/metric v1.27.1-0.20240624175855-921eb701b175 h1:YGLAewt2Wr/DFRQI6OjqqZ31B+CO3W1bcDWhsIL7r98= +go.opentelemetry.io/otel/metric v1.27.1-0.20240624175855-921eb701b175/go.mod h1:YHVa4iPQFl9x0kF/Pxk1MYAbroRbl7zK+vT34w7VsCA= +go.opentelemetry.io/otel/sdk v1.27.1-0.20240624175855-921eb701b175 h1:QdXgh+39hJwRYYQ3OgcI4SMawt/rPBgmQdtZYGxzdq8= +go.opentelemetry.io/otel/sdk v1.27.1-0.20240624175855-921eb701b175/go.mod h1:Ru3JMbgJMftQARx7nhi0IJotnL2n8bv61IOrAgFpd+s= +go.opentelemetry.io/otel/sdk/metric v1.27.1-0.20240624175855-921eb701b175 h1:GYxFdqdupCe5BEORUuWvNMRKeZliRLK8INKCgGzYLRA= +go.opentelemetry.io/otel/sdk/metric v1.27.1-0.20240624175855-921eb701b175/go.mod h1:SRwqsyIihvNIPKOVe1CqC4owsJX2vv63U5op9Axd4fI= +go.opentelemetry.io/otel/trace v1.27.1-0.20240624175855-921eb701b175 h1:1y2KlY7+gTBn4GZa2yMKqgcb4H2KIOrBSSrG4esCC3g= +go.opentelemetry.io/otel/trace v1.27.1-0.20240624175855-921eb701b175/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= -go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1547,10 +1547,10 @@ google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d h1:Aqf0fiIdUQEj0Gn9mKFFXoQfTTEaNopWpfVyYADxiSg= +google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Od4k8V1LQSizPRUK4OzZ7TBE/20k+jPczUDAEyvn69Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1588,8 +1588,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/integration/resources/compose/tracing.yml b/integration/resources/compose/tracing.yml index 6f6c15526..7dc2438d6 100644 --- a/integration/resources/compose/tracing.yml +++ b/integration/resources/compose/tracing.yml @@ -7,7 +7,7 @@ services: volumes: - ./fixtures/tracing/tempo.yaml:/etc/tempo.yaml otel-collector: - image: otel/opentelemetry-collector-contrib:0.89.0 + image: otel/opentelemetry-collector-contrib:0.103.0 volumes: - ./fixtures/tracing/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml whoami: diff --git a/integration/tracing_test.go b/integration/tracing_test.go index aa5500108..4849034b8 100644 --- a/integration/tracing_test.go +++ b/integration/tracing_test.go @@ -2,7 +2,9 @@ package integration import ( "encoding/json" + "fmt" "io" + "net" "net/http" "net/url" "os" @@ -96,27 +98,46 @@ func (s *TracingSuite) TestOpentelemetryBasic_HTTP() { { "batches.0.scopeSpans.0.scope.name": "github.com/traefik/traefik", - "batches.0.scopeSpans.0.spans.0.name": "EntryPoint", - "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_SERVER", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"entry_point\").value.stringValue": "web", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.path\").value.stringValue": "/basic", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "200", + "batches.0.scopeSpans.0.spans.0.name": "ReverseProxy", + "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_CLIENT", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.protocol.version\").value.stringValue": "1.1", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.full\").value.stringValue": fmt.Sprintf("http://%s/basic", net.JoinHostPort(s.whoamiIP, "80")), + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.port\").value.intValue": "80", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.port\").value.intValue": "80", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - "batches.0.scopeSpans.0.spans.1.name": "Router", - "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "router0@file", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.service.name\").value.stringValue": "service0@file", + "batches.0.scopeSpans.0.spans.1.name": "Metrics", + "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-service", "batches.0.scopeSpans.0.spans.2.name": "Service", "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.service.name\").value.stringValue": "service0@file", - "batches.0.scopeSpans.0.spans.3.name": "ReverseProxy", - "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_CLIENT", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"url.scheme\").value.stringValue": "http", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.3.name": "Router", + "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.service.name\").value.stringValue": "service0@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.router.name\").value.stringValue": "router0@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.route\").value.stringValue": "Path(`/basic`)", + + "batches.0.scopeSpans.0.spans.4.name": "Metrics", + "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", + + "batches.0.scopeSpans.0.spans.5.name": "EntryPoint", + "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_SERVER", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"entry_point\").value.stringValue": "web", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"url.path\").value.stringValue": "/basic", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"url.query\").value.stringValue": "", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"server.address\").value.stringValue": "127.0.0.1:8000", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"network.peer.address\").value.stringValue": "127.0.0.1", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"http.response.status_code\").value.intValue": "200", }, } @@ -145,27 +166,35 @@ func (s *TracingSuite) TestOpentelemetryBasic_gRPC() { { "batches.0.scopeSpans.0.scope.name": "github.com/traefik/traefik", - "batches.0.scopeSpans.0.spans.0.name": "EntryPoint", - "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_SERVER", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"entry_point\").value.stringValue": "web", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.path\").value.stringValue": "/basic", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "200", + "batches.0.scopeSpans.0.spans.0.name": "ReverseProxy", + "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_CLIENT", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.protocol.version\").value.stringValue": "1.1", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.full\").value.stringValue": fmt.Sprintf("http://%s/basic", net.JoinHostPort(s.whoamiIP, "80")), + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.port\").value.intValue": "80", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.port\").value.intValue": "80", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - "batches.0.scopeSpans.0.spans.1.name": "Router", - "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "router0@file", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.service.name\").value.stringValue": "service0@file", + "batches.0.scopeSpans.0.spans.1.name": "Metrics", + "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-service", "batches.0.scopeSpans.0.spans.2.name": "Service", "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.service.name\").value.stringValue": "service0@file", - "batches.0.scopeSpans.0.spans.3.name": "ReverseProxy", - "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_CLIENT", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"url.scheme\").value.stringValue": "http", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.3.name": "Router", + "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.service.name\").value.stringValue": "service0@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.router.name\").value.stringValue": "router0@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.route\").value.stringValue": "Path(`/basic`)", + + "batches.0.scopeSpans.0.spans.4.name": "Metrics", + "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", }, } @@ -215,54 +244,75 @@ func (s *TracingSuite) TestOpentelemetryRateLimit() { { "batches.0.scopeSpans.0.scope.name": "github.com/traefik/traefik", - "batches.0.scopeSpans.0.spans.0.name": "EntryPoint", - "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_SERVER", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"entry_point\").value.stringValue": "web", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.path\").value.stringValue": "/ratelimit", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "200", + "batches.0.scopeSpans.0.spans.0.name": "RateLimiter", + "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "ratelimit-1@file", - "batches.0.scopeSpans.0.spans.1.name": "Router", - "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "router1@file", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.service.name\").value.stringValue": "service1@file", + "batches.0.scopeSpans.0.spans.1.name": "Retry", + "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", - "batches.0.scopeSpans.0.spans.2.name": "Retry", + "batches.0.scopeSpans.0.spans.2.name": "RateLimiter", "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "ratelimit-1@file", - "batches.0.scopeSpans.0.spans.3.name": "RateLimiter", + "batches.0.scopeSpans.0.spans.3.name": "Retry", "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "ratelimit-1@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.request.resend_count\").value.intValue": "1", - "batches.0.scopeSpans.0.spans.4.name": "Service", + "batches.0.scopeSpans.0.spans.4.name": "RateLimiter", "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.service.name\").value.stringValue": "service1@file", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "ratelimit-1@file", - "batches.0.scopeSpans.0.spans.5.name": "ReverseProxy", - "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_CLIENT", - "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"url.scheme\").value.stringValue": "http", - "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.5.name": "Retry", + "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"http.request.resend_count\").value.intValue": "2", + + "batches.0.scopeSpans.0.spans.6.name": "Router", + "batches.0.scopeSpans.0.spans.6.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"traefik.service.name\").value.stringValue": "service1@file", + "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"traefik.router.name\").value.stringValue": "router1@file", + "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"http.route\").value.stringValue": "Path(`/ratelimit`)", + + "batches.0.scopeSpans.0.spans.7.name": "Metrics", + "batches.0.scopeSpans.0.spans.7.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", + + "batches.0.scopeSpans.0.spans.8.name": "EntryPoint", + "batches.0.scopeSpans.0.spans.8.kind": "SPAN_KIND_SERVER", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"entry_point\").value.stringValue": "web", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"url.path\").value.stringValue": "/ratelimit", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"url.query\").value.stringValue": "", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"server.address\").value.stringValue": "127.0.0.1:8000", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"network.peer.address\").value.stringValue": "127.0.0.1", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"http.response.status_code\").value.intValue": "429", }, { "batches.0.scopeSpans.0.scope.name": "github.com/traefik/traefik", - "batches.0.scopeSpans.0.spans.0.name": "EntryPoint", - "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_SERVER", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"entry_point\").value.stringValue": "web", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.path\").value.stringValue": "/ratelimit", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "429", + "batches.0.scopeSpans.0.spans.0.name": "ReverseProxy", + "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_CLIENT", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.protocol.version\").value.stringValue": "1.1", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.full\").value.stringValue": fmt.Sprintf("http://%s/ratelimit", net.JoinHostPort(s.whoamiIP, "80")), + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.port\").value.intValue": "80", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.port\").value.intValue": "80", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - "batches.0.scopeSpans.0.spans.1.name": "Router", - "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "router1@file", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.service.name\").value.stringValue": "service1@file", + "batches.0.scopeSpans.0.spans.1.name": "Metrics", + "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-service", - "batches.0.scopeSpans.0.spans.2.name": "Retry", + "batches.0.scopeSpans.0.spans.2.name": "Service", "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.service.name\").value.stringValue": "service1@file", "batches.0.scopeSpans.0.spans.3.name": "RateLimiter", "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", @@ -271,20 +321,27 @@ func (s *TracingSuite) TestOpentelemetryRateLimit() { "batches.0.scopeSpans.0.spans.4.name": "Retry", "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", - "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"http.resend_count\").value.intValue": "1", - "batches.0.scopeSpans.0.spans.5.name": "RateLimiter", + "batches.0.scopeSpans.0.spans.5.name": "Router", "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "ratelimit-1@file", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.service.name\").value.stringValue": "service1@file", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.router.name\").value.stringValue": "router1@file", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"http.route\").value.stringValue": "Path(`/ratelimit`)", - "batches.0.scopeSpans.0.spans.6.name": "Retry", + "batches.0.scopeSpans.0.spans.6.name": "Metrics", "batches.0.scopeSpans.0.spans.6.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", - "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"http.resend_count\").value.intValue": "2", + "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", - "batches.0.scopeSpans.0.spans.7.name": "RateLimiter", - "batches.0.scopeSpans.0.spans.7.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "ratelimit-1@file", + "batches.0.scopeSpans.0.spans.7.name": "EntryPoint", + "batches.0.scopeSpans.0.spans.7.kind": "SPAN_KIND_SERVER", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"entry_point\").value.stringValue": "web", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"url.path\").value.stringValue": "/ratelimit", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"url.query\").value.stringValue": "", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"server.address\").value.stringValue": "127.0.0.1:8000", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"network.peer.address\").value.stringValue": "127.0.0.1", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"http.response.status_code\").value.intValue": "200", }, } @@ -312,60 +369,102 @@ func (s *TracingSuite) TestOpentelemetryRetry() { { "batches.0.scopeSpans.0.scope.name": "github.com/traefik/traefik", - "batches.0.scopeSpans.0.spans.0.name": "EntryPoint", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.path\").value.stringValue": "/retry", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "502", - "batches.0.scopeSpans.0.spans.0.status.code": "STATUS_CODE_ERROR", + "batches.0.scopeSpans.0.spans.0.name": "ReverseProxy", + "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_CLIENT", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.protocol.version\").value.stringValue": "1.1", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.full\").value.stringValue": fmt.Sprintf("http://%s/retry", net.JoinHostPort(s.whoamiIP, "81")), + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.port\").value.intValue": "81", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.port\").value.intValue": "81", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "502", + "batches.0.scopeSpans.0.spans.0.status.code": "STATUS_CODE_ERROR", - "batches.0.scopeSpans.0.spans.1.name": "Router", + "batches.0.scopeSpans.0.spans.1.name": "Metrics", "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.service.name\").value.stringValue": "service2@file", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "router2@file", + "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-service", - "batches.0.scopeSpans.0.spans.2.name": "Retry", + "batches.0.scopeSpans.0.spans.2.name": "Service", "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.service.name\").value.stringValue": "service2@file", - "batches.0.scopeSpans.0.spans.3.name": "Service", + "batches.0.scopeSpans.0.spans.3.name": "Retry", "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.service.name\").value.stringValue": "service2@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", - "batches.0.scopeSpans.0.spans.4.name": "ReverseProxy", - "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_CLIENT", - "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"url.scheme\").value.stringValue": "http", - "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"http.response.status_code\").value.intValue": "502", - "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.4.name": "ReverseProxy", + "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_CLIENT", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"network.protocol.version\").value.stringValue": "1.1", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"url.full\").value.stringValue": fmt.Sprintf("http://%s/retry", net.JoinHostPort(s.whoamiIP, "81")), + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"network.peer.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"network.peer.port\").value.intValue": "81", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"server.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"server.port\").value.intValue": "81", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"http.response.status_code\").value.intValue": "502", + "batches.0.scopeSpans.0.spans.4.status.code": "STATUS_CODE_ERROR", - "batches.0.scopeSpans.0.spans.5.name": "Retry", + "batches.0.scopeSpans.0.spans.5.name": "Metrics", "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", - "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"http.resend_count\").value.intValue": "1", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-service", "batches.0.scopeSpans.0.spans.6.name": "Service", "batches.0.scopeSpans.0.spans.6.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"traefik.service.name\").value.stringValue": "service2@file", - "batches.0.scopeSpans.0.spans.7.name": "ReverseProxy", - "batches.0.scopeSpans.0.spans.7.kind": "SPAN_KIND_CLIENT", - "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"url.scheme\").value.stringValue": "http", - "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"http.response.status_code\").value.intValue": "502", - "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.7.name": "Retry", + "batches.0.scopeSpans.0.spans.7.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"http.request.resend_count\").value.intValue": "1", - "batches.0.scopeSpans.0.spans.8.name": "Retry", - "batches.0.scopeSpans.0.spans.8.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", - "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"http.resend_count\").value.intValue": "2", + "batches.0.scopeSpans.0.spans.8.name": "ReverseProxy", + "batches.0.scopeSpans.0.spans.8.kind": "SPAN_KIND_CLIENT", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"network.protocol.version\").value.stringValue": "1.1", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"url.full\").value.stringValue": fmt.Sprintf("http://%s/retry", net.JoinHostPort(s.whoamiIP, "81")), + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"network.peer.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"network.peer.port\").value.intValue": "81", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"server.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"server.port\").value.intValue": "81", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"http.response.status_code\").value.intValue": "502", + "batches.0.scopeSpans.0.spans.8.status.code": "STATUS_CODE_ERROR", - "batches.0.scopeSpans.0.spans.9.name": "Service", + "batches.0.scopeSpans.0.spans.9.name": "Metrics", "batches.0.scopeSpans.0.spans.9.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.9.attributes.#(key=\"traefik.service.name\").value.stringValue": "service2@file", + "batches.0.scopeSpans.0.spans.9.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-service", - "batches.0.scopeSpans.0.spans.10.name": "ReverseProxy", - "batches.0.scopeSpans.0.spans.10.kind": "SPAN_KIND_CLIENT", - "batches.0.scopeSpans.0.spans.10.attributes.#(key=\"url.scheme\").value.stringValue": "http", - "batches.0.scopeSpans.0.spans.10.attributes.#(key=\"http.response.status_code\").value.intValue": "502", - "batches.0.scopeSpans.0.spans.10.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.10.name": "Service", + "batches.0.scopeSpans.0.spans.10.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.10.attributes.#(key=\"traefik.service.name\").value.stringValue": "service2@file", + + "batches.0.scopeSpans.0.spans.11.name": "Retry", + "batches.0.scopeSpans.0.spans.11.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.11.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.11.attributes.#(key=\"http.request.resend_count\").value.intValue": "2", + + "batches.0.scopeSpans.0.spans.12.name": "Router", + "batches.0.scopeSpans.0.spans.12.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.12.attributes.#(key=\"traefik.service.name\").value.stringValue": "service2@file", + "batches.0.scopeSpans.0.spans.12.attributes.#(key=\"traefik.router.name\").value.stringValue": "router2@file", + + "batches.0.scopeSpans.0.spans.13.name": "Metrics", + "batches.0.scopeSpans.0.spans.13.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.13.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", + + "batches.0.scopeSpans.0.spans.14.name": "EntryPoint", + "batches.0.scopeSpans.0.spans.14.kind": "SPAN_KIND_SERVER", + "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"entry_point\").value.stringValue": "web", + "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"url.path\").value.stringValue": "/retry", + "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"url.query\").value.stringValue": "", + "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"server.address\").value.stringValue": "127.0.0.1:8000", + "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"network.peer.address\").value.stringValue": "127.0.0.1", + "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"http.response.status_code\").value.intValue": "502", }, } @@ -393,21 +492,131 @@ func (s *TracingSuite) TestOpentelemetryAuth() { { "batches.0.scopeSpans.0.scope.name": "github.com/traefik/traefik", - "batches.0.scopeSpans.0.spans.0.name": "EntryPoint", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.path\").value.stringValue": "/auth", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "401", + "batches.0.scopeSpans.0.spans.0.name": "BasicAuth", + "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "basic-auth@file", + "batches.0.scopeSpans.0.spans.0.status.message": "Authentication failed", + "batches.0.scopeSpans.0.spans.0.status.code": "STATUS_CODE_ERROR", - "batches.0.scopeSpans.0.spans.1.name": "Router", - "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "router3@file", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.service.name\").value.stringValue": "service3@file", + "batches.0.scopeSpans.0.spans.1.name": "Retry", + "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.2.name": "BasicAuth", "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", - "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "basic-auth@file", + "batches.0.scopeSpans.0.spans.2.status.message": "Authentication failed", + "batches.0.scopeSpans.0.spans.2.status.code": "STATUS_CODE_ERROR", + "batches.0.scopeSpans.0.spans.3.name": "Retry", + "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.request.resend_count\").value.intValue": "1", + + "batches.0.scopeSpans.0.spans.4.name": "BasicAuth", + "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "basic-auth@file", + "batches.0.scopeSpans.0.spans.4.status.message": "Authentication failed", + "batches.0.scopeSpans.0.spans.4.status.code": "STATUS_CODE_ERROR", + + "batches.0.scopeSpans.0.spans.5.name": "Retry", + "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"http.request.resend_count\").value.intValue": "2", + + "batches.0.scopeSpans.0.spans.6.name": "Router", + "batches.0.scopeSpans.0.spans.6.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"traefik.service.name\").value.stringValue": "service3@file", + "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"traefik.router.name\").value.stringValue": "router3@file", + "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"http.route\").value.stringValue": "Path(`/auth`)", + + "batches.0.scopeSpans.0.spans.7.name": "Metrics", + "batches.0.scopeSpans.0.spans.7.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", + + "batches.0.scopeSpans.0.spans.8.name": "EntryPoint", + "batches.0.scopeSpans.0.spans.8.kind": "SPAN_KIND_SERVER", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"entry_point\").value.stringValue": "web", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"url.path\").value.stringValue": "/auth", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"url.query\").value.stringValue": "", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"server.address\").value.stringValue": "127.0.0.1:8000", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"network.peer.address\").value.stringValue": "127.0.0.1", + "batches.0.scopeSpans.0.spans.8.attributes.#(key=\"http.response.status_code\").value.intValue": "401", + }, + } + + s.checkTraceContent(contains) +} + +func (s *TracingSuite) TestOpentelemetrySafeURL() { + file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{ + WhoamiIP: s.whoamiIP, + WhoamiPort: s.whoamiPort, + IP: s.otelCollectorIP, + }) + defer os.Remove(file) + + s.traefikCmd(withConfigFile(file)) + + // wait for traefik + err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth")) + require.NoError(s.T(), err) + + err = try.GetRequest("http://test:test@127.0.0.1:8000/auth?api_key=powpow", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) + require.NoError(s.T(), err) + + contains := []map[string]string{ + { + "batches.0.scopeSpans.0.scope.name": "github.com/traefik/traefik", + + "batches.0.scopeSpans.0.spans.0.name": "ReverseProxy", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.full\").value.stringValue": fmt.Sprintf("http://REDACTED:REDACTED@%s/auth?api_key=REDACTED", net.JoinHostPort(s.whoamiIP, "80")), + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.port\").value.intValue": "80", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.address\").value.stringValue": s.whoamiIP, + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.port\").value.intValue": "80", + "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "200", + + "batches.0.scopeSpans.0.spans.1.name": "Metrics", + "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-service", + + "batches.0.scopeSpans.0.spans.2.name": "Service", + "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.service.name\").value.stringValue": "service3@file", + + "batches.0.scopeSpans.0.spans.3.name": "BasicAuth", "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "basic-auth@file", + + "batches.0.scopeSpans.0.spans.4.name": "Retry", + "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "retry@file", + + "batches.0.scopeSpans.0.spans.5.name": "Router", + "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.service.name\").value.stringValue": "service3@file", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.router.name\").value.stringValue": "router3@file", + "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"http.route\").value.stringValue": "Path(`/auth`)", + + "batches.0.scopeSpans.0.spans.6.name": "Metrics", + "batches.0.scopeSpans.0.spans.6.kind": "SPAN_KIND_INTERNAL", + "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", + + "batches.0.scopeSpans.0.spans.7.name": "EntryPoint", + "batches.0.scopeSpans.0.spans.7.kind": "SPAN_KIND_SERVER", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"entry_point\").value.stringValue": "web", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"http.request.method\").value.stringValue": "GET", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"url.path\").value.stringValue": "/auth", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"url.query\").value.stringValue": "api_key=REDACTED", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"server.address\").value.stringValue": "127.0.0.1:8000", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"network.peer.address\").value.stringValue": "127.0.0.1", + "batches.0.scopeSpans.0.spans.7.attributes.#(key=\"http.response.status_code\").value.intValue": "200", }, } @@ -517,12 +726,22 @@ func (s *TracingSuite) checkTraceContent(expectedJSON []map[string]string) { contents = append(contents, string(content)) } + var missingElements []string for _, expected := range expectedJSON { - containsAll(expected, contents) + missingElements = append(missingElements, contains(expected, contents)...) } + + log.Printf("Contents: [%s]\n", strings.Join(contents, ",")) + for _, element := range missingElements { + log.Printf("Missing elements:\n%s\n", element) + } + + require.Empty(s.T(), missingElements) } -func containsAll(expectedJSON map[string]string, contents []string) { +func contains(expectedJSON map[string]string, contents []string) []string { + var missingElements []string + for k, v := range expectedJSON { found := false for _, content := range contents { @@ -531,12 +750,12 @@ func containsAll(expectedJSON map[string]string, contents []string) { break } } - if !found { - log.Info().Msgf("[" + strings.Join(contents, ",") + "]") - log.Error().Msgf("missing element: \nKey: %q\nValue: %q ", k, v) + missingElements = append(missingElements, "Key: "+k+", Value: "+v) } } + + return missingElements } // TraceResponse contains a list of traces. diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index 52743dde5..c16633a5d 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -201,6 +201,7 @@ type Tracing struct { GlobalAttributes map[string]string `description:"Defines additional attributes (key:value) on all spans." json:"globalAttributes,omitempty" toml:"globalAttributes,omitempty" yaml:"globalAttributes,omitempty" export:"true"` CapturedRequestHeaders []string `description:"Request headers to add as attributes for server and client spans." json:"capturedRequestHeaders,omitempty" toml:"capturedRequestHeaders,omitempty" yaml:"capturedRequestHeaders,omitempty" export:"true"` CapturedResponseHeaders []string `description:"Response headers to add as attributes for server and client spans." json:"capturedResponseHeaders,omitempty" toml:"capturedResponseHeaders,omitempty" yaml:"capturedResponseHeaders,omitempty" export:"true"` + SafeQueryParams []string `description:"Query params to not redact." json:"safeQueryParams,omitempty" toml:"safeQueryParams,omitempty" yaml:"safeQueryParams,omitempty" export:"true"` SampleRate float64 `description:"Sets the rate between 0.0 and 1.0 of requests to trace." json:"sampleRate,omitempty" toml:"sampleRate,omitempty" yaml:"sampleRate,omitempty" export:"true"` AddInternals bool `description:"Enables tracing for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"` diff --git a/pkg/metrics/opentelemetry.go b/pkg/metrics/opentelemetry.go index 23cbf8eba..d34ee41f4 100644 --- a/pkg/metrics/opentelemetry.go +++ b/pkg/metrics/opentelemetry.go @@ -20,7 +20,7 @@ import ( "go.opentelemetry.io/otel/metric" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding/gzip" ) @@ -58,16 +58,16 @@ func NewSemConvMetricRegistry(ctx context.Context, config *types.OTLP) (*SemConv meter := otel.Meter("github.com/traefik/traefik", metric.WithInstrumentationVersion(version.Version)) - httpServerRequestDuration, err := meter.Float64Histogram("http.server.request.duration", - metric.WithDescription("Duration of HTTP server requests."), + httpServerRequestDuration, err := meter.Float64Histogram(semconv.HTTPServerRequestDurationName, + metric.WithDescription(semconv.HTTPServerRequestDurationDescription), metric.WithUnit("s"), metric.WithExplicitBucketBoundaries(config.ExplicitBoundaries...)) if err != nil { return nil, fmt.Errorf("can't build httpServerRequestDuration histogram: %w", err) } - httpClientRequestDuration, err := meter.Float64Histogram("http.client.request.duration", - metric.WithDescription("Duration of HTTP client requests."), + httpClientRequestDuration, err := meter.Float64Histogram(semconv.HTTPClientRequestDurationName, + metric.WithDescription(semconv.HTTPClientRequestDurationDescription), metric.WithUnit("s"), metric.WithExplicitBucketBoundaries(config.ExplicitBoundaries...)) if err != nil { diff --git a/pkg/middlewares/auth/forward_test.go b/pkg/middlewares/auth/forward_test.go index e2f197b98..0b41506bb 100644 --- a/pkg/middlewares/auth/forward_test.go +++ b/pkg/middlewares/auth/forward_test.go @@ -513,7 +513,7 @@ func TestForwardAuthTracing(t *testing.T) { attribute.String("url.scheme", "http"), attribute.String("user_agent.original", ""), attribute.String("network.peer.address", "127.0.0.1"), - attribute.String("network.peer.port", serverPort), + attribute.Int64("network.peer.port", int64(serverPortInt)), attribute.String("server.address", "127.0.0.1"), attribute.Int64("server.port", int64(serverPortInt)), attribute.StringSlice("http.request.header.x-foo", []string{"foo", "bar"}), @@ -546,7 +546,7 @@ func TestForwardAuthTracing(t *testing.T) { otel.SetTextMapPropagator(autoprop.NewTextMapPropagator()) mockTracer := &mockTracer{} - tracer := tracing.NewTracer(mockTracer, []string{"X-Foo"}, []string{"X-Bar"}) + tracer := tracing.NewTracer(mockTracer, []string{"X-Foo"}, []string{"X-Bar"}, []string{"q"}) initialCtx, initialSpan := tracer.Start(req.Context(), "initial") defer initialSpan.End() req = req.WithContext(initialCtx) diff --git a/pkg/middlewares/observability/entrypoint.go b/pkg/middlewares/observability/entrypoint.go index 5de208a25..0de455b92 100644 --- a/pkg/middlewares/observability/entrypoint.go +++ b/pkg/middlewares/observability/entrypoint.go @@ -14,7 +14,7 @@ import ( "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/noop" ) @@ -35,7 +35,7 @@ type entryPointTracing struct { func WrapEntryPointHandler(ctx context.Context, tracer *tracing.Tracer, semConvMetricRegistry *metrics.SemConvMetricsRegistry, entryPointName string) alice.Constructor { return func(next http.Handler) (http.Handler, error) { if tracer == nil { - tracer = tracing.NewTracer(noop.Tracer{}, nil, nil) + tracer = tracing.NewTracer(noop.Tracer{}, nil, nil, nil) } return newEntryPoint(ctx, tracer, semConvMetricRegistry, entryPointName, next), nil @@ -47,7 +47,7 @@ func newEntryPoint(ctx context.Context, tracer *tracing.Tracer, semConvMetricReg middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug().Msg("Creating middleware") if tracer == nil { - tracer = tracing.NewTracer(noop.Tracer{}, nil, nil) + tracer = tracing.NewTracer(noop.Tracer{}, nil, nil, nil) } return &entryPointTracing{ diff --git a/pkg/middlewares/observability/entrypoint_test.go b/pkg/middlewares/observability/entrypoint_test.go index f02180901..f32c052c8 100644 --- a/pkg/middlewares/observability/entrypoint_test.go +++ b/pkg/middlewares/observability/entrypoint_test.go @@ -42,15 +42,14 @@ func TestEntryPointMiddleware_tracing(t *testing.T) { attribute.String("network.protocol.version", "1.1"), attribute.Int64("http.request.body.size", int64(0)), attribute.String("url.path", "/search"), - attribute.String("url.query", "q=Opentelemetry"), + attribute.String("url.query", "q=Opentelemetry&token=REDACTED"), attribute.String("url.scheme", "http"), attribute.String("user_agent.original", "entrypoint-test"), attribute.String("server.address", "www.test.com"), attribute.String("network.peer.address", "10.0.0.1"), - attribute.String("network.peer.port", "1234"), attribute.String("client.address", "10.0.0.1"), attribute.Int64("client.port", int64(1234)), - attribute.String("client.socket.address", ""), + attribute.Int64("network.peer.port", int64(1234)), attribute.StringSlice("http.request.header.x-foo", []string{"foo", "bar"}), attribute.Int64("http.response.status_code", int64(404)), attribute.StringSlice("http.response.header.x-bar", []string{"foo", "bar"}), @@ -61,7 +60,7 @@ func TestEntryPointMiddleware_tracing(t *testing.T) { for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil) + req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry&token=123", nil) rw := httptest.NewRecorder() req.RemoteAddr = "10.0.0.1:1234" req.Header.Set("User-Agent", "entrypoint-test") @@ -77,7 +76,7 @@ func TestEntryPointMiddleware_tracing(t *testing.T) { tracer := &mockTracer{} - handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{"X-Foo"}, []string{"X-Bar"}), nil, test.entryPoint, next) + handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{"X-Foo"}, []string{"X-Bar"}, []string{"q"}), nil, test.entryPoint, next) handler.ServeHTTP(rw, req) for _, span := range tracer.spans { diff --git a/pkg/middlewares/observability/router.go b/pkg/middlewares/observability/router.go index b9e69d78e..5339726ff 100644 --- a/pkg/middlewares/observability/router.go +++ b/pkg/middlewares/observability/router.go @@ -9,7 +9,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" ) diff --git a/pkg/middlewares/retry/retry.go b/pkg/middlewares/retry/retry.go index f8945a989..f462d2d70 100644 --- a/pkg/middlewares/retry/retry.go +++ b/pkg/middlewares/retry/retry.go @@ -16,7 +16,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" ) @@ -95,7 +95,7 @@ func (r *retry) ServeHTTP(rw http.ResponseWriter, req *http.Request) { currentSpan.SetAttributes(attribute.String("traefik.middleware.name", r.name)) // Only add the attribute "http.resend_count" defined by semantic conventions starting from second attempt. if attempts > 1 { - currentSpan.SetAttributes(semconv.HTTPResendCount(attempts - 1)) + currentSpan.SetAttributes(semconv.HTTPRequestResendCount(attempts - 1)) } req = req.WithContext(tracingCtx) diff --git a/pkg/server/service/observability_roundtripper.go b/pkg/server/service/observability_roundtripper.go index ae862deed..c10e028d5 100644 --- a/pkg/server/service/observability_roundtripper.go +++ b/pkg/server/service/observability_roundtripper.go @@ -14,7 +14,7 @@ import ( "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" ) diff --git a/pkg/tracing/opentelemetry/opentelemetry.go b/pkg/tracing/opentelemetry/opentelemetry.go index 35f1a5f6a..cc1533524 100644 --- a/pkg/tracing/opentelemetry/opentelemetry.go +++ b/pkg/tracing/opentelemetry/opentelemetry.go @@ -18,7 +18,7 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding/gzip" diff --git a/pkg/tracing/tracing.go b/pkg/tracing/tracing.go index 8faef513d..1dc2f898d 100644 --- a/pkg/tracing/tracing.go +++ b/pkg/tracing/tracing.go @@ -6,6 +6,8 @@ import ( "io" "net" "net/http" + "net/url" + "slices" "strconv" "strings" @@ -17,7 +19,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" ) @@ -47,7 +49,7 @@ func NewTracing(conf *static.Tracing) (*Tracer, io.Closer, error) { return nil, nil, err } - return NewTracer(tr, conf.CapturedRequestHeaders, conf.CapturedResponseHeaders), closer, nil + return NewTracer(tr, conf.CapturedRequestHeaders, conf.CapturedResponseHeaders, conf.SafeQueryParams), closer, nil } // TracerFromContext extracts the trace.Tracer from the given context. @@ -122,14 +124,16 @@ func (t TracerProvider) Tracer(name string, options ...trace.TracerOption) trace type Tracer struct { trace.Tracer + safeQueryParams []string capturedRequestHeaders []string capturedResponseHeaders []string } // NewTracer builds and configures a new Tracer. -func NewTracer(tracer trace.Tracer, capturedRequestHeaders, capturedResponseHeaders []string) *Tracer { +func NewTracer(tracer trace.Tracer, capturedRequestHeaders, capturedResponseHeaders, safeQueryParams []string) *Tracer { return &Tracer{ Tracer: tracer, + safeQueryParams: safeQueryParams, capturedRequestHeaders: capturedRequestHeaders, capturedResponseHeaders: capturedResponseHeaders, } @@ -153,37 +157,37 @@ func (t *Tracer) Start(ctx context.Context, spanName string, opts ...trace.SpanS } // CaptureClientRequest used to add span attributes from the request as a Client. -// TODO: need to update the semconv package as it does not implement fully Semantic Convention v1.23.0. func (t *Tracer) CaptureClientRequest(span trace.Span, r *http.Request) { if t == nil || span == nil || r == nil { return } - // Common attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md#common-attributes + // Common attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md#common-attributes span.SetAttributes(semconv.HTTPRequestMethodKey.String(r.Method)) span.SetAttributes(semconv.NetworkProtocolVersion(proto(r.Proto))) - // Client attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md#http-client - span.SetAttributes(semconv.URLFull(r.URL.String())) - span.SetAttributes(semconv.URLScheme(r.URL.Scheme)) + // Client attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md#http-client + sURL := t.safeURL(r.URL) + span.SetAttributes(semconv.URLFull(sURL.String())) + span.SetAttributes(semconv.URLScheme(sURL.Scheme)) span.SetAttributes(semconv.UserAgentOriginal(r.UserAgent())) - host, port, err := net.SplitHostPort(r.URL.Host) + host, port, err := net.SplitHostPort(sURL.Host) if err != nil { - span.SetAttributes(attribute.String("network.peer.address", host)) - span.SetAttributes(semconv.ServerAddress(r.URL.Host)) - switch r.URL.Scheme { + span.SetAttributes(semconv.NetworkPeerAddress(host)) + span.SetAttributes(semconv.ServerAddress(sURL.Host)) + switch sURL.Scheme { case "http": - span.SetAttributes(attribute.String("network.peer.port", "80")) + span.SetAttributes(semconv.NetworkPeerPort(80)) span.SetAttributes(semconv.ServerPort(80)) case "https": - span.SetAttributes(attribute.String("network.peer.port", "443")) + span.SetAttributes(semconv.NetworkPeerPort(443)) span.SetAttributes(semconv.ServerPort(443)) } } else { - span.SetAttributes(attribute.String("network.peer.address", host)) - span.SetAttributes(attribute.String("network.peer.port", port)) + span.SetAttributes(semconv.NetworkPeerAddress(host)) intPort, _ := strconv.Atoi(port) + span.SetAttributes(semconv.NetworkPeerPort(intPort)) span.SetAttributes(semconv.ServerAddress(host)) span.SetAttributes(semconv.ServerPort(intPort)) } @@ -201,20 +205,20 @@ func (t *Tracer) CaptureClientRequest(span trace.Span, r *http.Request) { } // CaptureServerRequest used to add span attributes from the request as a Server. -// TODO: need to update the semconv package as it does not implement fully Semantic Convention v1.23.0. func (t *Tracer) CaptureServerRequest(span trace.Span, r *http.Request) { if t == nil || span == nil || r == nil { return } - // Common attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md#common-attributes + // Common attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md#common-attributes span.SetAttributes(semconv.HTTPRequestMethodKey.String(r.Method)) span.SetAttributes(semconv.NetworkProtocolVersion(proto(r.Proto))) - // Server attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md#http-server-semantic-conventions + sURL := t.safeURL(r.URL) + // Server attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md#http-server-semantic-conventions span.SetAttributes(semconv.HTTPRequestBodySize(int(r.ContentLength))) - span.SetAttributes(semconv.URLPath(r.URL.Path)) - span.SetAttributes(semconv.URLQuery(r.URL.RawQuery)) + span.SetAttributes(semconv.URLPath(sURL.Path)) + span.SetAttributes(semconv.URLQuery(sURL.RawQuery)) span.SetAttributes(semconv.URLScheme(r.Header.Get("X-Forwarded-Proto"))) span.SetAttributes(semconv.UserAgentOriginal(r.UserAgent())) span.SetAttributes(semconv.ServerAddress(r.Host)) @@ -222,17 +226,15 @@ func (t *Tracer) CaptureServerRequest(span trace.Span, r *http.Request) { host, port, err := net.SplitHostPort(r.RemoteAddr) if err != nil { span.SetAttributes(semconv.ClientAddress(r.RemoteAddr)) - span.SetAttributes(attribute.String("network.peer.address", r.RemoteAddr)) + span.SetAttributes(semconv.NetworkPeerAddress(r.Host)) } else { - span.SetAttributes(attribute.String("network.peer.address", host)) - span.SetAttributes(attribute.String("network.peer.port", port)) + span.SetAttributes(semconv.NetworkPeerAddress(host)) span.SetAttributes(semconv.ClientAddress(host)) intPort, _ := strconv.Atoi(port) span.SetAttributes(semconv.ClientPort(intPort)) + span.SetAttributes(semconv.NetworkPeerPort(intPort)) } - span.SetAttributes(semconv.ClientSocketAddress(r.Header.Get("X-Forwarded-For"))) - for _, header := range t.capturedRequestHeaders { // User-agent is already part of the semantic convention as a recommended attribute. if strings.EqualFold(header, "User-Agent") { @@ -273,6 +275,32 @@ func (t *Tracer) CaptureResponse(span trace.Span, responseHeaders http.Header, c } } +func (t *Tracer) safeURL(originalURL *url.URL) *url.URL { + if originalURL == nil { + return nil + } + + redactedURL := *originalURL + + // Redact password if exists. + if redactedURL.User != nil { + redactedURL.User = url.UserPassword("REDACTED", "REDACTED") + } + + // Redact query parameters. + query := redactedURL.Query() + for k := range query { + if slices.Contains(t.safeQueryParams, k) { + continue + } + + query.Set(k, "REDACTED") + } + redactedURL.RawQuery = query.Encode() + + return &redactedURL +} + func proto(proto string) string { switch proto { case "HTTP/1.0": diff --git a/pkg/tracing/tracing_test.go b/pkg/tracing/tracing_test.go new file mode 100644 index 000000000..c9b4a511c --- /dev/null +++ b/pkg/tracing/tracing_test.go @@ -0,0 +1,57 @@ +package tracing + +import ( + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_safeFullURL(t *testing.T) { + testCases := []struct { + desc string + safeQueryParams []string + originalURL *url.URL + expectedURL *url.URL + }{ + { + desc: "Nil URL", + originalURL: nil, + expectedURL: nil, + }, + { + desc: "No query parameters", + originalURL: &url.URL{Scheme: "https", Host: "example.com"}, + expectedURL: &url.URL{Scheme: "https", Host: "example.com"}, + }, + { + desc: "All query parameters redacted", + originalURL: &url.URL{Scheme: "https", Host: "example.com", RawQuery: "foo=bar&baz=qux"}, + expectedURL: &url.URL{Scheme: "https", Host: "example.com", RawQuery: "baz=REDACTED&foo=REDACTED"}, + }, + { + desc: "Some query parameters unredacted", + safeQueryParams: []string{"foo"}, + originalURL: &url.URL{Scheme: "https", Host: "example.com", RawQuery: "foo=bar&baz=qux"}, + expectedURL: &url.URL{Scheme: "https", Host: "example.com", RawQuery: "baz=REDACTED&foo=bar"}, + }, + { + desc: "User info and some query parameters redacted", + safeQueryParams: []string{"foo"}, + originalURL: &url.URL{Scheme: "https", Host: "example.com", User: url.UserPassword("username", "password"), RawQuery: "foo=bar&baz=qux"}, + expectedURL: &url.URL{Scheme: "https", Host: "example.com", User: url.UserPassword("REDACTED", "REDACTED"), RawQuery: "baz=REDACTED&foo=bar"}, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + tr := NewTracer(nil, nil, nil, test.safeQueryParams) + + gotURL := tr.safeURL(test.originalURL) + + assert.Equal(t, test.expectedURL, gotURL) + }) + } +}