1
0
mirror of https://github.com/containous/traefik.git synced 2025-03-10 12:58:23 +03:00

OpenTelemetry Logs and Access Logs

Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
Romain 2024-12-06 14:50:04 +01:00 committed by GitHub
parent 33c1d700c0
commit 826a2b74aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 2297 additions and 475 deletions

View File

@ -1,6 +1,7 @@
package main
import (
"fmt"
"io"
stdlog "log"
"os"
@ -20,12 +21,13 @@ func init() {
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
}
func setupLogger(staticConfiguration *static.Configuration) {
func setupLogger(staticConfiguration *static.Configuration) error {
// configure log format
w := getLogWriter(staticConfiguration)
// configure log level
logLevel := getLogLevel(staticConfiguration)
zerolog.SetGlobalLevel(logLevel)
// create logger
logCtx := zerolog.New(w).With().Timestamp()
@ -34,8 +36,16 @@ func setupLogger(staticConfiguration *static.Configuration) {
}
log.Logger = logCtx.Logger().Level(logLevel)
if staticConfiguration.Log != nil && staticConfiguration.Log.OTLP != nil {
var err error
log.Logger, err = logs.SetupOTelLogger(log.Logger, staticConfiguration.Log.OTLP)
if err != nil {
return fmt.Errorf("setting up OpenTelemetry logger: %w", err)
}
}
zerolog.DefaultContextLogger = &log.Logger
zerolog.SetGlobalLevel(logLevel)
// Global logrus replacement (related to lib like go-rancher-metadata, docker, etc.)
logrus.StandardLogger().Out = logs.NoLevel(log.Logger, zerolog.DebugLevel)
@ -43,11 +53,16 @@ func setupLogger(staticConfiguration *static.Configuration) {
// configure default standard log.
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
stdlog.SetOutput(logs.NoLevel(log.Logger, zerolog.DebugLevel))
return nil
}
func getLogWriter(staticConfiguration *static.Configuration) io.Writer {
var w io.Writer = os.Stdout
if staticConfiguration.Log != nil && staticConfiguration.Log.OTLP != nil {
return io.Discard
}
var w io.Writer = os.Stdout
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
_, _ = os.OpenFile(staticConfiguration.Log.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666)
w = &lumberjack.Logger{

View File

@ -90,7 +90,9 @@ Complete documentation is available at https://traefik.io`,
}
func runCmd(staticConfiguration *static.Configuration) error {
setupLogger(staticConfiguration)
if err := setupLogger(staticConfiguration); err != nil {
return fmt.Errorf("setting up logger: %w", err)
}
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment

View File

@ -167,3 +167,9 @@ Please refer to the Forwarded headers [documentation](../routing/entrypoints.md#
In `v3.3`, the `acme.dnsChallenge.delaybeforecheck` and `acme.dnsChallenge.disablepropagationcheck` options of the ACME certificate resolver are deprecated,
please use respectively `acme.dnsChallenge.propagation.delayBeforeCheck` and `acme.dnsChallenge.propagation.disableAllChecks` options instead.
### Tracing Global Attributes
In `v3.3`, the `tracing.globalAttributes` option has been deprecated, please use the `tracing.resourceAttributes` option instead.
The `tracing.globalAttributes` option is misleading as its name does not reflect the operation of adding resource attributes to be sent to the collector,
and will be removed in the next major version.

View File

@ -30,7 +30,7 @@ accessLog: {}
_Optional, Default="false"_
Enables accessLogs for internal resources (e.g.: `ping@internal`).
Enables access logs for internal resources (e.g.: `ping@internal`).
```yaml tab="File (YAML)"
accesslog:
@ -306,4 +306,401 @@ services:
- /var/run/docker.sock:/var/run/docker.sock
```
## OpenTelemetry
To enable the OpenTelemetry Logger for access logs:
```yaml tab="File (YAML)"
accesslog:
otlp: {}
```
```toml tab="File (TOML)"
[accesslog.otlp]
```
```bash tab="CLI"
--accesslog.otlp=true
```
!!! info "Default protocol"
The OpenTelemetry Logger exporter will export access logs to the collector using HTTPS by default to https://localhost:4318/v1/logs, see the [gRPC Section](#grpc-configuration) to use gRPC.
### HTTP configuration
_Optional_
This instructs the exporter to send access logs to the OpenTelemetry Collector using HTTP.
```yaml tab="File (YAML)"
accesslog:
otlp:
http: {}
```
```toml tab="File (TOML)"
[accesslog.otlp.http]
```
```bash tab="CLI"
--accesslog.otlp.http=true
```
#### `endpoint`
_Optional, Default="`https://localhost:4318/v1/logs`", Format="`<scheme>://<host>:<port><path>`"_
URL of the OpenTelemetry Collector to send access logs to.
!!! info "Insecure mode"
To disable TLS, use `http://` instead of `https://` in the `endpoint` configuration.
```yaml tab="File (YAML)"
accesslog:
otlp:
http:
endpoint: https://collector:4318/v1/logs
```
```toml tab="File (TOML)"
[accesslog.otlp.http]
endpoint = "https://collector:4318/v1/logs"
```
```bash tab="CLI"
--accesslog.otlp.http.endpoint=https://collector:4318/v1/logs
```
#### `headers`
_Optional, Default={}_
Additional headers sent with access logs by the exporter to the OpenTelemetry Collector.
```yaml tab="File (YAML)"
accesslog:
otlp:
http:
headers:
foo: bar
baz: buz
```
```toml tab="File (TOML)"
[accesslog.otlp.http.headers]
foo = "bar"
baz = "buz"
```
```bash tab="CLI"
--accesslog.otlp.http.headers.foo=bar --accesslog.otlp.http.headers.baz=buz
```
#### `tls`
_Optional_
Defines the Client TLS configuration used by the exporter to send access logs to the OpenTelemetry Collector.
##### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
it defaults to the system bundle.
```yaml tab="File (YAML)"
accesslog:
otlp:
http:
tls:
ca: path/to/ca.crt
```
```toml tab="File (TOML)"
[accesslog.otlp.http.tls]
ca = "path/to/ca.crt"
```
```bash tab="CLI"
--accesslog.otlp.http.tls.ca=path/to/ca.crt
```
##### `cert`
_Optional_
`cert` is the path to the public certificate used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
accesslog:
otlp:
http:
tls:
cert: path/to/foo.cert
key: path/to/foo.key
```
```toml tab="File (TOML)"
[accesslog.otlp.http.tls]
cert = "path/to/foo.cert"
key = "path/to/foo.key"
```
```bash tab="CLI"
--accesslog.otlp.http.tls.cert=path/to/foo.cert
--accesslog.otlp.http.tls.key=path/to/foo.key
```
##### `key`
_Optional_
`key` is the path to the private key used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
accesslog:
otlp:
http:
tls:
cert: path/to/foo.cert
key: path/to/foo.key
```
```toml tab="File (TOML)"
[accesslog.otlp.http.tls]
cert = "path/to/foo.cert"
key = "path/to/foo.key"
```
```bash tab="CLI"
--accesslog.otlp.http.tls.cert=path/to/foo.cert
--accesslog.otlp.http.tls.key=path/to/foo.key
```
##### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`,
the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers.
```yaml tab="File (YAML)"
accesslog:
otlp:
http:
tls:
insecureSkipVerify: true
```
```toml tab="File (TOML)"
[accesslog.otlp.http.tls]
insecureSkipVerify = true
```
```bash tab="CLI"
--accesslog.otlp.http.tls.insecureSkipVerify=true
```
### gRPC configuration
_Optional_
This instructs the exporter to send access logs to the OpenTelemetry Collector using gRPC.
```yaml tab="File (YAML)"
accesslog:
otlp:
grpc: {}
```
```toml tab="File (TOML)"
[accesslog.otlp.grpc]
```
```bash tab="CLI"
--accesslog.otlp.grpc=true
```
#### `endpoint`
_Required, Default="localhost:4317", Format="`<host>:<port>`"_
Address of the OpenTelemetry Collector to send access logs to.
```yaml tab="File (YAML)"
accesslog:
otlp:
grpc:
endpoint: localhost:4317
```
```toml tab="File (TOML)"
[accesslog.otlp.grpc]
endpoint = "localhost:4317"
```
```bash tab="CLI"
--accesslog.otlp.grpc.endpoint=localhost:4317
```
#### `insecure`
_Optional, Default=false_
Allows exporter to send access logs to the OpenTelemetry Collector without using a secured protocol.
```yaml tab="File (YAML)"
accesslog:
otlp:
grpc:
insecure: true
```
```toml tab="File (TOML)"
[accesslog.otlp.grpc]
insecure = true
```
```bash tab="CLI"
--accesslog.otlp.grpc.insecure=true
```
#### `headers`
_Optional, Default={}_
Additional headers sent with access logs by the exporter to the OpenTelemetry Collector.
```yaml tab="File (YAML)"
accesslog:
otlp:
grpc:
headers:
foo: bar
baz: buz
```
```toml tab="File (TOML)"
[accesslog.otlp.grpc.headers]
foo = "bar"
baz = "buz"
```
```bash tab="CLI"
--accesslog.otlp.grpc.headers.foo=bar --accesslog.otlp.grpc.headers.baz=buz
```
#### `tls`
_Optional_
Defines the Client TLS configuration used by the exporter to send access logs to the OpenTelemetry Collector.
##### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
it defaults to the system bundle.
```yaml tab="File (YAML)"
accesslog:
otlp:
grpc:
tls:
ca: path/to/ca.crt
```
```toml tab="File (TOML)"
[accesslog.otlp.grpc.tls]
ca = "path/to/ca.crt"
```
```bash tab="CLI"
--accesslog.otlp.grpc.tls.ca=path/to/ca.crt
```
##### `cert`
_Optional_
`cert` is the path to the public certificate used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
accesslog:
otlp:
grpc:
tls:
cert: path/to/foo.cert
key: path/to/foo.key
```
```toml tab="File (TOML)"
[accesslog.otlp.grpc.tls]
cert = "path/to/foo.cert"
key = "path/to/foo.key"
```
```bash tab="CLI"
--accesslog.otlp.grpc.tls.cert=path/to/foo.cert
--accesslog.otlp.grpc.tls.key=path/to/foo.key
```
##### `key`
_Optional_
`key` is the path to the private key used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
accesslog:
otlp:
grpc:
tls:
cert: path/to/foo.cert
key: path/to/foo.key
```
```toml tab="File (TOML)"
[accesslog.otlp.grpc.tls]
cert = "path/to/foo.cert"
key = "path/to/foo.key"
```
```bash tab="CLI"
--accesslog.otlp.grpc.tls.cert=path/to/foo.cert
--accesslog.otlp.grpc.tls.key=path/to/foo.key
```
##### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`,
the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers.
```yaml tab="File (YAML)"
accesslog:
otlp:
grpc:
tls:
insecureSkipVerify: true
```
```toml tab="File (TOML)"
[accesslog.otlp.grpc.tls]
insecureSkipVerify = true
```
```bash tab="CLI"
--accesslog.otlp.grpc.tls.insecureSkipVerify=true
```
{!traefik-for-business-applications.md!}

View File

@ -181,4 +181,401 @@ log:
--log.compress=true
```
## OpenTelemetry
To enable the OpenTelemetry Logger for logs:
```yaml tab="File (YAML)"
log:
otlp: {}
```
```toml tab="File (TOML)"
[log.otlp]
```
```bash tab="CLI"
--log.otlp=true
```
!!! info "Default protocol"
The OpenTelemetry Logger exporter will export logs to the collector using HTTPS by default to https://localhost:4318/v1/logs, see the [gRPC Section](#grpc-configuration) to use gRPC.
### HTTP configuration
_Optional_
This instructs the exporter to send logs to the OpenTelemetry Collector using HTTP.
```yaml tab="File (YAML)"
log:
otlp:
http: {}
```
```toml tab="File (TOML)"
[log.otlp.http]
```
```bash tab="CLI"
--log.otlp.http=true
```
#### `endpoint`
_Optional, Default="`https://localhost:4318/v1/logs`", Format="`<scheme>://<host>:<port><path>`"_
URL of the OpenTelemetry Collector to send logs to.
!!! info "Insecure mode"
To disable TLS, use `http://` instead of `https://` in the `endpoint` configuration.
```yaml tab="File (YAML)"
log:
otlp:
http:
endpoint: https://collector:4318/v1/logs
```
```toml tab="File (TOML)"
[log.otlp.http]
endpoint = "https://collector:4318/v1/logs"
```
```bash tab="CLI"
--log.otlp.http.endpoint=https://collector:4318/v1/logs
```
#### `headers`
_Optional, Default={}_
Additional headers sent with logs by the exporter to the OpenTelemetry Collector.
```yaml tab="File (YAML)"
log:
otlp:
http:
headers:
foo: bar
baz: buz
```
```toml tab="File (TOML)"
[log.otlp.http.headers]
foo = "bar"
baz = "buz"
```
```bash tab="CLI"
--log.otlp.http.headers.foo=bar --log.otlp.http.headers.baz=buz
```
#### `tls`
_Optional_
Defines the Client TLS configuration used by the exporter to send logs to the OpenTelemetry Collector.
##### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
it defaults to the system bundle.
```yaml tab="File (YAML)"
log:
otlp:
http:
tls:
ca: path/to/ca.crt
```
```toml tab="File (TOML)"
[log.otlp.http.tls]
ca = "path/to/ca.crt"
```
```bash tab="CLI"
--log.otlp.http.tls.ca=path/to/ca.crt
```
##### `cert`
_Optional_
`cert` is the path to the public certificate used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
log:
otlp:
http:
tls:
cert: path/to/foo.cert
key: path/to/foo.key
```
```toml tab="File (TOML)"
[log.otlp.http.tls]
cert = "path/to/foo.cert"
key = "path/to/foo.key"
```
```bash tab="CLI"
--log.otlp.http.tls.cert=path/to/foo.cert
--log.otlp.http.tls.key=path/to/foo.key
```
##### `key`
_Optional_
`key` is the path to the private key used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
log:
otlp:
http:
tls:
cert: path/to/foo.cert
key: path/to/foo.key
```
```toml tab="File (TOML)"
[log.otlp.http.tls]
cert = "path/to/foo.cert"
key = "path/to/foo.key"
```
```bash tab="CLI"
--log.otlp.http.tls.cert=path/to/foo.cert
--log.otlp.http.tls.key=path/to/foo.key
```
##### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`,
the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers.
```yaml tab="File (YAML)"
log:
otlp:
http:
tls:
insecureSkipVerify: true
```
```toml tab="File (TOML)"
[log.otlp.http.tls]
insecureSkipVerify = true
```
```bash tab="CLI"
--log.otlp.http.tls.insecureSkipVerify=true
```
### gRPC configuration
_Optional_
This instructs the exporter to send logs to the OpenTelemetry Collector using gRPC.
```yaml tab="File (YAML)"
log:
otlp:
grpc: {}
```
```toml tab="File (TOML)"
[log.otlp.grpc]
```
```bash tab="CLI"
--log.otlp.grpc=true
```
#### `endpoint`
_Required, Default="localhost:4317", Format="`<host>:<port>`"_
Address of the OpenTelemetry Collector to send logs to.
```yaml tab="File (YAML)"
log:
otlp:
grpc:
endpoint: localhost:4317
```
```toml tab="File (TOML)"
[log.otlp.grpc]
endpoint = "localhost:4317"
```
```bash tab="CLI"
--log.otlp.grpc.endpoint=localhost:4317
```
#### `insecure`
_Optional, Default=false_
Allows exporter to send logs to the OpenTelemetry Collector without using a secured protocol.
```yaml tab="File (YAML)"
log:
otlp:
grpc:
insecure: true
```
```toml tab="File (TOML)"
[log.otlp.grpc]
insecure = true
```
```bash tab="CLI"
--log.otlp.grpc.insecure=true
```
#### `headers`
_Optional, Default={}_
Additional headers sent with logs by the exporter to the OpenTelemetry Collector.
```yaml tab="File (YAML)"
log:
otlp:
grpc:
headers:
foo: bar
baz: buz
```
```toml tab="File (TOML)"
[log.otlp.grpc.headers]
foo = "bar"
baz = "buz"
```
```bash tab="CLI"
--log.otlp.grpc.headers.foo=bar --log.otlp.grpc.headers.baz=buz
```
#### `tls`
_Optional_
Defines the Client TLS configuration used by the exporter to send logs to the OpenTelemetry Collector.
##### `ca`
_Optional_
`ca` is the path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
it defaults to the system bundle.
```yaml tab="File (YAML)"
log:
otlp:
grpc:
tls:
ca: path/to/ca.crt
```
```toml tab="File (TOML)"
[log.otlp.grpc.tls]
ca = "path/to/ca.crt"
```
```bash tab="CLI"
--log.otlp.grpc.tls.ca=path/to/ca.crt
```
##### `cert`
_Optional_
`cert` is the path to the public certificate used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `key` option is required.
```yaml tab="File (YAML)"
log:
otlp:
grpc:
tls:
cert: path/to/foo.cert
key: path/to/foo.key
```
```toml tab="File (TOML)"
[log.otlp.grpc.tls]
cert = "path/to/foo.cert"
key = "path/to/foo.key"
```
```bash tab="CLI"
--log.otlp.grpc.tls.cert=path/to/foo.cert
--log.otlp.grpc.tls.key=path/to/foo.key
```
##### `key`
_Optional_
`key` is the path to the private key used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `cert` option is required.
```yaml tab="File (YAML)"
log:
otlp:
grpc:
tls:
cert: path/to/foo.cert
key: path/to/foo.key
```
```toml tab="File (TOML)"
[log.otlp.grpc.tls]
cert = "path/to/foo.cert"
key = "path/to/foo.key"
```
```bash tab="CLI"
--log.otlp.grpc.tls.cert=path/to/foo.cert
--log.otlp.grpc.tls.key=path/to/foo.key
```
##### `insecureSkipVerify`
_Optional, Default=false_
If `insecureSkipVerify` is `true`,
the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers.
```yaml tab="File (YAML)"
log:
otlp:
grpc:
tls:
insecureSkipVerify: true
```
```toml tab="File (TOML)"
[log.otlp.grpc.tls]
insecureSkipVerify = true
```
```bash tab="CLI"
--log.otlp.grpc.tls.insecureSkipVerify=true
```
{!traefik-for-business-applications.md!}

View File

@ -23,7 +23,7 @@ metrics:
!!! info "Default protocol"
The OpenTelemetry exporter will export metrics to the collector using HTTP by default to https://localhost:4318/v1/metrics, see the [gRPC Section](#grpc-configuration) to use gRPC.
The OpenTelemetry exporter will export metrics to the collector using HTTPS by default to https://localhost:4318/v1/metrics, see the [gRPC Section](#grpc-configuration) to use gRPC.
#### `addEntryPointsLabels`
@ -184,25 +184,29 @@ metrics:
#### `endpoint`
_Required, Default="http://localhost:4318/v1/metrics", Format="`<scheme>://<host>:<port><path>`"_
_Optional, Default="https://localhost:4318/v1/metrics", Format="`<scheme>://<host>:<port><path>`"_
URL of the OpenTelemetry Collector to send metrics to.
!!! info "Insecure mode"
To disable TLS, use `http://` instead of `https://` in the `endpoint` configuration.
```yaml tab="File (YAML)"
metrics:
otlp:
http:
endpoint: http://localhost:4318/v1/metrics
endpoint: https://collector:4318/v1/metrics
```
```toml tab="File (TOML)"
[metrics]
[metrics.otlp.http]
endpoint = "http://localhost:4318/v1/metrics"
endpoint = "https://collector:4318/v1/metrics"
```
```bash tab="CLI"
--metrics.otlp.http.endpoint=http://localhost:4318/v1/metrics
--metrics.otlp.http.endpoint=https://collector:4318/v1/metrics
```
#### `headers`

View File

@ -25,7 +25,7 @@ tracing:
!!! info "Default protocol"
The OpenTelemetry trace exporter will export traces to the collector using HTTP by default to https://localhost:4318/v1/traces, see the [gRPC Section](#grpc-configuration) to use gRPC.
The OpenTelemetry trace exporter will export traces to the collector using HTTPS by default to https://localhost:4318/v1/traces, see the [gRPC Section](#grpc-configuration) to use gRPC.
!!! info "Trace sampling"
@ -72,25 +72,29 @@ tracing:
#### `endpoint`
_Required, Default="http://localhost:4318/v1/traces", Format="`<scheme>://<host>:<port><path>`"_
_Optional, Default="https://localhost:4318/v1/traces", Format="`<scheme>://<host>:<port><path>`"_
URL of the OpenTelemetry Collector to send spans to.
!!! info "Insecure mode"
To disable TLS, use `http://` instead of `https://` in the `endpoint` configuration.
```yaml tab="File (YAML)"
tracing:
otlp:
http:
endpoint: http://localhost:4318/v1/traces
endpoint: https://collector:4318/v1/traces
```
```toml tab="File (TOML)"
[tracing]
[tracing.otlp.http]
endpoint = "http://localhost:4318/v1/traces"
endpoint = "https://collector:4318/v1/traces"
```
```bash tab="CLI"
--tracing.otlp.http.endpoint=http://localhost:4318/v1/traces
--tracing.otlp.http.endpoint=https://collector:4318/v1/traces
```
#### `headers`

View File

@ -92,29 +92,29 @@ tracing:
--tracing.sampleRate=0.2
```
#### `globalAttributes`
#### `resourceAttributes`
_Optional, Default=empty_
Applies a list of shared key:value attributes on all spans.
Defines additional resource attributes to be sent to the collector.
```yaml tab="File (YAML)"
tracing:
globalAttributes:
resourceAttributes:
attr1: foo
attr2: bar
```
```toml tab="File (TOML)"
[tracing]
[tracing.globalAttributes]
[tracing.resourceAttributes]
attr1 = "foo"
attr2 = "bar"
```
```bash tab="CLI"
--tracing.globalAttributes.attr1=foo
--tracing.globalAttributes.attr2=bar
--tracing.resourceAttributes.attr1=foo
--tracing.resourceAttributes.attr2=bar
```
#### `capturedRequestHeaders`

View File

@ -39,6 +39,60 @@ Keep access logs with status codes in the specified range.
`--accesslog.format`:
Access log format: json | common (Default: ```common```)
`--accesslog.otlp`:
Settings for OpenTelemetry. (Default: ```false```)
`--accesslog.otlp.grpc`:
gRPC configuration for the OpenTelemetry collector. (Default: ```false```)
`--accesslog.otlp.grpc.endpoint`:
Sets the gRPC endpoint (host:port) of the collector. (Default: ```localhost:4317```)
`--accesslog.otlp.grpc.headers.<name>`:
Headers sent with payload.
`--accesslog.otlp.grpc.insecure`:
Disables client transport security for the exporter. (Default: ```false```)
`--accesslog.otlp.grpc.tls.ca`:
TLS CA
`--accesslog.otlp.grpc.tls.cert`:
TLS cert
`--accesslog.otlp.grpc.tls.insecureskipverify`:
TLS insecure skip verify (Default: ```false```)
`--accesslog.otlp.grpc.tls.key`:
TLS key
`--accesslog.otlp.http`:
HTTP configuration for the OpenTelemetry collector. (Default: ```false```)
`--accesslog.otlp.http.endpoint`:
Sets the HTTP endpoint (scheme://host:port/path) of the collector. (Default: ```https://localhost:4318```)
`--accesslog.otlp.http.headers.<name>`:
Headers sent with payload.
`--accesslog.otlp.http.tls.ca`:
TLS CA
`--accesslog.otlp.http.tls.cert`:
TLS cert
`--accesslog.otlp.http.tls.insecureskipverify`:
TLS insecure skip verify (Default: ```false```)
`--accesslog.otlp.http.tls.key`:
TLS key
`--accesslog.otlp.resourceattributes.<name>`:
Defines additional resource attributes (key:value).
`--accesslog.otlp.servicename`:
Set the name for this service. (Default: ```traefik```)
`--api`:
Enable api/dashboard. (Default: ```false```)
@ -333,6 +387,60 @@ Maximum size in megabytes of the log file before it gets rotated. (Default: ```0
`--log.nocolor`:
When using the 'common' format, disables the colorized output. (Default: ```false```)
`--log.otlp`:
Settings for OpenTelemetry. (Default: ```false```)
`--log.otlp.grpc`:
gRPC configuration for the OpenTelemetry collector. (Default: ```false```)
`--log.otlp.grpc.endpoint`:
Sets the gRPC endpoint (host:port) of the collector. (Default: ```localhost:4317```)
`--log.otlp.grpc.headers.<name>`:
Headers sent with payload.
`--log.otlp.grpc.insecure`:
Disables client transport security for the exporter. (Default: ```false```)
`--log.otlp.grpc.tls.ca`:
TLS CA
`--log.otlp.grpc.tls.cert`:
TLS cert
`--log.otlp.grpc.tls.insecureskipverify`:
TLS insecure skip verify (Default: ```false```)
`--log.otlp.grpc.tls.key`:
TLS key
`--log.otlp.http`:
HTTP configuration for the OpenTelemetry collector. (Default: ```false```)
`--log.otlp.http.endpoint`:
Sets the HTTP endpoint (scheme://host:port/path) of the collector. (Default: ```https://localhost:4318```)
`--log.otlp.http.headers.<name>`:
Headers sent with payload.
`--log.otlp.http.tls.ca`:
TLS CA
`--log.otlp.http.tls.cert`:
TLS cert
`--log.otlp.http.tls.insecureskipverify`:
TLS insecure skip verify (Default: ```false```)
`--log.otlp.http.tls.key`:
TLS key
`--log.otlp.resourceattributes.<name>`:
Defines additional resource attributes (key:value).
`--log.otlp.servicename`:
Set the name for this service. (Default: ```traefik```)
`--metrics.addinternals`:
Enables metrics for internal services (ping, dashboard, etc...). (Default: ```false```)
@ -1138,7 +1246,7 @@ Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain).
Defines the allowed SPIFFE trust domain.
`--tracing`:
OpenTracing configuration. (Default: ```false```)
Tracing configuration. (Default: ```false```)
`--tracing.addinternals`:
Enables tracing for internal services (ping, dashboard, etc...). (Default: ```false```)
@ -1150,7 +1258,7 @@ Request headers to add as attributes for server and client spans.
Response headers to add as attributes for server and client spans.
`--tracing.globalattributes.<name>`:
Defines additional attributes (key:value) on all spans.
(Deprecated) Defines additional resource attributes (key:value).
`--tracing.otlp`:
Settings for OpenTelemetry. (Default: ```false```)
@ -1200,6 +1308,9 @@ TLS insecure skip verify (Default: ```false```)
`--tracing.otlp.http.tls.key`:
TLS key
`--tracing.resourceattributes.<name>`:
Defines additional resource attributes (key:value).
`--tracing.safequeryparams`:
Query params to not redact.
@ -1207,4 +1318,4 @@ Query params to not redact.
Sets the rate between 0.0 and 1.0 of requests to trace. (Default: ```1.000000```)
`--tracing.servicename`:
Set the name for this service. (Default: ```traefik```)
Sets the name for this service. (Default: ```traefik```)

View File

@ -39,6 +39,60 @@ Keep access logs with status codes in the specified range.
`TRAEFIK_ACCESSLOG_FORMAT`:
Access log format: json | common (Default: ```common```)
`TRAEFIK_ACCESSLOG_OTLP`:
Settings for OpenTelemetry. (Default: ```false```)
`TRAEFIK_ACCESSLOG_OTLP_GRPC`:
gRPC configuration for the OpenTelemetry collector. (Default: ```false```)
`TRAEFIK_ACCESSLOG_OTLP_GRPC_ENDPOINT`:
Sets the gRPC endpoint (host:port) of the collector. (Default: ```localhost:4317```)
`TRAEFIK_ACCESSLOG_OTLP_GRPC_HEADERS_<NAME>`:
Headers sent with payload.
`TRAEFIK_ACCESSLOG_OTLP_GRPC_INSECURE`:
Disables client transport security for the exporter. (Default: ```false```)
`TRAEFIK_ACCESSLOG_OTLP_GRPC_TLS_CA`:
TLS CA
`TRAEFIK_ACCESSLOG_OTLP_GRPC_TLS_CERT`:
TLS cert
`TRAEFIK_ACCESSLOG_OTLP_GRPC_TLS_INSECURESKIPVERIFY`:
TLS insecure skip verify (Default: ```false```)
`TRAEFIK_ACCESSLOG_OTLP_GRPC_TLS_KEY`:
TLS key
`TRAEFIK_ACCESSLOG_OTLP_HTTP`:
HTTP configuration for the OpenTelemetry collector. (Default: ```false```)
`TRAEFIK_ACCESSLOG_OTLP_HTTP_ENDPOINT`:
Sets the HTTP endpoint (scheme://host:port/path) of the collector. (Default: ```https://localhost:4318```)
`TRAEFIK_ACCESSLOG_OTLP_HTTP_HEADERS_<NAME>`:
Headers sent with payload.
`TRAEFIK_ACCESSLOG_OTLP_HTTP_TLS_CA`:
TLS CA
`TRAEFIK_ACCESSLOG_OTLP_HTTP_TLS_CERT`:
TLS cert
`TRAEFIK_ACCESSLOG_OTLP_HTTP_TLS_INSECURESKIPVERIFY`:
TLS insecure skip verify (Default: ```false```)
`TRAEFIK_ACCESSLOG_OTLP_HTTP_TLS_KEY`:
TLS key
`TRAEFIK_ACCESSLOG_OTLP_RESOURCEATTRIBUTES_<NAME>`:
Defines additional resource attributes (key:value).
`TRAEFIK_ACCESSLOG_OTLP_SERVICENAME`:
Set the name for this service. (Default: ```traefik```)
`TRAEFIK_API`:
Enable api/dashboard. (Default: ```false```)
@ -333,6 +387,60 @@ Maximum size in megabytes of the log file before it gets rotated. (Default: ```0
`TRAEFIK_LOG_NOCOLOR`:
When using the 'common' format, disables the colorized output. (Default: ```false```)
`TRAEFIK_LOG_OTLP`:
Settings for OpenTelemetry. (Default: ```false```)
`TRAEFIK_LOG_OTLP_GRPC`:
gRPC configuration for the OpenTelemetry collector. (Default: ```false```)
`TRAEFIK_LOG_OTLP_GRPC_ENDPOINT`:
Sets the gRPC endpoint (host:port) of the collector. (Default: ```localhost:4317```)
`TRAEFIK_LOG_OTLP_GRPC_HEADERS_<NAME>`:
Headers sent with payload.
`TRAEFIK_LOG_OTLP_GRPC_INSECURE`:
Disables client transport security for the exporter. (Default: ```false```)
`TRAEFIK_LOG_OTLP_GRPC_TLS_CA`:
TLS CA
`TRAEFIK_LOG_OTLP_GRPC_TLS_CERT`:
TLS cert
`TRAEFIK_LOG_OTLP_GRPC_TLS_INSECURESKIPVERIFY`:
TLS insecure skip verify (Default: ```false```)
`TRAEFIK_LOG_OTLP_GRPC_TLS_KEY`:
TLS key
`TRAEFIK_LOG_OTLP_HTTP`:
HTTP configuration for the OpenTelemetry collector. (Default: ```false```)
`TRAEFIK_LOG_OTLP_HTTP_ENDPOINT`:
Sets the HTTP endpoint (scheme://host:port/path) of the collector. (Default: ```https://localhost:4318```)
`TRAEFIK_LOG_OTLP_HTTP_HEADERS_<NAME>`:
Headers sent with payload.
`TRAEFIK_LOG_OTLP_HTTP_TLS_CA`:
TLS CA
`TRAEFIK_LOG_OTLP_HTTP_TLS_CERT`:
TLS cert
`TRAEFIK_LOG_OTLP_HTTP_TLS_INSECURESKIPVERIFY`:
TLS insecure skip verify (Default: ```false```)
`TRAEFIK_LOG_OTLP_HTTP_TLS_KEY`:
TLS key
`TRAEFIK_LOG_OTLP_RESOURCEATTRIBUTES_<NAME>`:
Defines additional resource attributes (key:value).
`TRAEFIK_LOG_OTLP_SERVICENAME`:
Set the name for this service. (Default: ```traefik```)
`TRAEFIK_METRICS_ADDINTERNALS`:
Enables metrics for internal services (ping, dashboard, etc...). (Default: ```false```)
@ -1138,7 +1246,7 @@ Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain).
Defines the allowed SPIFFE trust domain.
`TRAEFIK_TRACING`:
OpenTracing configuration. (Default: ```false```)
Tracing configuration. (Default: ```false```)
`TRAEFIK_TRACING_ADDINTERNALS`:
Enables tracing for internal services (ping, dashboard, etc...). (Default: ```false```)
@ -1150,7 +1258,7 @@ Request headers to add as attributes for server and client spans.
Response headers to add as attributes for server and client spans.
`TRAEFIK_TRACING_GLOBALATTRIBUTES_<NAME>`:
Defines additional attributes (key:value) on all spans.
(Deprecated) Defines additional resource attributes (key:value).
`TRAEFIK_TRACING_OTLP`:
Settings for OpenTelemetry. (Default: ```false```)
@ -1200,6 +1308,9 @@ TLS insecure skip verify (Default: ```false```)
`TRAEFIK_TRACING_OTLP_HTTP_TLS_KEY`:
TLS key
`TRAEFIK_TRACING_RESOURCEATTRIBUTES_<NAME>`:
Defines additional resource attributes (key:value).
`TRAEFIK_TRACING_SAFEQUERYPARAMS`:
Query params to not redact.
@ -1207,4 +1318,4 @@ Query params to not redact.
Sets the rate between 0.0 and 1.0 of requests to trace. (Default: ```1.000000```)
`TRAEFIK_TRACING_SERVICENAME`:
Set the name for this service. (Default: ```traefik```)
Sets the name for this service. (Default: ```traefik```)

View File

@ -381,6 +381,32 @@
maxAge = 42
maxBackups = 42
compress = true
[log.otlp]
serviceName = "foobar"
[log.otlp.resourceAttributes]
name0 = "foobar"
name1 = "foobar"
[log.otlp.grpc]
endpoint = "foobar"
insecure = true
[log.otlp.grpc.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
[log.otlp.grpc.headers]
name0 = "foobar"
name1 = "foobar"
[log.otlp.http]
endpoint = "foobar"
[log.otlp.http.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
[log.otlp.http.headers]
name0 = "foobar"
name1 = "foobar"
[accessLog]
filePath = "foobar"
@ -401,6 +427,32 @@
[accessLog.fields.headers.names]
name0 = "foobar"
name1 = "foobar"
[accessLog.otlp]
serviceName = "foobar"
[accessLog.otlp.resourceAttributes]
name0 = "foobar"
name1 = "foobar"
[accessLog.otlp.grpc]
endpoint = "foobar"
insecure = true
[accessLog.otlp.grpc.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
[accessLog.otlp.grpc.headers]
name0 = "foobar"
name1 = "foobar"
[accessLog.otlp.http]
endpoint = "foobar"
[accessLog.otlp.http.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
[accessLog.otlp.http.headers]
name0 = "foobar"
name1 = "foobar"
[tracing]
serviceName = "foobar"
@ -409,7 +461,7 @@
safeQueryParams = ["foobar", "foobar"]
sampleRate = 42.0
addInternals = true
[tracing.globalAttributes]
[tracing.resourceAttributes]
name0 = "foobar"
name1 = "foobar"
[tracing.otlp]
@ -434,6 +486,9 @@
[tracing.otlp.http.headers]
name0 = "foobar"
name1 = "foobar"
[tracing.globalAttributes]
name0 = "foobar"
name1 = "foobar"
[hostResolver]
cnameFlattening = true

View File

@ -418,6 +418,32 @@ log:
maxAge: 42
maxBackups: 42
compress: true
otlp:
serviceName: foobar
resourceAttributes:
name0: foobar
name1: foobar
grpc:
endpoint: foobar
insecure: true
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
headers:
name0: foobar
name1: foobar
http:
endpoint: foobar
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
headers:
name0: foobar
name1: foobar
accessLog:
filePath: foobar
format: foobar
@ -439,9 +465,35 @@ accessLog:
name1: foobar
bufferingSize: 42
addInternals: true
otlp:
serviceName: foobar
resourceAttributes:
name0: foobar
name1: foobar
grpc:
endpoint: foobar
insecure: true
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
headers:
name0: foobar
name1: foobar
http:
endpoint: foobar
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
headers:
name0: foobar
name1: foobar
tracing:
serviceName: foobar
globalAttributes:
resourceAttributes:
name0: foobar
name1: foobar
capturedRequestHeaders:
@ -477,6 +529,9 @@ tracing:
headers:
name0: foobar
name1: foobar
globalAttributes:
name0: foobar
name1: foobar
hostResolver:
cnameFlattening: true
resolvConfig: foobar

27
go.mod
View File

@ -51,7 +51,7 @@ require (
github.com/prometheus/client_golang v1.19.1
github.com/prometheus/client_model v0.6.1
github.com/quic-go/quic-go v0.47.0
github.com/rs/zerolog v1.29.0
github.com/rs/zerolog v1.33.0
github.com/sirupsen/logrus v1.9.3
github.com/spiffe/go-spiffe/v2 v2.1.1
github.com/stealthrocket/wasi-go v0.8.0
@ -72,23 +72,28 @@ require (
github.com/vulcand/oxy/v2 v2.0.0
github.com/vulcand/predicate v1.2.0
go.opentelemetry.io/collector/pdata v1.10.0
go.opentelemetry.io/contrib/bridges/otellogrus v0.7.0
go.opentelemetry.io/contrib/propagators/autoprop v0.53.0
go.opentelemetry.io/otel v1.29.0
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0
go.opentelemetry.io/otel/metric v1.29.0
go.opentelemetry.io/otel/sdk v1.28.0
go.opentelemetry.io/otel/log v0.8.0
go.opentelemetry.io/otel/metric v1.32.0
go.opentelemetry.io/otel/sdk v1.32.0
go.opentelemetry.io/otel/sdk/log v0.8.0
go.opentelemetry.io/otel/sdk/metric v1.28.0
go.opentelemetry.io/otel/trace v1.29.0
go.opentelemetry.io/otel/trace v1.32.0
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // No tag on the repo.
golang.org/x/mod v0.21.0
golang.org/x/net v0.30.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.26.0
golang.org/x/text v0.19.0
golang.org/x/sync v0.9.0
golang.org/x/sys v0.27.0
golang.org/x/text v0.20.0
golang.org/x/time v0.7.0
golang.org/x/tools v0.25.0
google.golang.org/grpc v1.67.1
@ -219,7 +224,7 @@ require (
github.com/gophercloud/gophercloud v1.14.1 // indirect
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
github.com/hashicorp/cronexpr v1.1.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@ -364,8 +369,8 @@ require (
golang.org/x/term v0.25.0 // indirect
google.golang.org/api v0.204.0 // indirect
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/h2non/gock.v1 v1.0.16 // indirect

63
go.sum
View File

@ -262,7 +262,6 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
@ -576,8 +575,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
@ -818,6 +817,7 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@ -1046,13 +1046,13 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sacloud/api-client-go v0.2.10 h1:+rv3jDohD+pkdYwOTBiB+jZsM0xK3AxadXRzhp3q66c=
@ -1288,6 +1288,8 @@ 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.10.0 h1:oLyPLGvPTQrcRT64ZVruwvmH/u3SHTfNo01pteS4WOE=
go.opentelemetry.io/collector/pdata v1.10.0/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE=
go.opentelemetry.io/contrib/bridges/otellogrus v0.7.0 h1:vPSzn6dQvdPq9ZiXFs+jUSJnzoKJkADD9yBdx/a1WgI=
go.opentelemetry.io/contrib/bridges/otellogrus v0.7.0/go.mod h1:yZFNJIjn97IBhuMB3tTGPti9xasYLIdh3ChZIzyhz8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/contrib/propagators/autoprop v0.53.0 h1:4zaVLcJ5mvYw0vlk63TX62qS4qty/4jAY1BKZ1usu18=
@ -1300,8 +1302,12 @@ go.opentelemetry.io/contrib/propagators/jaeger v1.28.0 h1:xQ3ktSVS128JWIaN1DiPGI
go.opentelemetry.io/contrib/propagators/jaeger v1.28.0/go.mod h1:O9HIyI2kVBrFoEwQZ0IN6PHXykGoit4mZV2aEjkTRH4=
go.opentelemetry.io/contrib/propagators/ot v1.28.0 h1:rmlG+2pc5k5M7Y7izDrxAHZUIwDERdGMTD9oMV7llMk=
go.opentelemetry.io/contrib/propagators/ot v1.28.0/go.mod h1:MNgXIn+UrMbNGpd7xyckyo2LCHIgCdmdjEE7YNZGG+w=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0/go.mod h1:yeGZANgEcpdx/WK0IvvRFC+2oLiMS2u4L/0Rj2M2Qr0=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 h1:aLmmtjRke7LPDQ3lvpFz+kNEH43faFhzW7v8BFIEydg=
@ -1312,14 +1318,18 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6Z
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk=
go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs=
go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo=
go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08=
go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
@ -1508,8 +1518,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1597,11 +1607,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1631,8 +1642,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1770,10 +1781,10 @@ google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxH
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U=
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g=
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=

View File

@ -502,14 +502,17 @@ func (e *experimental) deprecationNotice(logger zerolog.Logger) bool {
return false
}
//
type tracing struct {
SpanNameLimit *int `json:"spanNameLimit,omitempty" toml:"spanNameLimit,omitempty" yaml:"spanNameLimit,omitempty"`
Jaeger map[string]any `json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" label:"allowEmpty" file:"allowEmpty"`
Zipkin map[string]any `json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" label:"allowEmpty" file:"allowEmpty"`
Datadog map[string]any `json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" label:"allowEmpty" file:"allowEmpty"`
Instana map[string]any `json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" label:"allowEmpty" file:"allowEmpty"`
Haystack map[string]any `json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" label:"allowEmpty" file:"allowEmpty"`
Elastic map[string]any `json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" label:"allowEmpty" file:"allowEmpty"`
SpanNameLimit *int `json:"spanNameLimit,omitempty" toml:"spanNameLimit,omitempty" yaml:"spanNameLimit,omitempty"`
GlobalAttributes map[string]string `json:"globalAttributes,omitempty" toml:"globalAttributes,omitempty" yaml:"globalAttributes,omitempty" export:"true"`
Jaeger map[string]any `json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" label:"allowEmpty" file:"allowEmpty"`
Zipkin map[string]any `json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" label:"allowEmpty" file:"allowEmpty"`
Datadog map[string]any `json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" label:"allowEmpty" file:"allowEmpty"`
Instana map[string]any `json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" label:"allowEmpty" file:"allowEmpty"`
Haystack map[string]any `json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" label:"allowEmpty" file:"allowEmpty"`
Elastic map[string]any `json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (t *tracing) deprecationNotice(logger zerolog.Logger) bool {
@ -523,6 +526,14 @@ func (t *tracing) deprecationNotice(logger zerolog.Logger) bool {
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.2/migration/v2-to-v3/#tracing")
}
if t.GlobalAttributes != nil {
log.Warn().Msgf("tracing.globalAttributes option is now deprecated, please use tracing.resourceAttributes instead.")
logger.Error().Msg("`tracing.globalAttributes` option has been deprecated in v3.3, and will be removed in the next major version." +
"Please use the `tracing.resourceAttributes` option instead." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.3/migration/v3/#tracing-global-attributes")
}
if t.Jaeger != nil {
incompatible = true
logger.Error().Msg("Jaeger Tracing backend has been removed in v3, please remove all Jaeger-related Tracing static configuration for Traefik to start." +

View File

@ -28,7 +28,6 @@ import (
"github.com/traefik/traefik/v3/pkg/provider/kv/zk"
"github.com/traefik/traefik/v3/pkg/provider/nomad"
"github.com/traefik/traefik/v3/pkg/provider/rest"
"github.com/traefik/traefik/v3/pkg/tracing/opentelemetry"
"github.com/traefik/traefik/v3/pkg/types"
)
@ -69,7 +68,7 @@ type Configuration struct {
Log *types.TraefikLog `description:"Traefik log settings." json:"log,omitempty" toml:"log,omitempty" yaml:"log,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
AccessLog *types.AccessLog `description:"Access log settings." json:"accessLog,omitempty" toml:"accessLog,omitempty" yaml:"accessLog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Tracing *Tracing `description:"OpenTracing configuration." json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Tracing *Tracing `description:"Tracing configuration." json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
HostResolver *types.HostResolverConfig `description:"Enable CNAME Flattening." json:"hostResolver,omitempty" toml:"hostResolver,omitempty" yaml:"hostResolver,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
@ -200,15 +199,17 @@ func (a *LifeCycle) SetDefaults() {
// Tracing holds the tracing configuration.
type Tracing struct {
ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
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"`
ServiceName string `description:"Sets the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
ResourceAttributes map[string]string `description:"Defines additional resource attributes (key:value)." json:"resourceAttributes,omitempty" toml:"resourceAttributes,omitempty" yaml:"resourceAttributes,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"`
OTLP *types.OTelTracing `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
OTLP *opentelemetry.Config `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
// Deprecated: please use ResourceAttributes instead.
GlobalAttributes map[string]string `description:"(Deprecated) Defines additional resource attributes (key:value)." json:"globalAttributes,omitempty" toml:"globalAttributes,omitempty" yaml:"globalAttributes,omitempty" export:"true"`
}
// SetDefaults sets the default values.
@ -216,7 +217,7 @@ func (t *Tracing) SetDefaults() {
t.ServiceName = "traefik"
t.SampleRate = 1.0
t.OTLP = &opentelemetry.Config{}
t.OTLP = &types.OTelTracing{}
t.OTLP.SetDefaults()
}
@ -270,6 +271,10 @@ func (c *Configuration) SetEffectiveConfiguration() {
}
}
if c.Tracing != nil && c.Tracing.GlobalAttributes != nil && c.Tracing.ResourceAttributes == nil {
c.Tracing.ResourceAttributes = c.Tracing.GlobalAttributes
}
if c.Providers.Docker != nil {
if c.Providers.Docker.HTTPClientTimeout < 0 {
c.Providers.Docker.HTTPClientTimeout = 0
@ -381,6 +386,18 @@ func (c *Configuration) ValidateConfiguration() error {
}
}
if c.AccessLog != nil && c.AccessLog.OTLP != nil {
if c.AccessLog.OTLP.GRPC != nil && c.AccessLog.OTLP.GRPC.TLS != nil && c.AccessLog.OTLP.GRPC.Insecure {
return errors.New("access logs OTLP GRPC: TLS and Insecure options are mutually exclusive")
}
}
if c.Log != nil && c.Log.OTLP != nil {
if c.Log.OTLP.GRPC != nil && c.Log.OTLP.GRPC.TLS != nil && c.Log.OTLP.GRPC.Insecure {
return errors.New("logs OTLP GRPC: TLS and Insecure options are mutually exclusive")
}
}
if c.Tracing != nil && c.Tracing.OTLP != nil {
if c.Tracing.OTLP.GRPC != nil && c.Tracing.OTLP.GRPC.TLS != nil && c.Tracing.OTLP.GRPC.Insecure {
return errors.New("tracing OTLP GRPC: TLS and Insecure options are mutually exclusive")

120
pkg/logs/otel.go Normal file
View File

@ -0,0 +1,120 @@
package logs
import (
"encoding/json"
"fmt"
"reflect"
"time"
"github.com/rs/zerolog"
"github.com/traefik/traefik/v3/pkg/types"
otellog "go.opentelemetry.io/otel/log"
)
// SetupOTelLogger sets up the OpenTelemetry logger.
func SetupOTelLogger(logger zerolog.Logger, config *types.OTelLog) (zerolog.Logger, error) {
if config == nil {
return logger, nil
}
provider, err := config.NewLoggerProvider()
if err != nil {
return zerolog.Logger{}, fmt.Errorf("setting up OpenTelemetry logger provider: %w", err)
}
return logger.Hook(&otelLoggerHook{logger: provider.Logger("traefik")}), nil
}
// otelLoggerHook is a zerolog hook that forwards logs to OpenTelemetry.
type otelLoggerHook struct {
logger otellog.Logger
}
// Run forwards the log message to OpenTelemetry.
func (h *otelLoggerHook) Run(e *zerolog.Event, level zerolog.Level, message string) {
if level == zerolog.Disabled {
return
}
// Discard the event to avoid double logging.
e.Discard()
var record otellog.Record
record.SetTimestamp(time.Now().UTC())
record.SetSeverity(otelLogSeverity(level))
record.SetBody(otellog.StringValue(message))
// See https://github.com/rs/zerolog/issues/493.
// This is a workaround to get the log fields from the event.
// At the moment there's no way to get the log fields from the event, so we use reflection to get the buffer and parse it.
logData := make(map[string]any)
eventBuffer := fmt.Sprintf("%s}", reflect.ValueOf(e).Elem().FieldByName("buf"))
if err := json.Unmarshal([]byte(eventBuffer), &logData); err != nil {
record.AddAttributes(otellog.String("parsing_error", fmt.Sprintf("parsing log fields: %s", err)))
h.logger.Emit(e.GetCtx(), record)
return
}
recordAttributes := make([]otellog.KeyValue, 0, len(logData))
for k, v := range logData {
if k == "level" {
continue
}
if k == "time" {
eventTimestamp, ok := v.(string)
if !ok {
continue
}
t, err := time.Parse(time.RFC3339, eventTimestamp)
if err == nil {
record.SetTimestamp(t)
continue
}
}
var attributeValue otellog.Value
switch v := v.(type) {
case string:
attributeValue = otellog.StringValue(v)
case int:
attributeValue = otellog.IntValue(v)
case int64:
attributeValue = otellog.Int64Value(v)
case float64:
attributeValue = otellog.Float64Value(v)
case bool:
attributeValue = otellog.BoolValue(v)
case []byte:
attributeValue = otellog.BytesValue(v)
default:
attributeValue = otellog.StringValue(fmt.Sprintf("%v", v))
}
recordAttributes = append(recordAttributes, otellog.KeyValue{
Key: k,
Value: attributeValue,
})
}
record.AddAttributes(recordAttributes...)
h.logger.Emit(e.GetCtx(), record)
}
func otelLogSeverity(level zerolog.Level) otellog.Severity {
switch level {
case zerolog.TraceLevel:
return otellog.SeverityTrace
case zerolog.DebugLevel:
return otellog.SeverityDebug
case zerolog.InfoLevel:
return otellog.SeverityInfo
case zerolog.WarnLevel:
return otellog.SeverityWarn
case zerolog.ErrorLevel:
return otellog.SeverityError
case zerolog.FatalLevel:
return otellog.SeverityFatal
case zerolog.PanicLevel:
return otellog.SeverityFatal4
default:
return otellog.SeverityUndefined
}
}

197
pkg/logs/otel_test.go Normal file
View File

@ -0,0 +1,197 @@
package logs
import (
"compress/gzip"
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v3/pkg/types"
"go.opentelemetry.io/collector/pdata/plog/plogotlp"
"go.opentelemetry.io/otel/trace"
)
func TestLog(t *testing.T) {
tests := []struct {
desc string
level zerolog.Level
assertFn func(*testing.T, string)
noLog bool
}{
{
desc: "no level log",
level: zerolog.NoLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityUndefined Severity = 0 // UNDEFINED
assert.NotContains(t, log, `"severityNumber"`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "trace log",
level: zerolog.TraceLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityTrace1 Severity = 1 // TRACE
assert.Contains(t, log, `"severityNumber":1`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "debug log",
level: zerolog.DebugLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityDebug1 Severity = 5 // DEBUG
assert.Contains(t, log, `"severityNumber":5`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "info log",
level: zerolog.InfoLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityInfo1 Severity = 9 // INFO
assert.Contains(t, log, `"severityNumber":9`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "warn log",
level: zerolog.WarnLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityWarn1 Severity = 13 // WARN
assert.Contains(t, log, `"severityNumber":13`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "error log",
level: zerolog.ErrorLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityError1 Severity = 17 // ERROR
assert.Contains(t, log, `"severityNumber":17`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "fatal log",
level: zerolog.FatalLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityFatal Severity = 21 // FATAL
assert.Contains(t, log, `"severityNumber":21`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
{
desc: "panic log",
level: zerolog.PanicLevel,
assertFn: func(t *testing.T, log string) {
t.Helper()
// SeverityFatal4 Severity = 24 // FATAL
assert.Contains(t, log, `"severityNumber":24`)
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `"body":{"stringValue":"test"}`, log)
assert.Regexp(t, `{"key":"foo","value":{"stringValue":"bar"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
},
},
}
logCh := make(chan string)
collector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gzr, err := gzip.NewReader(r.Body)
require.NoError(t, err)
body, err := io.ReadAll(gzr)
require.NoError(t, err)
req := plogotlp.NewExportRequest()
err = req.UnmarshalProto(body)
require.NoError(t, err)
marshalledReq, err := json.Marshal(req)
require.NoError(t, err)
logCh <- string(marshalledReq)
}))
t.Cleanup(collector.Close)
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
config := &types.OTelLog{
ServiceName: "test",
ResourceAttributes: map[string]string{"resource": "attribute"},
HTTP: &types.OTelHTTP{
Endpoint: collector.URL,
},
}
out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339})
logger := zerolog.New(out).With().Caller().Logger()
logger, err := SetupOTelLogger(logger, config)
require.NoError(t, err)
ctx := trace.ContextWithSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
SpanID: trace.SpanID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
}))
logger = logger.With().Ctx(ctx).Logger()
logger.WithLevel(test.level).Str("foo", "bar").Msg("test")
select {
case <-time.After(5 * time.Second):
t.Error("Log not exported")
case log := <-logCh:
if test.assertFn != nil {
test.assertFn(t, log)
}
}
})
}
}

View File

@ -237,7 +237,7 @@ func newOpenTelemetryMeterProvider(ctx context.Context, config *types.OTLP) (*sd
return meterProvider, nil
}
func newHTTPExporter(ctx context.Context, config *types.OtelHTTP) (sdkmetric.Exporter, error) {
func newHTTPExporter(ctx context.Context, config *types.OTelHTTP) (sdkmetric.Exporter, error) {
endpoint, err := url.Parse(config.Endpoint)
if err != nil {
return nil, fmt.Errorf("invalid collector endpoint %q: %w", config.Endpoint, err)
@ -269,7 +269,7 @@ func newHTTPExporter(ctx context.Context, config *types.OtelHTTP) (sdkmetric.Exp
return otlpmetrichttp.New(ctx, opts...)
}
func newGRPCExporter(ctx context.Context, config *types.OtelGRPC) (sdkmetric.Exporter, error) {
func newGRPCExporter(ctx context.Context, config *types.OTelGRPC) (sdkmetric.Exporter, error) {
host, port, err := net.SplitHostPort(config.Endpoint)
if err != nil {
return nil, fmt.Errorf("invalid collector endpoint %q: %w", config.Endpoint, err)

View File

@ -327,7 +327,7 @@ func TestOpenTelemetry(t *testing.T) {
var cfg types.OTLP
(&cfg).SetDefaults()
cfg.AddRoutersLabels = true
cfg.HTTP = &types.OtelHTTP{
cfg.HTTP = &types.OTelHTTP{
Endpoint: ts.URL,
}
cfg.PushInterval = ptypes.Duration(10 * time.Millisecond)

View File

@ -23,6 +23,7 @@ import (
"github.com/traefik/traefik/v3/pkg/middlewares/capture"
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
"github.com/traefik/traefik/v3/pkg/types"
"go.opentelemetry.io/contrib/bridges/otellogrus"
)
type key string
@ -52,6 +53,7 @@ func (n noopCloser) Close() error {
}
type handlerParams struct {
ctx context.Context
logDataTable *LogData
}
@ -106,6 +108,16 @@ func NewHandler(config *types.AccessLog) (*Handler, error) {
Level: logrus.InfoLevel,
}
if config.OTLP != nil {
otelLoggerProvider, err := config.OTLP.NewLoggerProvider()
if err != nil {
return nil, fmt.Errorf("setting up OpenTelemetry logger provider: %w", err)
}
logger.Hooks.Add(otellogrus.NewHook("traefik", otellogrus.WithLoggerProvider(otelLoggerProvider)))
logger.Out = io.Discard
}
// Transform header names to a canonical form, to be used as is without further transformations,
// and transform field names to lower case, to enable case-insensitive lookup.
if config.Fields != nil {
@ -150,7 +162,7 @@ func NewHandler(config *types.AccessLog) (*Handler, error) {
go func() {
defer logHandler.wg.Done()
for handlerParams := range logHandler.logHandlerChan {
logHandler.logTheRoundTrip(handlerParams.logDataTable)
logHandler.logTheRoundTrip(handlerParams.ctx, handlerParams.logDataTable)
}
}()
}
@ -256,12 +268,13 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
if h.config.BufferingSize > 0 {
h.logHandlerChan <- handlerParams{
ctx: req.Context(),
logDataTable: logDataTable,
}
return
}
h.logTheRoundTrip(logDataTable)
h.logTheRoundTrip(req.Context(), logDataTable)
}()
next.ServeHTTP(rw, reqWithDataTable)
@ -313,7 +326,7 @@ func usernameIfPresent(theURL *url.URL) string {
}
// Logging handler to log frontend name, backend name, and elapsed time.
func (h *Handler) logTheRoundTrip(logDataTable *LogData) {
func (h *Handler) logTheRoundTrip(ctx context.Context, logDataTable *LogData) {
core := logDataTable.Core
retryAttempts, ok := core[RetryAttempts].(int)
@ -359,7 +372,7 @@ func (h *Handler) logTheRoundTrip(logDataTable *LogData) {
h.mu.Lock()
defer h.mu.Unlock()
h.logger.WithFields(fields).Println()
h.logger.WithContext(ctx).WithFields(fields).Println()
}
}

View File

@ -2,6 +2,7 @@ package accesslog
import (
"bytes"
"compress/gzip"
"context"
"crypto/tls"
"crypto/x509"
@ -25,6 +26,8 @@ import (
ptypes "github.com/traefik/paerser/types"
"github.com/traefik/traefik/v3/pkg/middlewares/capture"
"github.com/traefik/traefik/v3/pkg/types"
"go.opentelemetry.io/collector/pdata/plog/plogotlp"
"go.opentelemetry.io/otel/trace"
)
const delta float64 = 1e-10
@ -49,6 +52,75 @@ var (
testStart = time.Now()
)
func TestOTelAccessLog(t *testing.T) {
logCh := make(chan string)
collector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gzr, err := gzip.NewReader(r.Body)
require.NoError(t, err)
body, err := io.ReadAll(gzr)
require.NoError(t, err)
req := plogotlp.NewExportRequest()
err = req.UnmarshalProto(body)
require.NoError(t, err)
marshalledReq, err := json.Marshal(req)
require.NoError(t, err)
logCh <- string(marshalledReq)
}))
t.Cleanup(collector.Close)
config := &types.AccessLog{
OTLP: &types.OTelLog{
ServiceName: "test",
ResourceAttributes: map[string]string{"resource": "attribute"},
HTTP: &types.OTelHTTP{
Endpoint: collector.URL,
},
},
}
logHandler, err := NewHandler(config)
require.NoError(t, err)
t.Cleanup(func() {
err := logHandler.Close()
require.NoError(t, err)
})
req := &http.Request{
Header: map[string][]string{},
URL: &url.URL{
Path: testPath,
},
}
ctx := trace.ContextWithSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
SpanID: trace.SpanID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
}))
req = req.WithContext(ctx)
chain := alice.New()
chain = chain.Append(capture.Wrap)
chain = chain.Append(WrapHandler(logHandler))
handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusOK)
}))
require.NoError(t, err)
handler.ServeHTTP(httptest.NewRecorder(), req)
select {
case <-time.After(5 * time.Second):
t.Error("AccessLog not exported")
case log := <-logCh:
assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log)
assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log)
assert.Regexp(t, `{"key":"DownstreamStatus","value":{"intValue":"200"}}`, log)
assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log)
}
}
func TestLogRotation(t *testing.T) {
fileName := filepath.Join(t.TempDir(), "traefik.log")
rotatedFileName := fileName + ".rotated"

View File

@ -9,6 +9,7 @@ import (
"time"
"github.com/containous/alice"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/metrics"
"github.com/traefik/traefik/v3/pkg/middlewares"
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
@ -64,7 +65,11 @@ func (e *entryPointTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request)
start := time.Now()
tracingCtx, span := e.tracer.Start(tracingCtx, "EntryPoint", trace.WithSpanKind(trace.SpanKindServer), trace.WithTimestamp(start))
req = req.WithContext(tracingCtx)
// Associate the request context with the logger.
logger := log.Ctx(tracingCtx).With().Ctx(tracingCtx).Logger()
loggerCtx := logger.WithContext(tracingCtx)
req = req.WithContext(loggerCtx)
span.SetAttributes(attribute.String("entry_point", e.entryPoint))

View File

@ -30,7 +30,6 @@ import (
"github.com/traefik/traefik/v3/pkg/provider/kv/zk"
"github.com/traefik/traefik/v3/pkg/provider/rest"
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
"github.com/traefik/traefik/v3/pkg/tracing/opentelemetry"
"github.com/traefik/traefik/v3/pkg/types"
)
@ -831,6 +830,25 @@ func TestDo_staticConfiguration(t *testing.T) {
MaxAge: 3,
MaxBackups: 4,
Compress: true,
OTLP: &types.OTelLog{
ServiceName: "foobar",
ResourceAttributes: map[string]string{
"foobar": "foobar",
},
GRPC: &types.OTelGRPC{
Endpoint: "foobar",
Insecure: true,
Headers: map[string]string{
"foobar": "foobar",
},
},
HTTP: &types.OTelHTTP{
Endpoint: "foobar",
Headers: map[string]string{
"foobar": "foobar",
},
},
},
}
config.AccessLog = &types.AccessLog{
@ -854,18 +872,46 @@ func TestDo_staticConfiguration(t *testing.T) {
},
},
BufferingSize: 42,
OTLP: &types.OTelLog{
ServiceName: "foobar",
ResourceAttributes: map[string]string{
"foobar": "foobar",
},
GRPC: &types.OTelGRPC{
Endpoint: "foobar",
Insecure: true,
Headers: map[string]string{
"foobar": "foobar",
},
},
HTTP: &types.OTelHTTP{
Endpoint: "foobar",
Headers: map[string]string{
"foobar": "foobar",
},
},
},
}
config.Tracing = &static.Tracing{
ServiceName: "myServiceName",
ResourceAttributes: map[string]string{
"foobar": "foobar",
},
GlobalAttributes: map[string]string{
"foobar": "foobar",
},
SampleRate: 42,
OTLP: &opentelemetry.Config{
HTTP: &types.OtelHTTP{
OTLP: &types.OTelTracing{
HTTP: &types.OTelHTTP{
Endpoint: "foobar",
TLS: nil,
Headers: map[string]string{
"foobar": "foobar",
},
},
GRPC: &types.OTelGRPC{
Endpoint: "foobar",
Insecure: true,
Headers: map[string]string{
"foobar": "foobar",
},

View File

@ -315,7 +315,17 @@
"maxSize": 5,
"maxAge": 3,
"maxBackups": 4,
"compress": true
"compress": true,
"otlp": {
"serviceName": "foobar",
"grpc": {
"endpoint": "xxxx",
"insecure": true
},
"http": {
"endpoint": "xxxx"
}
}
},
"accessLog": {
"filePath": "xxxx",
@ -340,18 +350,35 @@
}
}
},
"bufferingSize": 42
"bufferingSize": 42,
"otlp": {
"serviceName": "foobar",
"grpc": {
"endpoint": "xxxx",
"insecure": true
},
"http": {
"endpoint": "xxxx"
}
}
},
"tracing": {
"serviceName": "myServiceName",
"globalAttributes": {
"resourceAttributes": {
"foobar": "foobar"
},
"sampleRate": 42,
"otlp": {
"grpc": {
"endpoint": "xxxx",
"insecure": true
},
"http": {
"endpoint": "xxxx"
}
},
"globalAttributes": {
"foobar": "foobar"
}
},
"hostResolver": {
@ -370,11 +397,11 @@
"certificatesDuration": 42,
"dnsChallenge": {
"provider": "DNSProvider",
"delayBeforeCheck": "42ns",
"resolvers": [
"xxxx",
"xxxx"
],
"delayBeforeCheck": "42ns",
"disablePropagationCheck": true
},
"httpChallenge": {

View File

@ -54,6 +54,12 @@ func (o *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName stri
}
}
// As the Entry point observability middleware ensures that the tracing is added to the request and logger context,
// it needs to be added before the access log middleware to ensure that the trace ID is logged.
if (o.tracer != nil && o.ShouldAddTracing(resourceName)) || (o.metricsRegistry != nil && o.metricsRegistry.IsEpEnabled() && o.ShouldAddMetrics(resourceName)) {
chain = chain.Append(observability.WrapEntryPointHandler(ctx, o.tracer, o.semConvMetricRegistry, entryPointName))
}
if o.accessLoggerMiddleware != nil && o.ShouldAddAccessLogs(resourceName) {
chain = chain.Append(accesslog.WrapHandler(o.accessLoggerMiddleware))
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
@ -61,10 +67,6 @@ func (o *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName stri
})
}
if (o.tracer != nil && o.ShouldAddTracing(resourceName)) || (o.metricsRegistry != nil && o.metricsRegistry.IsEpEnabled() && o.ShouldAddMetrics(resourceName)) {
chain = chain.Append(observability.WrapEntryPointHandler(ctx, o.tracer, o.semConvMetricRegistry, entryPointName))
}
if o.metricsRegistry != nil && o.metricsRegistry.IsEpEnabled() && o.ShouldAddMetrics(resourceName) {
metricsHandler := metricsMiddle.WrapEntryPointHandler(ctx, o.metricsRegistry, entryPointName)

View File

@ -1,324 +0,0 @@
package opentelemetry_test
import (
"compress/gzip"
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/containous/alice"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v3/pkg/config/static"
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
"github.com/traefik/traefik/v3/pkg/tracing"
"github.com/traefik/traefik/v3/pkg/tracing/opentelemetry"
"github.com/traefik/traefik/v3/pkg/types"
"go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp"
)
func TestTracing(t *testing.T) {
tests := []struct {
desc string
propagators string
headers map[string]string
wantServiceHeadersFn func(t *testing.T, headers http.Header)
assertFn func(*testing.T, string)
}{
{
desc: "service name and version",
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `({"key":"service.name","value":{"stringValue":"traefik"}})`, trace)
assert.Regexp(t, `({"key":"service.version","value":{"stringValue":"dev"}})`, trace)
},
},
{
desc: "TraceContext propagation",
propagators: "tracecontext",
headers: map[string]string{
"traceparent": "00-00000000000000000000000000000001-0000000000000001-01",
"tracestate": "foo=bar",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(00-00000000000000000000000000000001-\w{16}-01)`, headers["Traceparent"][0])
assert.Equal(t, []string{"foo=bar"}, headers["Tracestate"])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace)
assert.Regexp(t, `("parentSpanId":"0000000000000001")`, trace)
assert.Regexp(t, `("traceState":"foo=bar")`, trace)
},
},
{
desc: "root span TraceContext propagation",
propagators: "tracecontext",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(00-\w{32}-\w{16}-01)`, headers["Traceparent"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "B3 propagation",
propagators: "b3",
headers: map[string]string{
"b3": "00000000000000000000000000000001-0000000000000002-1-0000000000000001",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(00000000000000000000000000000001-\w{16}-1)`, headers["B3"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace)
assert.Regexp(t, `("parentSpanId":"0000000000000002")`, trace)
},
},
{
desc: "root span B3 propagation",
propagators: "b3",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(\w{32}-\w{16}-1)`, headers["B3"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "B3 propagation Multiple Headers",
propagators: "b3multi",
headers: map[string]string{
"x-b3-traceid": "00000000000000000000000000000001",
"x-b3-parentspanid": "0000000000000001",
"x-b3-spanid": "0000000000000002",
"x-b3-sampled": "1",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Equal(t, "00000000000000000000000000000001", headers["X-B3-Traceid"][0])
assert.Equal(t, "0000000000000001", headers["X-B3-Parentspanid"][0])
assert.Equal(t, "1", headers["X-B3-Sampled"][0])
assert.Len(t, headers["X-B3-Spanid"][0], 16)
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace)
assert.Regexp(t, `("parentSpanId":"0000000000000002")`, trace)
},
},
{
desc: "root span B3 propagation Multiple Headers",
propagators: "b3multi",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(\w{32})`, headers["X-B3-Traceid"][0])
assert.Equal(t, "1", headers["X-B3-Sampled"][0])
assert.Regexp(t, `(\w{16})`, headers["X-B3-Spanid"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"")`, trace)
},
},
{
desc: "Baggage propagation",
propagators: "baggage",
headers: map[string]string{
"baggage": "userId=id",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Equal(t, []string{"userId=id"}, headers["Baggage"])
},
},
{
desc: "Jaeger propagation",
propagators: "jaeger",
headers: map[string]string{
"uber-trace-id": "00000000000000000000000000000001:0000000000000002:0000000000000001:1",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(00000000000000000000000000000001:\w{16}:0:1)`, headers["Uber-Trace-Id"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "root span Jaeger propagation",
propagators: "jaeger",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(\w{32}:\w{16}:0:1)`, headers["Uber-Trace-Id"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "XRay propagation",
propagators: "xray",
headers: map[string]string{
"X-Amzn-Trace-Id": "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(Root=1-5759e988-bd862e3fe1be46a994272793;Parent=\w{16};Sampled=1)`, headers["X-Amzn-Trace-Id"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"5759e988bd862e3fe1be46a994272793")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "root span XRay propagation",
propagators: "xray",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(Root=1-\w{8}-\w{24};Parent=\w{16};Sampled=1)`, headers["X-Amzn-Trace-Id"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "no propagation",
propagators: "none",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Empty(t, headers)
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
}
traceCh := make(chan string)
collector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gzr, err := gzip.NewReader(r.Body)
require.NoError(t, err)
body, err := io.ReadAll(gzr)
require.NoError(t, err)
req := ptraceotlp.NewExportRequest()
err = req.UnmarshalProto(body)
require.NoError(t, err)
marshalledReq, err := json.Marshal(req)
require.NoError(t, err)
traceCh <- string(marshalledReq)
}))
t.Cleanup(collector.Close)
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
t.Setenv("OTEL_PROPAGATORS", test.propagators)
service := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tracer := tracing.TracerFromContext(r.Context())
ctx, span := tracer.Start(r.Context(), "service")
defer span.End()
r = r.WithContext(ctx)
tracing.InjectContextIntoCarrier(r)
if test.wantServiceHeadersFn != nil {
test.wantServiceHeadersFn(t, r.Header)
}
})
tracingConfig := &static.Tracing{
ServiceName: "traefik",
SampleRate: 1.0,
OTLP: &opentelemetry.Config{
HTTP: &types.OtelHTTP{
Endpoint: collector.URL,
},
},
}
newTracing, closer, err := tracing.NewTracing(tracingConfig)
require.NoError(t, err)
t.Cleanup(func() {
_ = closer.Close()
})
chain := alice.New(observability.WrapEntryPointHandler(context.Background(), newTracing, nil, "test"))
epHandler, err := chain.Then(service)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "http://www.test.com", nil)
for k, v := range test.headers {
req.Header.Set(k, v)
}
rw := httptest.NewRecorder()
epHandler.ServeHTTP(rw, req)
select {
case <-time.After(10 * time.Second):
t.Error("Trace not exported")
case trace := <-traceCh:
assert.Equal(t, http.StatusOK, rw.Code)
if test.assertFn != nil {
test.assertFn(t, trace)
}
}
})
}
}

View File

@ -13,7 +13,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/config/static"
"github.com/traefik/traefik/v3/pkg/tracing/opentelemetry"
"github.com/traefik/traefik/v3/pkg/types"
"go.opentelemetry.io/contrib/propagators/autoprop"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
@ -38,7 +38,7 @@ func NewTracing(conf *static.Tracing) (*Tracer, io.Closer, error) {
if backend == nil {
log.Debug().Msg("Could not initialize tracing, using OpenTelemetry by default")
defaultBackend := &opentelemetry.Config{}
defaultBackend := &types.OTelTracing{}
backend = defaultBackend
}

View File

@ -1,10 +1,22 @@
package tracing
import (
"compress/gzip"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
"github.com/containous/alice"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v3/pkg/config/static"
"github.com/traefik/traefik/v3/pkg/types"
"go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp"
"go.opentelemetry.io/otel/trace"
)
func Test_safeFullURL(t *testing.T) {
@ -55,3 +67,315 @@ func Test_safeFullURL(t *testing.T) {
})
}
}
func TestTracing(t *testing.T) {
tests := []struct {
desc string
propagators string
headers map[string]string
wantServiceHeadersFn func(t *testing.T, headers http.Header)
assertFn func(*testing.T, string)
}{
{
desc: "service name and version",
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `({"key":"service.name","value":{"stringValue":"traefik"}})`, trace)
assert.Regexp(t, `({"key":"service.version","value":{"stringValue":"dev"}})`, trace)
},
},
{
desc: "TraceContext propagation",
propagators: "tracecontext",
headers: map[string]string{
"traceparent": "00-00000000000000000000000000000001-0000000000000001-01",
"tracestate": "foo=bar",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(00-00000000000000000000000000000001-\w{16}-01)`, headers["Traceparent"][0])
assert.Equal(t, []string{"foo=bar"}, headers["Tracestate"])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace)
assert.Regexp(t, `("parentSpanId":"0000000000000001")`, trace)
assert.Regexp(t, `("traceState":"foo=bar")`, trace)
},
},
{
desc: "root span TraceContext propagation",
propagators: "tracecontext",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(00-\w{32}-\w{16}-01)`, headers["Traceparent"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "B3 propagation",
propagators: "b3",
headers: map[string]string{
"b3": "00000000000000000000000000000001-0000000000000002-1-0000000000000001",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(00000000000000000000000000000001-\w{16}-1)`, headers["B3"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace)
assert.Regexp(t, `("parentSpanId":"0000000000000002")`, trace)
},
},
{
desc: "root span B3 propagation",
propagators: "b3",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(\w{32}-\w{16}-1)`, headers["B3"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "B3 propagation Multiple Headers",
propagators: "b3multi",
headers: map[string]string{
"x-b3-traceid": "00000000000000000000000000000001",
"x-b3-parentspanid": "0000000000000001",
"x-b3-spanid": "0000000000000002",
"x-b3-sampled": "1",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Equal(t, "00000000000000000000000000000001", headers["X-B3-Traceid"][0])
assert.Equal(t, "0000000000000001", headers["X-B3-Parentspanid"][0])
assert.Equal(t, "1", headers["X-B3-Sampled"][0])
assert.Len(t, headers["X-B3-Spanid"][0], 16)
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace)
assert.Regexp(t, `("parentSpanId":"0000000000000002")`, trace)
},
},
{
desc: "root span B3 propagation Multiple Headers",
propagators: "b3multi",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(\w{32})`, headers["X-B3-Traceid"][0])
assert.Equal(t, "1", headers["X-B3-Sampled"][0])
assert.Regexp(t, `(\w{16})`, headers["X-B3-Spanid"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"")`, trace)
},
},
{
desc: "Baggage propagation",
propagators: "baggage",
headers: map[string]string{
"baggage": "userId=id",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Equal(t, []string{"userId=id"}, headers["Baggage"])
},
},
{
desc: "Jaeger propagation",
propagators: "jaeger",
headers: map[string]string{
"uber-trace-id": "00000000000000000000000000000001:0000000000000002:0000000000000001:1",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(00000000000000000000000000000001:\w{16}:0:1)`, headers["Uber-Trace-Id"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "root span Jaeger propagation",
propagators: "jaeger",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(\w{32}:\w{16}:0:1)`, headers["Uber-Trace-Id"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "XRay propagation",
propagators: "xray",
headers: map[string]string{
"X-Amzn-Trace-Id": "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1",
},
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(Root=1-5759e988-bd862e3fe1be46a994272793;Parent=\w{16};Sampled=1)`, headers["X-Amzn-Trace-Id"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"5759e988bd862e3fe1be46a994272793")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "root span XRay propagation",
propagators: "xray",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Regexp(t, `(Root=1-\w{8}-\w{24};Parent=\w{16};Sampled=1)`, headers["X-Amzn-Trace-Id"][0])
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
{
desc: "no propagation",
propagators: "none",
wantServiceHeadersFn: func(t *testing.T, headers http.Header) {
t.Helper()
assert.Empty(t, headers)
},
assertFn: func(t *testing.T, trace string) {
t.Helper()
assert.Regexp(t, `("traceId":"\w{32}")`, trace)
assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace)
},
},
}
traceCh := make(chan string)
collector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gzr, err := gzip.NewReader(r.Body)
require.NoError(t, err)
body, err := io.ReadAll(gzr)
require.NoError(t, err)
req := ptraceotlp.NewExportRequest()
err = req.UnmarshalProto(body)
require.NoError(t, err)
marshalledReq, err := json.Marshal(req)
require.NoError(t, err)
traceCh <- string(marshalledReq)
}))
t.Cleanup(collector.Close)
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
t.Setenv("OTEL_PROPAGATORS", test.propagators)
service := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tracer := TracerFromContext(r.Context())
ctx, span := tracer.Start(r.Context(), "service")
defer span.End()
r = r.WithContext(ctx)
InjectContextIntoCarrier(r)
if test.wantServiceHeadersFn != nil {
test.wantServiceHeadersFn(t, r.Header)
}
})
tracingConfig := &static.Tracing{
ServiceName: "traefik",
SampleRate: 1.0,
OTLP: &types.OTelTracing{
HTTP: &types.OTelHTTP{
Endpoint: collector.URL,
},
},
}
tracer, closer, err := NewTracing(tracingConfig)
require.NoError(t, err)
t.Cleanup(func() {
_ = closer.Close()
})
chain := alice.New(func(next http.Handler) (http.Handler, error) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tracingCtx := ExtractCarrierIntoContext(r.Context(), r.Header)
start := time.Now()
tracingCtx, span := tracer.Start(tracingCtx, "test", trace.WithSpanKind(trace.SpanKindServer), trace.WithTimestamp(start))
end := time.Now()
span.End(trace.WithTimestamp(end))
next.ServeHTTP(w, r.WithContext(tracingCtx))
}), nil
})
epHandler, err := chain.Then(service)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "http://www.test.com", nil)
for k, v := range test.headers {
req.Header.Set(k, v)
}
rw := httptest.NewRecorder()
epHandler.ServeHTTP(rw, req)
select {
case <-time.After(10 * time.Second):
t.Error("Trace not exported")
case trace := <-traceCh:
assert.Equal(t, http.StatusOK, rw.Code)
if test.assertFn != nil {
test.assertFn(t, trace)
}
}
})
}
}

View File

@ -1,6 +1,22 @@
package types
import "github.com/traefik/paerser/types"
import (
"context"
"fmt"
"net"
"net/url"
"github.com/traefik/paerser/types"
"github.com/traefik/traefik/v3/pkg/version"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
otelsdk "go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.27.0"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/encoding/gzip"
)
const (
// AccessLogKeep is the keep string value.
@ -14,11 +30,10 @@ const (
const (
// CommonFormat is the common logging format (CLF).
CommonFormat string = "common"
// JSONFormat is the JSON logging format.
JSONFormat string = "json"
)
const OTelTraefikServiceName = "traefik"
// TraefikLog holds the configuration settings for the traefik logger.
type TraefikLog struct {
Level string `description:"Log level set to traefik logs." json:"level,omitempty" toml:"level,omitempty" yaml:"level,omitempty" export:"true"`
@ -30,6 +45,8 @@ type TraefikLog struct {
MaxAge int `description:"Maximum number of days to retain old log files based on the timestamp encoded in their filename." json:"maxAge,omitempty" toml:"maxAge,omitempty" yaml:"maxAge,omitempty" export:"true"`
MaxBackups int `description:"Maximum number of old log files to retain." json:"maxBackups,omitempty" toml:"maxBackups,omitempty" yaml:"maxBackups,omitempty" export:"true"`
Compress bool `description:"Determines if the rotated log files should be compressed using gzip." json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" export:"true"`
OTLP *OTelLog `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// SetDefaults sets the default values.
@ -46,6 +63,8 @@ type AccessLog struct {
Fields *AccessLogFields `description:"AccessLogFields." json:"fields,omitempty" toml:"fields,omitempty" yaml:"fields,omitempty" export:"true"`
BufferingSize int64 `description:"Number of access log lines to process in a buffered way." json:"bufferingSize,omitempty" toml:"bufferingSize,omitempty" yaml:"bufferingSize,omitempty" export:"true"`
AddInternals bool `description:"Enables access log for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"`
OTLP *OTelLog `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// SetDefaults sets the default values.
@ -128,3 +147,123 @@ func checkFieldHeaderValue(value, defaultValue string) string {
}
return defaultValue
}
// OTelLog provides configuration settings for the open-telemetry logger.
type OTelLog struct {
ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
ResourceAttributes map[string]string `description:"Defines additional resource attributes (key:value)." json:"resourceAttributes,omitempty" toml:"resourceAttributes,omitempty" yaml:"resourceAttributes,omitempty"`
GRPC *OTelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
HTTP *OTelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// SetDefaults sets the default values.
func (o *OTelLog) SetDefaults() {
o.ServiceName = OTelTraefikServiceName
o.HTTP = &OTelHTTP{}
o.HTTP.SetDefaults()
}
// NewLoggerProvider creates a new OpenTelemetry logger provider.
func (o *OTelLog) NewLoggerProvider() (*otelsdk.LoggerProvider, error) {
var (
err error
exporter otelsdk.Exporter
)
if o.GRPC != nil {
exporter, err = o.buildGRPCExporter()
} else {
exporter, err = o.buildHTTPExporter()
}
if err != nil {
return nil, fmt.Errorf("setting up exporter: %w", err)
}
attr := []attribute.KeyValue{
semconv.ServiceNameKey.String(o.ServiceName),
semconv.ServiceVersionKey.String(version.Version),
}
for k, v := range o.ResourceAttributes {
attr = append(attr, attribute.String(k, v))
}
res, err := resource.New(context.Background(),
resource.WithAttributes(attr...),
resource.WithFromEnv(),
resource.WithTelemetrySDK(),
resource.WithOSType(),
resource.WithProcessCommandArgs(),
)
if err != nil {
return nil, fmt.Errorf("building resource: %w", err)
}
// Register the trace provider to allow the global logger to access it.
bp := otelsdk.NewBatchProcessor(exporter)
loggerProvider := otelsdk.NewLoggerProvider(
otelsdk.WithResource(res),
otelsdk.WithProcessor(bp),
)
return loggerProvider, nil
}
func (o *OTelLog) buildHTTPExporter() (*otlploghttp.Exporter, error) {
endpoint, err := url.Parse(o.HTTP.Endpoint)
if err != nil {
return nil, fmt.Errorf("invalid collector endpoint %q: %w", o.HTTP.Endpoint, err)
}
opts := []otlploghttp.Option{
otlploghttp.WithEndpoint(endpoint.Host),
otlploghttp.WithHeaders(o.HTTP.Headers),
otlploghttp.WithCompression(otlploghttp.GzipCompression),
}
if endpoint.Scheme == "http" {
opts = append(opts, otlploghttp.WithInsecure())
}
if endpoint.Path != "" {
opts = append(opts, otlploghttp.WithURLPath(endpoint.Path))
}
if o.HTTP.TLS != nil {
tlsConfig, err := o.HTTP.TLS.CreateTLSConfig(context.Background())
if err != nil {
return nil, fmt.Errorf("creating TLS client config: %w", err)
}
opts = append(opts, otlploghttp.WithTLSClientConfig(tlsConfig))
}
return otlploghttp.New(context.Background(), opts...)
}
func (o *OTelLog) buildGRPCExporter() (*otlploggrpc.Exporter, error) {
host, port, err := net.SplitHostPort(o.GRPC.Endpoint)
if err != nil {
return nil, fmt.Errorf("invalid collector endpoint %q: %w", o.GRPC.Endpoint, err)
}
opts := []otlploggrpc.Option{
otlploggrpc.WithEndpoint(fmt.Sprintf("%s:%s", host, port)),
otlploggrpc.WithHeaders(o.GRPC.Headers),
otlploggrpc.WithCompressor(gzip.Name),
}
if o.GRPC.Insecure {
opts = append(opts, otlploggrpc.WithInsecure())
}
if o.GRPC.TLS != nil {
tlsConfig, err := o.GRPC.TLS.CreateTLSConfig(context.Background())
if err != nil {
return nil, fmt.Errorf("creating TLS client config: %w", err)
}
opts = append(opts, otlploggrpc.WithTLSCredentials(credentials.NewTLS(tlsConfig)))
}
return otlploggrpc.New(context.Background(), opts...)
}

View File

@ -108,8 +108,8 @@ func (i *InfluxDB2) SetDefaults() {
// OTLP contains specific configuration used by the OpenTelemetry Metrics exporter.
type OTLP struct {
GRPC *OtelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
HTTP *OtelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
GRPC *OTelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
HTTP *OTelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"`
@ -121,14 +121,14 @@ type OTLP struct {
// SetDefaults sets the default values.
func (o *OTLP) SetDefaults() {
o.HTTP = &OtelHTTP{}
o.HTTP = &OTelHTTP{}
o.HTTP.SetDefaults()
o.AddEntryPointsLabels = true
o.AddServicesLabels = true
o.ExplicitBoundaries = []float64{.005, .01, .025, .05, .075, .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10}
o.PushInterval = types.Duration(10 * time.Second)
o.ServiceName = "traefik"
o.ServiceName = OTelTraefikServiceName
}
// Statistics provides options for monitoring request and response stats.
@ -140,28 +140,3 @@ type Statistics struct {
func (s *Statistics) SetDefaults() {
s.RecentErrors = 10
}
// OtelGRPC provides configuration settings for the gRPC open-telemetry.
type OtelGRPC struct {
Endpoint string `description:"Sets the gRPC endpoint (host:port) of the collector." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
Insecure bool `description:"Disables client transport security for the exporter." json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty" export:"true"`
TLS *ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
Headers map[string]string `description:"Headers sent with payload." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"`
}
// SetDefaults sets the default values.
func (c *OtelGRPC) SetDefaults() {
c.Endpoint = "localhost:4317"
}
// OtelHTTP provides configuration settings for the HTTP open-telemetry.
type OtelHTTP struct {
Endpoint string `description:"Sets the HTTP endpoint (scheme://host:port/path) of the collector." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
TLS *ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
Headers map[string]string `description:"Headers sent with payload." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"`
}
// SetDefaults sets the default values.
func (c *OtelHTTP) SetDefaults() {
c.Endpoint = "https://localhost:4318"
}

26
pkg/types/otel.go Normal file
View File

@ -0,0 +1,26 @@
package types
// OTelGRPC provides configuration settings for the gRPC open-telemetry.
type OTelGRPC struct {
Endpoint string `description:"Sets the gRPC endpoint (host:port) of the collector." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
Insecure bool `description:"Disables client transport security for the exporter." json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty" export:"true"`
TLS *ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
Headers map[string]string `description:"Headers sent with payload." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"`
}
// SetDefaults sets the default values.
func (o *OTelGRPC) SetDefaults() {
o.Endpoint = "localhost:4317"
}
// OTelHTTP provides configuration settings for the HTTP open-telemetry.
type OTelHTTP struct {
Endpoint string `description:"Sets the HTTP endpoint (scheme://host:port/path) of the collector." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
TLS *ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
Headers map[string]string `description:"Headers sent with payload." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"`
}
// SetDefaults sets the default values.
func (o *OTelHTTP) SetDefaults() {
o.Endpoint = "https://localhost:4318"
}

View File

@ -1,4 +1,4 @@
package opentelemetry
package types
import (
"context"
@ -9,7 +9,6 @@ import (
"time"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/types"
"github.com/traefik/traefik/v3/pkg/version"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
@ -18,26 +17,26 @@ 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.26.0"
semconv "go.opentelemetry.io/otel/semconv/v1.27.0"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/encoding/gzip"
)
// Config provides configuration settings for the open-telemetry tracer.
type Config struct {
GRPC *types.OtelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
HTTP *types.OtelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
// OTelTracing provides configuration settings for the open-telemetry tracer.
type OTelTracing struct {
GRPC *OTelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
HTTP *OTelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// SetDefaults sets the default values.
func (c *Config) SetDefaults() {
c.HTTP = &types.OtelHTTP{}
func (c *OTelTracing) SetDefaults() {
c.HTTP = &OTelHTTP{}
c.HTTP.SetDefaults()
}
// Setup sets up the tracer.
func (c *Config) Setup(serviceName string, sampleRate float64, globalAttributes map[string]string) (trace.Tracer, io.Closer, error) {
func (c *OTelTracing) Setup(serviceName string, sampleRate float64, globalAttributes map[string]string) (trace.Tracer, io.Closer, error) {
var (
err error
exporter *otlptrace.Exporter
@ -87,7 +86,7 @@ func (c *Config) Setup(serviceName string, sampleRate float64, globalAttributes
return tracerProvider.Tracer("github.com/traefik/traefik"), &tpCloser{provider: tracerProvider}, err
}
func (c *Config) setupHTTPExporter() (*otlptrace.Exporter, error) {
func (c *OTelTracing) setupHTTPExporter() (*otlptrace.Exporter, error) {
endpoint, err := url.Parse(c.HTTP.Endpoint)
if err != nil {
return nil, fmt.Errorf("invalid collector endpoint %q: %w", c.HTTP.Endpoint, err)
@ -119,7 +118,7 @@ func (c *Config) setupHTTPExporter() (*otlptrace.Exporter, error) {
return otlptrace.New(context.Background(), otlptracehttp.NewClient(opts...))
}
func (c *Config) setupGRPCExporter() (*otlptrace.Exporter, error) {
func (c *OTelTracing) setupGRPCExporter() (*otlptrace.Exporter, error) {
host, port, err := net.SplitHostPort(c.GRPC.Endpoint)
if err != nil {
return nil, fmt.Errorf("invalid collector endpoint %q: %w", c.GRPC.Endpoint, err)