Remove observability for internal resources
This commit is contained in:
parent
d02be003ab
commit
8b77f0c2dd
36 changed files with 594 additions and 317 deletions
|
@ -193,10 +193,13 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||||
|
|
||||||
tsProviders := initTailscaleProviders(staticConfiguration, &providerAggregator)
|
tsProviders := initTailscaleProviders(staticConfiguration, &providerAggregator)
|
||||||
|
|
||||||
// Metrics
|
// Observability
|
||||||
|
|
||||||
metricRegistries := registerMetricClients(staticConfiguration.Metrics)
|
metricRegistries := registerMetricClients(staticConfiguration.Metrics)
|
||||||
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
|
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
|
||||||
|
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||||
|
tracer, tracerCloser := setupTracing(staticConfiguration.Tracing)
|
||||||
|
observabilityMgr := middleware.NewObservabilityMgr(*staticConfiguration, metricsRegistry, accessLog, tracer, tracerCloser)
|
||||||
|
|
||||||
// Entrypoints
|
// Entrypoints
|
||||||
|
|
||||||
|
@ -263,14 +266,11 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||||
roundTripperManager := service.NewRoundTripperManager(spiffeX509Source)
|
roundTripperManager := service.NewRoundTripperManager(spiffeX509Source)
|
||||||
dialerManager := tcp.NewDialerManager(spiffeX509Source)
|
dialerManager := tcp.NewDialerManager(spiffeX509Source)
|
||||||
acmeHTTPHandler := getHTTPChallengeHandler(acmeProviders, httpChallengeProvider)
|
acmeHTTPHandler := getHTTPChallengeHandler(acmeProviders, httpChallengeProvider)
|
||||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry, roundTripperManager, acmeHTTPHandler)
|
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, observabilityMgr, roundTripperManager, acmeHTTPHandler)
|
||||||
|
|
||||||
// Router factory
|
// Router factory
|
||||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
|
||||||
|
|
||||||
tracer, tracerCloser := setupTracing(staticConfiguration.Tracing)
|
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, observabilityMgr, pluginBuilder, dialerManager)
|
||||||
chainBuilder := middleware.NewChainBuilder(metricsRegistry, accessLog, tracer)
|
|
||||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder, pluginBuilder, metricsRegistry, dialerManager)
|
|
||||||
|
|
||||||
// Watcher
|
// Watcher
|
||||||
|
|
||||||
|
@ -351,7 +351,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return server.NewServer(routinesPool, serverEntryPointsTCP, serverEntryPointsUDP, watcher, chainBuilder, accessLog, tracerCloser), nil
|
return server.NewServer(routinesPool, serverEntryPointsTCP, serverEntryPointsUDP, watcher, observabilityMgr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHTTPChallengeHandler(acmeProviders []*acme.Provider, httpChallengeProvider http.Handler) http.Handler {
|
func getHTTPChallengeHandler(acmeProviders []*acme.Provider, httpChallengeProvider http.Handler) http.Handler {
|
||||||
|
|
|
@ -693,3 +693,13 @@ Here are two possible transition strategies:
|
||||||
This allows continued compatibility with the existing infrastructure.
|
This allows continued compatibility with the existing infrastructure.
|
||||||
|
|
||||||
Please check the [OpenTelemetry Tracing provider documention](../observability/tracing/opentelemetry.md) for more information.
|
Please check the [OpenTelemetry Tracing provider documention](../observability/tracing/opentelemetry.md) for more information.
|
||||||
|
|
||||||
|
#### Internal Resources Observability (AccessLogs, Metrics and Tracing)
|
||||||
|
|
||||||
|
In v3, observability for internal routers or services (e.g.: `ping@internal`) is disabled by default.
|
||||||
|
To enable it one should use the new `addInternals` option for AccessLogs, Metrics or Tracing.
|
||||||
|
Please take a look at the observability documentation for more information:
|
||||||
|
|
||||||
|
- [AccessLogs](../observability/access-logs.md#addinternals)
|
||||||
|
- [Metrics](../observability/metrics/overview.md#addinternals)
|
||||||
|
- [AccessLogs](../observability/tracing/overview.md#addinternals)
|
||||||
|
|
|
@ -26,6 +26,26 @@ accessLog: {}
|
||||||
--accesslog=true
|
--accesslog=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `addInternals`
|
||||||
|
|
||||||
|
_Optional, Default="false"_
|
||||||
|
|
||||||
|
Enables accessLogs for internal resources.
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
accesslog:
|
||||||
|
addInternals: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[accesslog]
|
||||||
|
addInternals = true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--accesslog.addinternals
|
||||||
|
```
|
||||||
|
|
||||||
### `filePath`
|
### `filePath`
|
||||||
|
|
||||||
By default access logs are written to the standard output.
|
By default access logs are written to the standard output.
|
||||||
|
|
|
@ -14,6 +14,28 @@ Traefik supports these metrics backends:
|
||||||
|
|
||||||
Traefik Proxy hosts an official Grafana dashboard for both [on-premises](https://grafana.com/grafana/dashboards/17346) and [Kubernetes](https://grafana.com/grafana/dashboards/17347) deployments.
|
Traefik Proxy hosts an official Grafana dashboard for both [on-premises](https://grafana.com/grafana/dashboards/17346) and [Kubernetes](https://grafana.com/grafana/dashboards/17347) deployments.
|
||||||
|
|
||||||
|
## Common Options
|
||||||
|
|
||||||
|
### `addInternals`
|
||||||
|
|
||||||
|
_Optional, Default="false"_
|
||||||
|
|
||||||
|
Enables metrics for internal resources.
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
metrics:
|
||||||
|
addInternals: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[metrics]
|
||||||
|
addInternals = true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--metrics.addinternals
|
||||||
|
```
|
||||||
|
|
||||||
## Global Metrics
|
## Global Metrics
|
||||||
|
|
||||||
| Metric | Type | [Labels](#labels) | Description |
|
| Metric | Type | [Labels](#labels) | Description |
|
||||||
|
|
42
docs/content/observability/overview.md
Normal file
42
docs/content/observability/overview.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
title: "Traefik Observability Overview"
|
||||||
|
description: "Traefik provides Logs, Access Logs, Metrics and Tracing. Read the full documentation to get started."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
Traefik's Observability system
|
||||||
|
{: .subtitle }
|
||||||
|
|
||||||
|
## Logs
|
||||||
|
|
||||||
|
Traefik logs informs about everything that happens within Traefik (startup, configuration, events, shutdown, and so on).
|
||||||
|
|
||||||
|
Read the [Logs documentation](./logs.md) to learn how to configure it.
|
||||||
|
|
||||||
|
## Access Logs
|
||||||
|
|
||||||
|
Access logs are a key part of observability in Traefik.
|
||||||
|
|
||||||
|
They are providing valuable insights about incoming traffic, and allow to monitor it.
|
||||||
|
The access logs record detailed information about each request received by Traefik,
|
||||||
|
including the source IP address, requested URL, response status code, and more.
|
||||||
|
|
||||||
|
Read the [Access Logs documentation](./access-logs.md) to learn how to configure it.
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
Traefik offers a metrics feature that provides valuable insights about the performance and usage.
|
||||||
|
These metrics include the number of requests received, the requests duration, and more.
|
||||||
|
|
||||||
|
Traefik supports these metrics systems: Prometheus, Datadog, InfluxDB 2.X, and StatsD.
|
||||||
|
|
||||||
|
Read the [Metrics documentation](./metrics/overview.md) to learn how to configure it.
|
||||||
|
|
||||||
|
## Tracing
|
||||||
|
|
||||||
|
The Traefik tracing system allows developers to gain deep visibility into the flow of requests through their infrastructure.
|
||||||
|
|
||||||
|
Traefik supports these tracing with OpenTelemetry.
|
||||||
|
|
||||||
|
Read the [Tracing documentation](./tracing/overview.md) to learn how to configure it.
|
|
@ -14,10 +14,8 @@ Traefik uses [OpenTelemetry](https://opentelemetry.io/ "Link to website of OTel"
|
||||||
|
|
||||||
Please check our dedicated [OTel docs](./opentelemetry.md) to learn more.
|
Please check our dedicated [OTel docs](./opentelemetry.md) to learn more.
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|
||||||
To enable the tracing:
|
To enable the tracing:
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
|
@ -34,6 +32,26 @@ tracing: {}
|
||||||
|
|
||||||
### Common Options
|
### Common Options
|
||||||
|
|
||||||
|
#### `addInternals`
|
||||||
|
|
||||||
|
_Optional, Default="false"_
|
||||||
|
|
||||||
|
Enables tracing for internal resources.
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
tracing:
|
||||||
|
addInternals: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[tracing]
|
||||||
|
addInternals = true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--tracing.addinternals
|
||||||
|
```
|
||||||
|
|
||||||
#### `serviceName`
|
#### `serviceName`
|
||||||
|
|
||||||
_Required, Default="traefik"_
|
_Required, Default="traefik"_
|
||||||
|
|
|
@ -6,6 +6,9 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
`--accesslog`:
|
`--accesslog`:
|
||||||
Access log settings. (Default: ```false```)
|
Access log settings. (Default: ```false```)
|
||||||
|
|
||||||
|
`--accesslog.addinternals`:
|
||||||
|
Enables access log for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||||
|
|
||||||
`--accesslog.bufferingsize`:
|
`--accesslog.bufferingsize`:
|
||||||
Number of access log lines to process in a buffered way. (Default: ```0```)
|
Number of access log lines to process in a buffered way. (Default: ```0```)
|
||||||
|
|
||||||
|
@ -267,6 +270,9 @@ Maximum size in megabytes of the log file before it gets rotated. (Default: ```0
|
||||||
`--log.nocolor`:
|
`--log.nocolor`:
|
||||||
When using the 'common' format, disables the colorized output. (Default: ```false```)
|
When using the 'common' format, disables the colorized output. (Default: ```false```)
|
||||||
|
|
||||||
|
`--metrics.addinternals`:
|
||||||
|
Enables metrics for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||||
|
|
||||||
`--metrics.datadog`:
|
`--metrics.datadog`:
|
||||||
Datadog metrics exporter type. (Default: ```false```)
|
Datadog metrics exporter type. (Default: ```false```)
|
||||||
|
|
||||||
|
@ -993,6 +999,9 @@ Defines the allowed SPIFFE trust domain.
|
||||||
`--tracing`:
|
`--tracing`:
|
||||||
OpenTracing configuration. (Default: ```false```)
|
OpenTracing configuration. (Default: ```false```)
|
||||||
|
|
||||||
|
`--tracing.addinternals`:
|
||||||
|
Enables tracing for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||||
|
|
||||||
`--tracing.globalattributes.<name>`:
|
`--tracing.globalattributes.<name>`:
|
||||||
Defines additional attributes (key:value) on all spans.
|
Defines additional attributes (key:value) on all spans.
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
`TRAEFIK_ACCESSLOG`:
|
`TRAEFIK_ACCESSLOG`:
|
||||||
Access log settings. (Default: ```false```)
|
Access log settings. (Default: ```false```)
|
||||||
|
|
||||||
|
`TRAEFIK_ACCESSLOG_ADDINTERNALS`:
|
||||||
|
Enables access log for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_ACCESSLOG_BUFFERINGSIZE`:
|
`TRAEFIK_ACCESSLOG_BUFFERINGSIZE`:
|
||||||
Number of access log lines to process in a buffered way. (Default: ```0```)
|
Number of access log lines to process in a buffered way. (Default: ```0```)
|
||||||
|
|
||||||
|
@ -267,6 +270,9 @@ Maximum size in megabytes of the log file before it gets rotated. (Default: ```0
|
||||||
`TRAEFIK_LOG_NOCOLOR`:
|
`TRAEFIK_LOG_NOCOLOR`:
|
||||||
When using the 'common' format, disables the colorized output. (Default: ```false```)
|
When using the 'common' format, disables the colorized output. (Default: ```false```)
|
||||||
|
|
||||||
|
`TRAEFIK_METRICS_ADDINTERNALS`:
|
||||||
|
Enables metrics for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_METRICS_DATADOG`:
|
`TRAEFIK_METRICS_DATADOG`:
|
||||||
Datadog metrics exporter type. (Default: ```false```)
|
Datadog metrics exporter type. (Default: ```false```)
|
||||||
|
|
||||||
|
@ -993,6 +999,9 @@ Defines the allowed SPIFFE trust domain.
|
||||||
`TRAEFIK_TRACING`:
|
`TRAEFIK_TRACING`:
|
||||||
OpenTracing configuration. (Default: ```false```)
|
OpenTracing configuration. (Default: ```false```)
|
||||||
|
|
||||||
|
`TRAEFIK_TRACING_ADDINTERNALS`:
|
||||||
|
Enables tracing for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_TRACING_GLOBALATTRIBUTES_<NAME>`:
|
`TRAEFIK_TRACING_GLOBALATTRIBUTES_<NAME>`:
|
||||||
Defines additional attributes (key:value) on all spans.
|
Defines additional attributes (key:value) on all spans.
|
||||||
|
|
||||||
|
|
|
@ -277,6 +277,7 @@
|
||||||
disableDashboardAd = true
|
disableDashboardAd = true
|
||||||
|
|
||||||
[metrics]
|
[metrics]
|
||||||
|
addInternals = true
|
||||||
[metrics.prometheus]
|
[metrics.prometheus]
|
||||||
buckets = [42.0, 42.0]
|
buckets = [42.0, 42.0]
|
||||||
addEntryPointsLabels = true
|
addEntryPointsLabels = true
|
||||||
|
@ -351,6 +352,7 @@
|
||||||
filePath = "foobar"
|
filePath = "foobar"
|
||||||
format = "foobar"
|
format = "foobar"
|
||||||
bufferingSize = 42
|
bufferingSize = 42
|
||||||
|
addInternals = true
|
||||||
[accessLog.filters]
|
[accessLog.filters]
|
||||||
statusCodes = ["foobar", "foobar"]
|
statusCodes = ["foobar", "foobar"]
|
||||||
retryAttempts = true
|
retryAttempts = true
|
||||||
|
@ -369,6 +371,7 @@
|
||||||
[tracing]
|
[tracing]
|
||||||
serviceName = "foobar"
|
serviceName = "foobar"
|
||||||
sampleRate = 42.0
|
sampleRate = 42.0
|
||||||
|
addInternals = true
|
||||||
[tracing.headers]
|
[tracing.headers]
|
||||||
name0 = "foobar"
|
name0 = "foobar"
|
||||||
name1 = "foobar"
|
name1 = "foobar"
|
||||||
|
|
|
@ -308,6 +308,7 @@ api:
|
||||||
debug: true
|
debug: true
|
||||||
disableDashboardAd: true
|
disableDashboardAd: true
|
||||||
metrics:
|
metrics:
|
||||||
|
addInternals: true
|
||||||
prometheus:
|
prometheus:
|
||||||
buckets:
|
buckets:
|
||||||
- 42
|
- 42
|
||||||
|
@ -399,6 +400,7 @@ accessLog:
|
||||||
name0: foobar
|
name0: foobar
|
||||||
name1: foobar
|
name1: foobar
|
||||||
bufferingSize: 42
|
bufferingSize: 42
|
||||||
|
addInternals: true
|
||||||
tracing:
|
tracing:
|
||||||
serviceName: foobar
|
serviceName: foobar
|
||||||
headers:
|
headers:
|
||||||
|
@ -408,6 +410,7 @@ tracing:
|
||||||
name0: foobar
|
name0: foobar
|
||||||
name1: foobar
|
name1: foobar
|
||||||
sampleRate: 42
|
sampleRate: 42
|
||||||
|
addInternals: true
|
||||||
otlp:
|
otlp:
|
||||||
grpc:
|
grpc:
|
||||||
endpoint: foobar
|
endpoint: foobar
|
||||||
|
|
|
@ -149,6 +149,7 @@ nav:
|
||||||
- 'API': 'operations/api.md'
|
- 'API': 'operations/api.md'
|
||||||
- 'Ping': 'operations/ping.md'
|
- 'Ping': 'operations/ping.md'
|
||||||
- 'Observability':
|
- 'Observability':
|
||||||
|
- 'Overview': 'observability/overview.md'
|
||||||
- 'Logs': 'observability/logs.md'
|
- 'Logs': 'observability/logs.md'
|
||||||
- 'Access Logs': 'observability/access-logs.md'
|
- 'Access Logs': 'observability/access-logs.md'
|
||||||
- 'Metrics':
|
- 'Metrics':
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (s *AccessLogSuite) TestAccessLog() {
|
||||||
ensureWorkingDirectoryIsClean()
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||||
|
@ -130,7 +130,7 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
|
|
||||||
s.checkStatsForLogFile()
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
|
|
||||||
s.checkStatsForLogFile()
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ func (s *AccessLogSuite) TestAccessLogFrontendRedirect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
|
|
||||||
s.checkStatsForLogFile()
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
|
@ -410,7 +410,7 @@ func (s *AccessLogSuite) TestAccessLogRateLimit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
|
|
||||||
s.checkStatsForLogFile()
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
|
@ -454,7 +454,7 @@ func (s *AccessLogSuite) TestAccessLogBackendNotFound() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
|
|
||||||
s.waitForTraefik("server1")
|
s.waitForTraefik("server1")
|
||||||
|
|
||||||
|
@ -494,7 +494,7 @@ func (s *AccessLogSuite) TestAccessLogFrontendAllowlist() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
|
|
||||||
s.checkStatsForLogFile()
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
|
@ -534,7 +534,7 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
|
|
||||||
s.checkStatsForLogFile()
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
|
@ -575,7 +575,7 @@ func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
|
|
||||||
s.checkStatsForLogFile()
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
|
@ -603,6 +603,56 @@ func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware() {
|
||||||
s.checkNoOtherTraefikProblems()
|
s.checkNoOtherTraefikProblems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *AccessLogSuite) TestAccessLogDisabledForInternals() {
|
||||||
|
ensureWorkingDirectoryIsClean()
|
||||||
|
|
||||||
|
file := s.adaptFile("fixtures/access_log/access_log_ping.toml", struct{}{})
|
||||||
|
|
||||||
|
// Start Traefik.
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
log.Info().Msg(string(traefikLog))
|
||||||
|
}()
|
||||||
|
|
||||||
|
// waitForTraefik makes at least one call to the rawdata api endpoint,
|
||||||
|
// but the logs for this endpoint are ignored in checkAccessLogOutput.
|
||||||
|
s.waitForTraefik("customPing")
|
||||||
|
|
||||||
|
s.checkStatsForLogFile()
|
||||||
|
|
||||||
|
// Verify Traefik started OK.
|
||||||
|
s.checkTraefikStarted()
|
||||||
|
|
||||||
|
// Make some requests on the internal ping router.
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/ping", nil)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
// Make some requests on the custom ping router.
|
||||||
|
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/ping", nil)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
// Verify access.log output as expected.
|
||||||
|
count := s.checkAccessLogOutput()
|
||||||
|
|
||||||
|
require.Equal(s.T(), 0, count)
|
||||||
|
|
||||||
|
// Verify no other Traefik problems.
|
||||||
|
s.checkNoOtherTraefikProblems()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) checkNoOtherTraefikProblems() {
|
func (s *AccessLogSuite) checkNoOtherTraefikProblems() {
|
||||||
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
|
@ -612,6 +662,8 @@ func (s *AccessLogSuite) checkNoOtherTraefikProblems() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) checkAccessLogOutput() int {
|
func (s *AccessLogSuite) checkAccessLogOutput() int {
|
||||||
|
s.T().Helper()
|
||||||
|
|
||||||
lines := s.extractLines()
|
lines := s.extractLines()
|
||||||
count := 0
|
count := 0
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
|
@ -624,6 +676,8 @@ func (s *AccessLogSuite) checkAccessLogOutput() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) checkAccessLogExactValuesOutput(values []accessLogValue) int {
|
func (s *AccessLogSuite) checkAccessLogExactValuesOutput(values []accessLogValue) int {
|
||||||
|
s.T().Helper()
|
||||||
|
|
||||||
lines := s.extractLines()
|
lines := s.extractLines()
|
||||||
count := 0
|
count := 0
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
|
@ -641,6 +695,8 @@ func (s *AccessLogSuite) checkAccessLogExactValuesOutput(values []accessLogValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) extractLines() []string {
|
func (s *AccessLogSuite) extractLines() []string {
|
||||||
|
s.T().Helper()
|
||||||
|
|
||||||
accessLog, err := os.ReadFile(traefikTestAccessLogFile)
|
accessLog, err := os.ReadFile(traefikTestAccessLogFile)
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
@ -656,6 +712,8 @@ func (s *AccessLogSuite) extractLines() []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) checkStatsForLogFile() {
|
func (s *AccessLogSuite) checkStatsForLogFile() {
|
||||||
|
s.T().Helper()
|
||||||
|
|
||||||
err := try.Do(1*time.Second, func() error {
|
err := try.Do(1*time.Second, func() error {
|
||||||
if _, errStat := os.Stat(traefikTestLogFile); errStat != nil {
|
if _, errStat := os.Stat(traefikTestLogFile); errStat != nil {
|
||||||
return fmt.Errorf("could not get stats for log file: %w", errStat)
|
return fmt.Errorf("could not get stats for log file: %w", errStat)
|
||||||
|
@ -671,6 +729,8 @@ func ensureWorkingDirectoryIsClean() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) checkTraefikStarted() []byte {
|
func (s *AccessLogSuite) checkTraefikStarted() []byte {
|
||||||
|
s.T().Helper()
|
||||||
|
|
||||||
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
if len(traefikLog) > 0 {
|
if len(traefikLog) > 0 {
|
||||||
|
@ -680,6 +740,8 @@ func (s *AccessLogSuite) checkTraefikStarted() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseSuite) CheckAccessLogFormat(line string, i int) {
|
func (s *BaseSuite) CheckAccessLogFormat(line string, i int) {
|
||||||
|
s.T().Helper()
|
||||||
|
|
||||||
results, err := accesslog.ParseAccessLog(line)
|
results, err := accesslog.ParseAccessLog(line)
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
assert.Len(s.T(), results, 14)
|
assert.Len(s.T(), results, 14)
|
||||||
|
@ -692,6 +754,8 @@ func (s *BaseSuite) CheckAccessLogFormat(line string, i int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccessLogSuite) checkAccessLogExactValues(line string, i int, v accessLogValue) {
|
func (s *AccessLogSuite) checkAccessLogExactValues(line string, i int, v accessLogValue) {
|
||||||
|
s.T().Helper()
|
||||||
|
|
||||||
results, err := accesslog.ParseAccessLog(line)
|
results, err := accesslog.ParseAccessLog(line)
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
assert.Len(s.T(), results, 14)
|
assert.Len(s.T(), results, 14)
|
||||||
|
|
30
integration/fixtures/access_log/access_log_ping.toml
Normal file
30
integration/fixtures/access_log/access_log_ping.toml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
[global]
|
||||||
|
checkNewVersion = false
|
||||||
|
sendAnonymousUsage = false
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "ERROR"
|
||||||
|
filePath = "traefik.log"
|
||||||
|
|
||||||
|
[accessLog]
|
||||||
|
filePath = "access.log"
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.web]
|
||||||
|
address = ":8000"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
insecure = true
|
||||||
|
|
||||||
|
[ping]
|
||||||
|
|
||||||
|
[providers]
|
||||||
|
[providers.file]
|
||||||
|
filename = "{{ .SelfFilename }}"
|
||||||
|
|
||||||
|
## dynamic configuration ##
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.customPing]
|
||||||
|
entryPoints = ["web"]
|
||||||
|
rule = "PathPrefix(`/ping`)"
|
||||||
|
service = "ping@internal"
|
|
@ -19,5 +19,6 @@
|
||||||
insecure = true
|
insecure = true
|
||||||
|
|
||||||
[metrics]
|
[metrics]
|
||||||
|
addInternals = true
|
||||||
[metrics.prometheus]
|
[metrics.prometheus]
|
||||||
buckets = [0.1,0.3,1.2,5.0]
|
buckets = [0.1,0.3,1.2,5.0]
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
[api]
|
[api]
|
||||||
insecure = true
|
insecure = true
|
||||||
|
|
||||||
|
[ping]
|
||||||
|
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.web]
|
[entryPoints.web]
|
||||||
address = ":8000"
|
address = ":8000"
|
||||||
|
@ -47,6 +49,10 @@
|
||||||
Service = "service3"
|
Service = "service3"
|
||||||
Middlewares = ["retry", "basic-auth"]
|
Middlewares = ["retry", "basic-auth"]
|
||||||
Rule = "Path(`/auth`)"
|
Rule = "Path(`/auth`)"
|
||||||
|
[http.routers.customPing]
|
||||||
|
entryPoints = ["web"]
|
||||||
|
rule = "PathPrefix(`/ping`)"
|
||||||
|
service = "ping@internal"
|
||||||
|
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.retry.retry]
|
[http.middlewares.retry.retry]
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (s *LogRotationSuite) TearDownSuite() {
|
||||||
|
|
||||||
func (s *LogRotationSuite) TestAccessLogRotation() {
|
func (s *LogRotationSuite) TestAccessLogRotation() {
|
||||||
// Start Traefik
|
// Start Traefik
|
||||||
cmd, _ := s.cmdTraefik(withConfigFile("fixtures/access_log_config.toml"))
|
cmd, _ := s.cmdTraefik(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||||
defer s.displayTraefikLogFile(traefikTestLogFile)
|
defer s.displayTraefikLogFile(traefikTestLogFile)
|
||||||
|
|
||||||
// Verify Traefik started ok
|
// Verify Traefik started ok
|
||||||
|
|
|
@ -287,6 +287,10 @@ func (s *SimpleSuite) TestMetricsPrometheusDefaultEntryPoint() {
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/metrics", 1*time.Second, try.BodyContains("_service_"))
|
err = try.GetRequest("http://127.0.0.1:8080/metrics", 1*time.Second, try.BodyContains("_service_"))
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
// No metrics for internals.
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/metrics", 1*time.Second, try.BodyNotContains("router=\"api@internal\"", "service=\"api@internal\""))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimpleSuite) TestMetricsPrometheusTwoRoutersOneService() {
|
func (s *SimpleSuite) TestMetricsPrometheusTwoRoutersOneService() {
|
||||||
|
|
|
@ -414,6 +414,67 @@ func (s *TracingSuite) TestOpentelemetryAuth() {
|
||||||
s.checkTraceContent(contains)
|
s.checkTraceContent(contains)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TracingSuite) TestNoInternals() {
|
||||||
|
file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{
|
||||||
|
WhoamiIP: s.whoamiIP,
|
||||||
|
WhoamiPort: s.whoamiPort,
|
||||||
|
IP: s.otelCollectorIP,
|
||||||
|
IsHTTP: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
// wait for traefik
|
||||||
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8000/ping", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/ping", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
baseURL, err := url.Parse("http://" + s.tempoIP + ":3200/api/search")
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
req := &http.Request{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
URL: baseURL,
|
||||||
|
}
|
||||||
|
// Wait for traces to be available.
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
resp, err := try.Response(req, 5*time.Second)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
out := &TraceResponse{}
|
||||||
|
content, err := io.ReadAll(resp.Body)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
err = json.Unmarshal(content, &out)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
s.NotEmptyf(len(out.Traces), "expected at least one trace")
|
||||||
|
|
||||||
|
for _, t := range out.Traces {
|
||||||
|
baseURL, err := url.Parse("http://" + s.tempoIP + ":3200/api/traces/" + t.TraceID)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
req := &http.Request{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
URL: baseURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := try.Response(req, 5*time.Second)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
content, err := io.ReadAll(resp.Body)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
require.NotContains(s.T(), content, "@internal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TracingSuite) checkTraceContent(expectedJSON []map[string]string) {
|
func (s *TracingSuite) checkTraceContent(expectedJSON []map[string]string) {
|
||||||
s.T().Helper()
|
s.T().Helper()
|
||||||
|
|
||||||
|
|
|
@ -197,6 +197,7 @@ type Tracing struct {
|
||||||
Headers map[string]string `description:"Defines additional headers to be sent with the payloads." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty" export:"true"`
|
Headers map[string]string `description:"Defines additional headers to be sent with the payloads." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,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"`
|
GlobalAttributes map[string]string `description:"Defines additional attributes (key:value) on all spans." json:"globalAttributes,omitempty" toml:"globalAttributes,omitempty" yaml:"globalAttributes,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"`
|
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 *opentelemetry.Config `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"`
|
||||||
}
|
}
|
||||||
|
@ -218,7 +219,7 @@ type Providers struct {
|
||||||
KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
KubernetesGateway *gateway.Provider `description:"Enable Kubernetes gateway api provider with default settings." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
KubernetesGateway *gateway.Provider `description:"Enable Kubernetes gateway api provider with default settings." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
Rest *rest.Provider ` description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
ConsulCatalog *consulcatalog.ProviderBuilder `description:"Enable ConsulCatalog backend with default settings." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
ConsulCatalog *consulcatalog.ProviderBuilder `description:"Enable ConsulCatalog backend with default settings." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
Nomad *nomad.ProviderBuilder `description:"Enable Nomad backend with default settings." json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
Nomad *nomad.ProviderBuilder `description:"Enable Nomad backend with default settings." json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
Ecs *ecs.Provider `description:"Enable AWS ECS backend with default settings." json:"ecs,omitempty" toml:"ecs,omitempty" yaml:"ecs,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
Ecs *ecs.Provider `description:"Enable AWS ECS backend with default settings." json:"ecs,omitempty" toml:"ecs,omitempty" yaml:"ecs,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
|
|
|
@ -12,9 +12,10 @@ import (
|
||||||
|
|
||||||
// Muxer handles routing with rules.
|
// Muxer handles routing with rules.
|
||||||
type Muxer struct {
|
type Muxer struct {
|
||||||
routes routes
|
routes routes
|
||||||
parser predicate.Parser
|
parser predicate.Parser
|
||||||
parserV2 predicate.Parser
|
parserV2 predicate.Parser
|
||||||
|
defaultHandler http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMuxer returns a new muxer instance.
|
// NewMuxer returns a new muxer instance.
|
||||||
|
@ -40,8 +41,9 @@ func NewMuxer() (*Muxer, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Muxer{
|
return &Muxer{
|
||||||
parser: parser,
|
parser: parser,
|
||||||
parserV2: parserV2,
|
parserV2: parserV2,
|
||||||
|
defaultHandler: http.NotFoundHandler(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +57,12 @@ func (m *Muxer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http.NotFoundHandler().ServeHTTP(rw, req)
|
m.defaultHandler.ServeHTTP(rw, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaultHandler sets the muxer default handler.
|
||||||
|
func (m *Muxer) SetDefaultHandler(handler http.Handler) {
|
||||||
|
m.defaultHandler = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRulePriority computes the priority for a given rule.
|
// GetRulePriority computes the priority for a given rule.
|
||||||
|
|
|
@ -407,4 +407,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,63 +0,0 @@
|
||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/containous/alice"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/capture"
|
|
||||||
metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
|
|
||||||
tracingMiddle "github.com/traefik/traefik/v3/pkg/middlewares/tracing"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ChainBuilder Creates a middleware chain by entry point. It is used for middlewares that are created almost systematically and that need to be created before all others.
|
|
||||||
type ChainBuilder struct {
|
|
||||||
metricsRegistry metrics.Registry
|
|
||||||
accessLoggerMiddleware *accesslog.Handler
|
|
||||||
tracer trace.Tracer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewChainBuilder Creates a new ChainBuilder.
|
|
||||||
func NewChainBuilder(metricsRegistry metrics.Registry, accessLoggerMiddleware *accesslog.Handler, tracer trace.Tracer) *ChainBuilder {
|
|
||||||
return &ChainBuilder{
|
|
||||||
metricsRegistry: metricsRegistry,
|
|
||||||
accessLoggerMiddleware: accessLoggerMiddleware,
|
|
||||||
tracer: tracer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a middleware chain by entry point.
|
|
||||||
func (c *ChainBuilder) Build(ctx context.Context, entryPointName string) alice.Chain {
|
|
||||||
chain := alice.New()
|
|
||||||
|
|
||||||
if c.accessLoggerMiddleware != nil || c.metricsRegistry != nil && (c.metricsRegistry.IsEpEnabled() || c.metricsRegistry.IsRouterEnabled() || c.metricsRegistry.IsSvcEnabled()) {
|
|
||||||
chain = chain.Append(capture.Wrap)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.accessLoggerMiddleware != nil {
|
|
||||||
chain = chain.Append(accesslog.WrapHandler(c.accessLoggerMiddleware))
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.tracer != nil {
|
|
||||||
chain = chain.Append(tracingMiddle.WrapEntryPointHandler(ctx, c.tracer, entryPointName))
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.metricsRegistry != nil && c.metricsRegistry.IsEpEnabled() {
|
|
||||||
metricsHandler := metricsMiddle.WrapEntryPointHandler(ctx, c.metricsRegistry, entryPointName)
|
|
||||||
chain = chain.Append(tracingMiddle.WrapMiddleware(ctx, metricsHandler))
|
|
||||||
}
|
|
||||||
|
|
||||||
return chain
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close accessLogger and tracer.
|
|
||||||
func (c *ChainBuilder) Close() {
|
|
||||||
if c.accessLoggerMiddleware != nil {
|
|
||||||
if err := c.accessLoggerMiddleware.Close(); err != nil {
|
|
||||||
log.Error().Err(err).Msg("Could not close the access log file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -387,6 +387,9 @@ func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) (
|
||||||
return nil, fmt.Errorf("invalid middleware %q configuration: invalid middleware type or middleware does not exist", middlewareName)
|
return nil, fmt.Errorf("invalid middleware %q configuration: invalid middleware type or middleware does not exist", middlewareName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The tracing middleware is a NOOP if tracing is not setup on the middleware chain.
|
||||||
|
// Hence, regarding internal resources' observability deactivation,
|
||||||
|
// this would not enable tracing.
|
||||||
return tracing.WrapMiddleware(ctx, middleware), nil
|
return tracing.WrapMiddleware(ctx, middleware), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
140
pkg/server/middleware/observability.go
Normal file
140
pkg/server/middleware/observability.go
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containous/alice"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/logs"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/metrics"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/middlewares/capture"
|
||||||
|
metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
|
||||||
|
tracingMiddle "github.com/traefik/traefik/v3/pkg/middlewares/tracing"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObservabilityMgr is a manager for observability (AccessLogs, Metrics and Tracing) enablement.
|
||||||
|
type ObservabilityMgr struct {
|
||||||
|
config static.Configuration
|
||||||
|
accessLoggerMiddleware *accesslog.Handler
|
||||||
|
metricsRegistry metrics.Registry
|
||||||
|
tracer trace.Tracer
|
||||||
|
tracerCloser io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewObservabilityMgr creates a new ObservabilityMgr.
|
||||||
|
func NewObservabilityMgr(config static.Configuration, metricsRegistry metrics.Registry, accessLoggerMiddleware *accesslog.Handler, tracer trace.Tracer, tracerCloser io.Closer) *ObservabilityMgr {
|
||||||
|
return &ObservabilityMgr{
|
||||||
|
config: config,
|
||||||
|
metricsRegistry: metricsRegistry,
|
||||||
|
accessLoggerMiddleware: accessLoggerMiddleware,
|
||||||
|
tracer: tracer,
|
||||||
|
tracerCloser: tracerCloser,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildEPChain an observability middleware chain by entry point.
|
||||||
|
func (c *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName string, resourceName string) alice.Chain {
|
||||||
|
chain := alice.New()
|
||||||
|
|
||||||
|
if c == nil {
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.accessLoggerMiddleware != nil || c.metricsRegistry != nil && (c.metricsRegistry.IsEpEnabled() || c.metricsRegistry.IsRouterEnabled() || c.metricsRegistry.IsSvcEnabled()) {
|
||||||
|
if c.ShouldAddAccessLogs(resourceName) || c.ShouldAddMetrics(resourceName) {
|
||||||
|
chain = chain.Append(capture.Wrap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.accessLoggerMiddleware != nil && c.ShouldAddAccessLogs(resourceName) {
|
||||||
|
chain = chain.Append(accesslog.WrapHandler(c.accessLoggerMiddleware))
|
||||||
|
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||||
|
return accesslog.NewFieldHandler(next, logs.EntryPointName, entryPointName, accesslog.InitServiceFields), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.tracer != nil && c.ShouldAddTracing(resourceName) {
|
||||||
|
chain = chain.Append(tracingMiddle.WrapEntryPointHandler(ctx, c.tracer, entryPointName))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.metricsRegistry != nil && c.metricsRegistry.IsEpEnabled() && c.ShouldAddMetrics(resourceName) {
|
||||||
|
metricsHandler := metricsMiddle.WrapEntryPointHandler(ctx, c.metricsRegistry, entryPointName)
|
||||||
|
|
||||||
|
if c.tracer != nil && c.ShouldAddTracing(resourceName) {
|
||||||
|
chain = chain.Append(tracingMiddle.WrapMiddleware(ctx, metricsHandler))
|
||||||
|
} else {
|
||||||
|
chain = chain.Append(metricsHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldAddAccessLogs returns whether the access logs should be enabled for the given resource.
|
||||||
|
func (c *ObservabilityMgr) ShouldAddAccessLogs(resourceName string) bool {
|
||||||
|
if c == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.config.AccessLog != nil && (c.config.AccessLog.AddInternals || !strings.HasSuffix(resourceName, "@internal"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldAddMetrics returns whether the metrics should be enabled for the given resource.
|
||||||
|
func (c *ObservabilityMgr) ShouldAddMetrics(resourceName string) bool {
|
||||||
|
if c == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.config.Metrics != nil && (c.config.Metrics.AddInternals || !strings.HasSuffix(resourceName, "@internal"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldAddTracing returns whether the tracing should be enabled for the given resource.
|
||||||
|
func (c *ObservabilityMgr) ShouldAddTracing(resourceName string) bool {
|
||||||
|
if c == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.config.Tracing != nil && (c.config.Tracing.AddInternals || !strings.HasSuffix(resourceName, "@internal"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetricsRegistry is an accessor to the metrics registry.
|
||||||
|
func (c *ObservabilityMgr) MetricsRegistry() metrics.Registry {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.metricsRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the accessLogger and tracer.
|
||||||
|
func (c *ObservabilityMgr) Close() {
|
||||||
|
if c == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.accessLoggerMiddleware != nil {
|
||||||
|
if err := c.accessLoggerMiddleware.Close(); err != nil {
|
||||||
|
log.Error().Err(err).Msg("Could not close the access log file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.tracerCloser != nil {
|
||||||
|
if err := c.tracerCloser.Close(); err != nil {
|
||||||
|
log.Error().Err(err).Msg("Could not close the tracer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ObservabilityMgr) RotateAccessLogs() error {
|
||||||
|
if c.accessLoggerMiddleware == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.accessLoggerMiddleware.Rotate()
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||||
"github.com/traefik/traefik/v3/pkg/logs"
|
"github.com/traefik/traefik/v3/pkg/logs"
|
||||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/denyrouterrecursion"
|
"github.com/traefik/traefik/v3/pkg/middlewares/denyrouterrecursion"
|
||||||
metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
|
metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
|
||||||
|
@ -35,21 +34,19 @@ type serviceManager interface {
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
routerHandlers map[string]http.Handler
|
routerHandlers map[string]http.Handler
|
||||||
serviceManager serviceManager
|
serviceManager serviceManager
|
||||||
metricsRegistry metrics.Registry
|
observabilityMgr *middleware.ObservabilityMgr
|
||||||
middlewaresBuilder middlewareBuilder
|
middlewaresBuilder middlewareBuilder
|
||||||
chainBuilder *middleware.ChainBuilder
|
|
||||||
conf *runtime.Configuration
|
conf *runtime.Configuration
|
||||||
tlsManager *tls.Manager
|
tlsManager *tls.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new Manager.
|
// NewManager creates a new Manager.
|
||||||
func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, chainBuilder *middleware.ChainBuilder, metricsRegistry metrics.Registry, tlsManager *tls.Manager) *Manager {
|
func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, observabilityMgr *middleware.ObservabilityMgr, tlsManager *tls.Manager) *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
routerHandlers: make(map[string]http.Handler),
|
routerHandlers: make(map[string]http.Handler),
|
||||||
serviceManager: serviceManager,
|
serviceManager: serviceManager,
|
||||||
metricsRegistry: metricsRegistry,
|
observabilityMgr: observabilityMgr,
|
||||||
middlewaresBuilder: middlewaresBuilder,
|
middlewaresBuilder: middlewaresBuilder,
|
||||||
chainBuilder: chainBuilder,
|
|
||||||
conf: conf,
|
conf: conf,
|
||||||
tlsManager: tlsManager,
|
tlsManager: tlsManager,
|
||||||
}
|
}
|
||||||
|
@ -73,49 +70,49 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
|
||||||
logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
|
logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
|
||||||
ctx := logger.WithContext(rootCtx)
|
ctx := logger.WithContext(rootCtx)
|
||||||
|
|
||||||
handler, err := m.buildEntryPointHandler(ctx, routers)
|
handler, err := m.buildEntryPointHandler(ctx, entryPointName, routers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error().Err(err).Send()
|
logger.Error().Err(err).Send()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerWithAccessLog, err := alice.New(func(next http.Handler) (http.Handler, error) {
|
entryPointHandlers[entryPointName] = handler
|
||||||
return accesslog.NewFieldHandler(next, logs.EntryPointName, entryPointName, accesslog.InitServiceFields), nil
|
|
||||||
}).Then(handler)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error().Err(err).Send()
|
|
||||||
entryPointHandlers[entryPointName] = handler
|
|
||||||
} else {
|
|
||||||
entryPointHandlers[entryPointName] = handlerWithAccessLog
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create default handlers.
|
||||||
for _, entryPointName := range entryPoints {
|
for _, entryPointName := range entryPoints {
|
||||||
logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
|
logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
|
||||||
ctx := logger.WithContext(rootCtx)
|
ctx := logger.WithContext(rootCtx)
|
||||||
|
|
||||||
handler, ok := entryPointHandlers[entryPointName]
|
handler, ok := entryPointHandlers[entryPointName]
|
||||||
if !ok || handler == nil {
|
if ok || handler != nil {
|
||||||
handler = BuildDefaultHTTPRouter()
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerWithMiddlewares, err := m.chainBuilder.Build(ctx, entryPointName).Then(handler)
|
handler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "").Then(BuildDefaultHTTPRouter())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error().Err(err).Send()
|
logger.Error().Err(err).Send()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
entryPointHandlers[entryPointName] = handlerWithMiddlewares
|
entryPointHandlers[entryPointName] = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
return entryPointHandlers
|
return entryPointHandlers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*runtime.RouterInfo) (http.Handler, error) {
|
func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName string, configs map[string]*runtime.RouterInfo) (http.Handler, error) {
|
||||||
muxer, err := httpmuxer.NewMuxer()
|
muxer, err := httpmuxer.NewMuxer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultHandler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "defaultHandler").Then(http.NotFoundHandler())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
muxer.SetDefaultHandler(defaultHandler)
|
||||||
|
|
||||||
for routerName, routerConfig := range configs {
|
for routerName, routerConfig := range configs {
|
||||||
logger := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
|
logger := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
|
||||||
ctxRouter := logger.WithContext(provider.AddInContext(ctx, routerName))
|
ctxRouter := logger.WithContext(provider.AddInContext(ctx, routerName))
|
||||||
|
@ -131,6 +128,14 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
observabilityChain := m.observabilityMgr.BuildEPChain(ctx, entryPointName, routerConfig.Service)
|
||||||
|
handler, err = observabilityChain.Then(handler)
|
||||||
|
if err != nil {
|
||||||
|
routerConfig.AddError(err, true)
|
||||||
|
logger.Error().Err(err).Send()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if err = muxer.AddRoute(routerConfig.Rule, routerConfig.RuleSyntax, routerConfig.Priority, handler); err != nil {
|
if err = muxer.AddRoute(routerConfig.Rule, routerConfig.RuleSyntax, routerConfig.Priority, handler); err != nil {
|
||||||
routerConfig.AddError(err, true)
|
routerConfig.AddError(err, true)
|
||||||
logger.Error().Err(err).Send()
|
logger.Error().Err(err).Send()
|
||||||
|
@ -167,6 +172,12 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, rou
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevents from enabling observability for internal resources.
|
||||||
|
if !m.observabilityMgr.ShouldAddAccessLogs(provider.GetQualifiedName(ctx, routerConfig.Service)) {
|
||||||
|
m.routerHandlers[routerName] = handler
|
||||||
|
return m.routerHandlers[routerName], nil
|
||||||
|
}
|
||||||
|
|
||||||
handlerWithAccessLog, err := alice.New(func(next http.Handler) (http.Handler, error) {
|
handlerWithAccessLog, err := alice.New(func(next http.Handler) (http.Handler, error) {
|
||||||
return accesslog.NewFieldHandler(next, accesslog.RouterName, routerName, nil), nil
|
return accesslog.NewFieldHandler(next, accesslog.RouterName, routerName, nil), nil
|
||||||
}).Then(handler)
|
}).Then(handler)
|
||||||
|
@ -200,10 +211,20 @@ func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterIn
|
||||||
|
|
||||||
chain := alice.New()
|
chain := alice.New()
|
||||||
|
|
||||||
|
if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsRouterEnabled() &&
|
||||||
|
m.observabilityMgr.ShouldAddMetrics(provider.GetQualifiedName(ctx, router.Service)) {
|
||||||
|
chain = chain.Append(metricsMiddle.WrapRouterHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, provider.GetQualifiedName(ctx, router.Service)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevents from enabling tracing for internal resources.
|
||||||
|
if !m.observabilityMgr.ShouldAddTracing(provider.GetQualifiedName(ctx, router.Service)) {
|
||||||
|
return chain.Extend(*mHandler).Then(sHandler)
|
||||||
|
}
|
||||||
|
|
||||||
chain = chain.Append(tracing.WrapRouterHandler(ctx, routerName, router.Rule, provider.GetQualifiedName(ctx, router.Service)))
|
chain = chain.Append(tracing.WrapRouterHandler(ctx, routerName, router.Rule, provider.GetQualifiedName(ctx, router.Service)))
|
||||||
|
|
||||||
if m.metricsRegistry != nil && m.metricsRegistry.IsRouterEnabled() {
|
if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsRouterEnabled() {
|
||||||
metricsHandler := metricsMiddle.WrapRouterHandler(ctx, m.metricsRegistry, routerName, provider.GetQualifiedName(ctx, router.Service))
|
metricsHandler := metricsMiddle.WrapRouterHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, provider.GetQualifiedName(ctx, router.Service))
|
||||||
chain = chain.Append(tracing.WrapMiddleware(ctx, metricsHandler))
|
chain = chain.Append(tracing.WrapMiddleware(ctx, metricsHandler))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,21 +9,15 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containous/alice"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
ptypes "github.com/traefik/paerser/types"
|
ptypes "github.com/traefik/paerser/types"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/capture"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/requestdecorator"
|
"github.com/traefik/traefik/v3/pkg/middlewares/requestdecorator"
|
||||||
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||||
"github.com/traefik/traefik/v3/pkg/server/service"
|
"github.com/traefik/traefik/v3/pkg/server/service"
|
||||||
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
||||||
"github.com/traefik/traefik/v3/pkg/tls"
|
"github.com/traefik/traefik/v3/pkg/tls"
|
||||||
"github.com/traefik/traefik/v3/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRouterManager_Get(t *testing.T) {
|
func TestRouterManager_Get(t *testing.T) {
|
||||||
|
@ -319,10 +313,9 @@ func TestRouterManager_Get(t *testing.T) {
|
||||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
|
||||||
tlsManager := tls.NewManager()
|
tlsManager := tls.NewManager()
|
||||||
|
|
||||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager)
|
||||||
|
|
||||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||||
|
|
||||||
|
@ -341,126 +334,6 @@ func TestRouterManager_Get(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccessLog(t *testing.T) {
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
|
||||||
|
|
||||||
t.Cleanup(func() { server.Close() })
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
routersConfig map[string]*dynamic.Router
|
|
||||||
serviceConfig map[string]*dynamic.Service
|
|
||||||
middlewaresConfig map[string]*dynamic.Middleware
|
|
||||||
entryPoints []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "apply routerName in accesslog (first match)",
|
|
||||||
routersConfig: map[string]*dynamic.Router{
|
|
||||||
"foo": {
|
|
||||||
EntryPoints: []string{"web"},
|
|
||||||
Service: "foo-service",
|
|
||||||
Rule: "Host(`foo.bar`)",
|
|
||||||
},
|
|
||||||
"bar": {
|
|
||||||
EntryPoints: []string{"web"},
|
|
||||||
Service: "foo-service",
|
|
||||||
Rule: "Host(`bar.foo`)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serviceConfig: map[string]*dynamic.Service{
|
|
||||||
"foo-service": {
|
|
||||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
|
||||||
Servers: []dynamic.Server{
|
|
||||||
{
|
|
||||||
URL: server.URL,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
entryPoints: []string{"web"},
|
|
||||||
expected: "foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "apply routerName in accesslog (second match)",
|
|
||||||
routersConfig: map[string]*dynamic.Router{
|
|
||||||
"foo": {
|
|
||||||
EntryPoints: []string{"web"},
|
|
||||||
Service: "foo-service",
|
|
||||||
Rule: "Host(`bar.foo`)",
|
|
||||||
},
|
|
||||||
"bar": {
|
|
||||||
EntryPoints: []string{"web"},
|
|
||||||
Service: "foo-service",
|
|
||||||
Rule: "Host(`foo.bar`)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serviceConfig: map[string]*dynamic.Service{
|
|
||||||
"foo-service": {
|
|
||||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
|
||||||
Servers: []dynamic.Server{
|
|
||||||
{
|
|
||||||
URL: server.URL,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
entryPoints: []string{"web"},
|
|
||||||
expected: "bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
rtConf := runtime.NewConfig(dynamic.Configuration{
|
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
|
||||||
Services: test.serviceConfig,
|
|
||||||
Routers: test.routersConfig,
|
|
||||||
Middlewares: test.middlewaresConfig,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
roundTripperManager := service.NewRoundTripperManager(nil)
|
|
||||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
|
||||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
|
||||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
|
||||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
|
||||||
tlsManager := tls.NewManager()
|
|
||||||
|
|
||||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
|
||||||
|
|
||||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
|
|
||||||
|
|
||||||
accesslogger, err := accesslog.NewHandler(&types.AccessLog{
|
|
||||||
Format: "json",
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reqHost := requestdecorator.New(nil)
|
|
||||||
|
|
||||||
chain := alice.New()
|
|
||||||
chain = chain.Append(capture.Wrap)
|
|
||||||
chain = chain.Append(accesslog.WrapHandler(accesslogger))
|
|
||||||
handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
reqHost.ServeHTTP(w, req, handlers["web"].ServeHTTP)
|
|
||||||
|
|
||||||
data := accesslog.GetLogData(req)
|
|
||||||
require.NotNil(t, data)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, data.Core[accesslog.RouterName])
|
|
||||||
}))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
handler.ServeHTTP(w, req)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRuntimeConfiguration(t *testing.T) {
|
func TestRuntimeConfiguration(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -788,11 +661,10 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
|
||||||
tlsManager := tls.NewManager()
|
tlsManager := tls.NewManager()
|
||||||
tlsManager.UpdateConfigs(context.Background(), nil, test.tlsOptions, nil)
|
tlsManager.UpdateConfigs(context.Background(), nil, test.tlsOptions, nil)
|
||||||
|
|
||||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager)
|
||||||
|
|
||||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, true)
|
_ = routerManager.BuildHandlers(context.Background(), entryPoints, true)
|
||||||
|
@ -866,10 +738,9 @@ func TestProviderOnMiddlewares(t *testing.T) {
|
||||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
|
||||||
tlsManager := tls.NewManager()
|
tlsManager := tls.NewManager()
|
||||||
|
|
||||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager)
|
||||||
|
|
||||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||||
|
|
||||||
|
@ -935,10 +806,9 @@ func BenchmarkRouterServe(b *testing.B) {
|
||||||
|
|
||||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
|
serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
|
||||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
|
||||||
tlsManager := tls.NewManager()
|
tlsManager := tls.NewManager()
|
||||||
|
|
||||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager)
|
||||||
|
|
||||||
handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||||
tcpmiddleware "github.com/traefik/traefik/v3/pkg/server/middleware/tcp"
|
tcpmiddleware "github.com/traefik/traefik/v3/pkg/server/middleware/tcp"
|
||||||
"github.com/traefik/traefik/v3/pkg/server/router"
|
"github.com/traefik/traefik/v3/pkg/server/router"
|
||||||
|
@ -25,13 +24,12 @@ type RouterFactory struct {
|
||||||
entryPointsTCP []string
|
entryPointsTCP []string
|
||||||
entryPointsUDP []string
|
entryPointsUDP []string
|
||||||
|
|
||||||
managerFactory *service.ManagerFactory
|
managerFactory *service.ManagerFactory
|
||||||
metricsRegistry metrics.Registry
|
|
||||||
|
|
||||||
pluginBuilder middleware.PluginsBuilder
|
pluginBuilder middleware.PluginsBuilder
|
||||||
|
|
||||||
chainBuilder *middleware.ChainBuilder
|
observabilityMgr *middleware.ObservabilityMgr
|
||||||
tlsManager *tls.Manager
|
tlsManager *tls.Manager
|
||||||
|
|
||||||
dialerManager *tcp.DialerManager
|
dialerManager *tcp.DialerManager
|
||||||
|
|
||||||
|
@ -40,7 +38,7 @@ type RouterFactory struct {
|
||||||
|
|
||||||
// NewRouterFactory creates a new RouterFactory.
|
// NewRouterFactory creates a new RouterFactory.
|
||||||
func NewRouterFactory(staticConfiguration static.Configuration, managerFactory *service.ManagerFactory, tlsManager *tls.Manager,
|
func NewRouterFactory(staticConfiguration static.Configuration, managerFactory *service.ManagerFactory, tlsManager *tls.Manager,
|
||||||
chainBuilder *middleware.ChainBuilder, pluginBuilder middleware.PluginsBuilder, metricsRegistry metrics.Registry, dialerManager *tcp.DialerManager,
|
observabilityMgr *middleware.ObservabilityMgr, pluginBuilder middleware.PluginsBuilder, dialerManager *tcp.DialerManager,
|
||||||
) *RouterFactory {
|
) *RouterFactory {
|
||||||
var entryPointsTCP, entryPointsUDP []string
|
var entryPointsTCP, entryPointsUDP []string
|
||||||
for name, cfg := range staticConfiguration.EntryPoints {
|
for name, cfg := range staticConfiguration.EntryPoints {
|
||||||
|
@ -58,14 +56,13 @@ func NewRouterFactory(staticConfiguration static.Configuration, managerFactory *
|
||||||
}
|
}
|
||||||
|
|
||||||
return &RouterFactory{
|
return &RouterFactory{
|
||||||
entryPointsTCP: entryPointsTCP,
|
entryPointsTCP: entryPointsTCP,
|
||||||
entryPointsUDP: entryPointsUDP,
|
entryPointsUDP: entryPointsUDP,
|
||||||
managerFactory: managerFactory,
|
managerFactory: managerFactory,
|
||||||
metricsRegistry: metricsRegistry,
|
observabilityMgr: observabilityMgr,
|
||||||
tlsManager: tlsManager,
|
tlsManager: tlsManager,
|
||||||
chainBuilder: chainBuilder,
|
pluginBuilder: pluginBuilder,
|
||||||
pluginBuilder: pluginBuilder,
|
dialerManager: dialerManager,
|
||||||
dialerManager: dialerManager,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +80,7 @@ func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string
|
||||||
|
|
||||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, f.pluginBuilder)
|
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, f.pluginBuilder)
|
||||||
|
|
||||||
routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.chainBuilder, f.metricsRegistry, f.tlsManager)
|
routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.observabilityMgr, f.tlsManager)
|
||||||
|
|
||||||
handlersNonTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, false)
|
handlersNonTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, false)
|
||||||
handlersTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, true)
|
handlersTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, true)
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||||
"github.com/traefik/traefik/v3/pkg/server/service"
|
"github.com/traefik/traefik/v3/pkg/server/service"
|
||||||
"github.com/traefik/traefik/v3/pkg/tcp"
|
"github.com/traefik/traefik/v3/pkg/tcp"
|
||||||
|
@ -51,12 +50,12 @@ func TestReuseService(t *testing.T) {
|
||||||
|
|
||||||
roundTripperManager := service.NewRoundTripperManager(nil)
|
roundTripperManager := service.NewRoundTripperManager(nil)
|
||||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
managerFactory := service.NewManagerFactory(staticConfig, nil, nil, roundTripperManager, nil)
|
||||||
tlsManager := tls.NewManager()
|
tlsManager := tls.NewManager()
|
||||||
|
|
||||||
dialerManager := tcp.NewDialerManager(nil)
|
dialerManager := tcp.NewDialerManager(nil)
|
||||||
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
|
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
|
||||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil), nil, metrics.NewVoidRegistry(), dialerManager)
|
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, nil, nil, dialerManager)
|
||||||
|
|
||||||
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
|
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
|
||||||
|
|
||||||
|
@ -189,12 +188,13 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
||||||
|
|
||||||
roundTripperManager := service.NewRoundTripperManager(nil)
|
roundTripperManager := service.NewRoundTripperManager(nil)
|
||||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
managerFactory := service.NewManagerFactory(staticConfig, nil, nil, roundTripperManager, nil)
|
||||||
tlsManager := tls.NewManager()
|
tlsManager := tls.NewManager()
|
||||||
|
|
||||||
dialerManager := tcp.NewDialerManager(nil)
|
dialerManager := tcp.NewDialerManager(nil)
|
||||||
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
|
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
|
||||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil), nil, metrics.NewVoidRegistry(), dialerManager)
|
observabiltyMgr := middleware.NewObservabilityMgr(staticConfig, nil, nil, nil, nil)
|
||||||
|
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, observabiltyMgr, nil, dialerManager)
|
||||||
|
|
||||||
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: test.config(testServer.URL)}))
|
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: test.config(testServer.URL)}))
|
||||||
|
|
||||||
|
@ -232,14 +232,12 @@ func TestInternalServices(t *testing.T) {
|
||||||
|
|
||||||
roundTripperManager := service.NewRoundTripperManager(nil)
|
roundTripperManager := service.NewRoundTripperManager(nil)
|
||||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
managerFactory := service.NewManagerFactory(staticConfig, nil, nil, roundTripperManager, nil)
|
||||||
tlsManager := tls.NewManager()
|
tlsManager := tls.NewManager()
|
||||||
|
|
||||||
voidRegistry := metrics.NewVoidRegistry()
|
|
||||||
|
|
||||||
dialerManager := tcp.NewDialerManager(nil)
|
dialerManager := tcp.NewDialerManager(nil)
|
||||||
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
|
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
|
||||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(voidRegistry, nil, nil), nil, voidRegistry, dialerManager)
|
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, nil, nil, dialerManager)
|
||||||
|
|
||||||
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
|
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
|
||||||
|
|
||||||
|
|
|
@ -3,47 +3,39 @@ package server
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
"github.com/traefik/traefik/v3/pkg/metrics"
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/safe"
|
"github.com/traefik/traefik/v3/pkg/safe"
|
||||||
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server is the reverse-proxy/load-balancer engine.
|
// Server is the reverse-proxy/load-balancer engine.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
watcher *ConfigurationWatcher
|
watcher *ConfigurationWatcher
|
||||||
tcpEntryPoints TCPEntryPoints
|
tcpEntryPoints TCPEntryPoints
|
||||||
udpEntryPoints UDPEntryPoints
|
udpEntryPoints UDPEntryPoints
|
||||||
chainBuilder *middleware.ChainBuilder
|
observabilityMgr *middleware.ObservabilityMgr
|
||||||
|
|
||||||
accessLoggerMiddleware *accesslog.Handler
|
|
||||||
|
|
||||||
signals chan os.Signal
|
signals chan os.Signal
|
||||||
stopChan chan bool
|
stopChan chan bool
|
||||||
|
|
||||||
routinesPool *safe.Pool
|
routinesPool *safe.Pool
|
||||||
|
|
||||||
tracerCloser io.Closer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer returns an initialized Server.
|
// NewServer returns an initialized Server.
|
||||||
func NewServer(routinesPool *safe.Pool, entryPoints TCPEntryPoints, entryPointsUDP UDPEntryPoints, watcher *ConfigurationWatcher, chainBuilder *middleware.ChainBuilder, accessLoggerMiddleware *accesslog.Handler, tracerCloser io.Closer) *Server {
|
func NewServer(routinesPool *safe.Pool, entryPoints TCPEntryPoints, entryPointsUDP UDPEntryPoints, watcher *ConfigurationWatcher, observabilityMgr *middleware.ObservabilityMgr) *Server {
|
||||||
srv := &Server{
|
srv := &Server{
|
||||||
watcher: watcher,
|
watcher: watcher,
|
||||||
tcpEntryPoints: entryPoints,
|
tcpEntryPoints: entryPoints,
|
||||||
chainBuilder: chainBuilder,
|
observabilityMgr: observabilityMgr,
|
||||||
accessLoggerMiddleware: accessLoggerMiddleware,
|
signals: make(chan os.Signal, 1),
|
||||||
signals: make(chan os.Signal, 1),
|
stopChan: make(chan bool, 1),
|
||||||
stopChan: make(chan bool, 1),
|
routinesPool: routinesPool,
|
||||||
routinesPool: routinesPool,
|
udpEntryPoints: entryPointsUDP,
|
||||||
udpEntryPoints: entryPointsUDP,
|
|
||||||
tracerCloser: tracerCloser,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.configureSignals()
|
srv.configureSignals()
|
||||||
|
@ -105,13 +97,7 @@ func (s *Server) Close() {
|
||||||
|
|
||||||
close(s.stopChan)
|
close(s.stopChan)
|
||||||
|
|
||||||
s.chainBuilder.Close()
|
s.observabilityMgr.Close()
|
||||||
|
|
||||||
if s.tracerCloser != nil {
|
|
||||||
if err := s.tracerCloser.Close(); err != nil {
|
|
||||||
log.Error().Err(err).Msg("Could not close the tracer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,8 @@ func (s *Server) listenSignals(ctx context.Context) {
|
||||||
if sig == syscall.SIGUSR1 {
|
if sig == syscall.SIGUSR1 {
|
||||||
log.Info().Msgf("Closing and re-opening log files for rotation: %+v", sig)
|
log.Info().Msgf("Closing and re-opening log files for rotation: %+v", sig)
|
||||||
|
|
||||||
if s.accessLoggerMiddleware != nil {
|
if err := s.observabilityMgr.RotateAccessLogs(); err != nil {
|
||||||
if err := s.accessLoggerMiddleware.Rotate(); err != nil {
|
log.Error().Err(err).Msg("Error rotating access log")
|
||||||
log.Error().Err(err).Msg("Error rotating access log")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,12 @@ import (
|
||||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
"github.com/traefik/traefik/v3/pkg/metrics"
|
||||||
"github.com/traefik/traefik/v3/pkg/safe"
|
"github.com/traefik/traefik/v3/pkg/safe"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ManagerFactory a factory of service manager.
|
// ManagerFactory a factory of service manager.
|
||||||
type ManagerFactory struct {
|
type ManagerFactory struct {
|
||||||
metricsRegistry metrics.Registry
|
observabilityMgr *middleware.ObservabilityMgr
|
||||||
|
|
||||||
roundTripperManager *RoundTripperManager
|
roundTripperManager *RoundTripperManager
|
||||||
|
|
||||||
|
@ -29,9 +30,9 @@ type ManagerFactory struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManagerFactory creates a new ManagerFactory.
|
// NewManagerFactory creates a new ManagerFactory.
|
||||||
func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *safe.Pool, metricsRegistry metrics.Registry, roundTripperManager *RoundTripperManager, acmeHTTPHandler http.Handler) *ManagerFactory {
|
func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *safe.Pool, observabilityMgr *middleware.ObservabilityMgr, roundTripperManager *RoundTripperManager, acmeHTTPHandler http.Handler) *ManagerFactory {
|
||||||
factory := &ManagerFactory{
|
factory := &ManagerFactory{
|
||||||
metricsRegistry: metricsRegistry,
|
observabilityMgr: observabilityMgr,
|
||||||
routinesPool: routinesPool,
|
routinesPool: routinesPool,
|
||||||
roundTripperManager: roundTripperManager,
|
roundTripperManager: roundTripperManager,
|
||||||
acmeHTTPHandler: acmeHTTPHandler,
|
acmeHTTPHandler: acmeHTTPHandler,
|
||||||
|
@ -72,7 +73,7 @@ func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *s
|
||||||
|
|
||||||
// Build creates a service manager.
|
// Build creates a service manager.
|
||||||
func (f *ManagerFactory) Build(configuration *runtime.Configuration) *InternalHandlers {
|
func (f *ManagerFactory) Build(configuration *runtime.Configuration) *InternalHandlers {
|
||||||
svcManager := NewManager(configuration.Services, f.metricsRegistry, f.routinesPool, f.roundTripperManager)
|
svcManager := NewManager(configuration.Services, f.observabilityMgr, f.routinesPool, f.roundTripperManager)
|
||||||
|
|
||||||
var apiHandler http.Handler
|
var apiHandler http.Handler
|
||||||
if f.api != nil {
|
if f.api != nil {
|
||||||
|
|
|
@ -20,12 +20,12 @@ import (
|
||||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||||
"github.com/traefik/traefik/v3/pkg/healthcheck"
|
"github.com/traefik/traefik/v3/pkg/healthcheck"
|
||||||
"github.com/traefik/traefik/v3/pkg/logs"
|
"github.com/traefik/traefik/v3/pkg/logs"
|
||||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
||||||
metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
|
metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
|
||||||
tracingMiddle "github.com/traefik/traefik/v3/pkg/middlewares/tracing"
|
tracingMiddle "github.com/traefik/traefik/v3/pkg/middlewares/tracing"
|
||||||
"github.com/traefik/traefik/v3/pkg/safe"
|
"github.com/traefik/traefik/v3/pkg/safe"
|
||||||
"github.com/traefik/traefik/v3/pkg/server/cookie"
|
"github.com/traefik/traefik/v3/pkg/server/cookie"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||||
"github.com/traefik/traefik/v3/pkg/server/provider"
|
"github.com/traefik/traefik/v3/pkg/server/provider"
|
||||||
"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/failover"
|
"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/failover"
|
||||||
"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/mirror"
|
"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/mirror"
|
||||||
|
@ -42,7 +42,7 @@ type RoundTripperGetter interface {
|
||||||
// Manager The service manager.
|
// Manager The service manager.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
routinePool *safe.Pool
|
routinePool *safe.Pool
|
||||||
metricsRegistry metrics.Registry
|
observabilityMgr *middleware.ObservabilityMgr
|
||||||
bufferPool httputil.BufferPool
|
bufferPool httputil.BufferPool
|
||||||
roundTripperManager RoundTripperGetter
|
roundTripperManager RoundTripperGetter
|
||||||
|
|
||||||
|
@ -53,10 +53,10 @@ type Manager struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new Manager.
|
// NewManager creates a new Manager.
|
||||||
func NewManager(configs map[string]*runtime.ServiceInfo, metricsRegistry metrics.Registry, routinePool *safe.Pool, roundTripperManager RoundTripperGetter) *Manager {
|
func NewManager(configs map[string]*runtime.ServiceInfo, observabilityMgr *middleware.ObservabilityMgr, routinePool *safe.Pool, roundTripperManager RoundTripperGetter) *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
routinePool: routinePool,
|
routinePool: routinePool,
|
||||||
metricsRegistry: metricsRegistry,
|
observabilityMgr: observabilityMgr,
|
||||||
bufferPool: newBufferPool(),
|
bufferPool: newBufferPool(),
|
||||||
roundTripperManager: roundTripperManager,
|
roundTripperManager: roundTripperManager,
|
||||||
services: make(map[string]http.Handler),
|
services: make(map[string]http.Handler),
|
||||||
|
@ -302,12 +302,17 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
|
||||||
|
|
||||||
proxy := buildSingleHostProxy(target, passHostHeader, time.Duration(flushInterval), roundTripper, m.bufferPool)
|
proxy := buildSingleHostProxy(target, passHostHeader, time.Duration(flushInterval), roundTripper, m.bufferPool)
|
||||||
|
|
||||||
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceURL, target.String(), nil)
|
// Prevents from enabling observability for internal resources.
|
||||||
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceAddr, target.Host, nil)
|
|
||||||
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceName, serviceName, accesslog.AddServiceFields)
|
|
||||||
|
|
||||||
if m.metricsRegistry != nil && m.metricsRegistry.IsSvcEnabled() {
|
if m.observabilityMgr.ShouldAddAccessLogs(provider.GetQualifiedName(ctx, serviceName)) {
|
||||||
metricsHandler := metricsMiddle.WrapServiceHandler(ctx, m.metricsRegistry, serviceName)
|
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceURL, target.String(), nil)
|
||||||
|
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceAddr, target.Host, nil)
|
||||||
|
proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceName, serviceName, accesslog.AddServiceFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsSvcEnabled() &&
|
||||||
|
m.observabilityMgr.ShouldAddMetrics(provider.GetQualifiedName(ctx, serviceName)) {
|
||||||
|
metricsHandler := metricsMiddle.WrapServiceHandler(ctx, m.observabilityMgr.MetricsRegistry(), serviceName)
|
||||||
|
|
||||||
proxy, err = alice.New().
|
proxy, err = alice.New().
|
||||||
Append(tracingMiddle.WrapMiddleware(ctx, metricsHandler)).
|
Append(tracingMiddle.WrapMiddleware(ctx, metricsHandler)).
|
||||||
|
@ -317,7 +322,9 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy = tracingMiddle.NewService(ctx, serviceName, proxy)
|
if m.observabilityMgr.ShouldAddTracing(provider.GetQualifiedName(ctx, serviceName)) {
|
||||||
|
proxy = tracingMiddle.NewService(ctx, serviceName, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
lb.Add(proxyName, proxy, server.Weight)
|
lb.Add(proxyName, proxy, server.Weight)
|
||||||
|
|
||||||
|
@ -330,7 +337,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
|
||||||
if service.HealthCheck != nil {
|
if service.HealthCheck != nil {
|
||||||
m.healthCheckers[serviceName] = healthcheck.NewServiceHealthChecker(
|
m.healthCheckers[serviceName] = healthcheck.NewServiceHealthChecker(
|
||||||
ctx,
|
ctx,
|
||||||
m.metricsRegistry,
|
m.observabilityMgr.MetricsRegistry(),
|
||||||
service.HealthCheck,
|
service.HealthCheck,
|
||||||
lb,
|
lb,
|
||||||
info,
|
info,
|
||||||
|
|
|
@ -42,6 +42,11 @@ func NewTracing(conf *static.Tracing) (trace.Tracer, io.Closer, error) {
|
||||||
|
|
||||||
// TracerFromContext extracts the trace.Tracer from the given context.
|
// TracerFromContext extracts the trace.Tracer from the given context.
|
||||||
func TracerFromContext(ctx context.Context) trace.Tracer {
|
func TracerFromContext(ctx context.Context) trace.Tracer {
|
||||||
|
// Prevent picking trace.noopSpan tracer.
|
||||||
|
if !trace.SpanContextFromContext(ctx).IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
span := trace.SpanFromContext(ctx)
|
span := trace.SpanFromContext(ctx)
|
||||||
if span != nil && span.TracerProvider() != nil {
|
if span != nil && span.TracerProvider() != nil {
|
||||||
return span.TracerProvider().Tracer("github.com/traefik/traefik")
|
return span.TracerProvider().Tracer("github.com/traefik/traefik")
|
||||||
|
|
|
@ -45,6 +45,7 @@ type AccessLog struct {
|
||||||
Filters *AccessLogFilters `description:"Access log filters, used to keep only specific access logs." json:"filters,omitempty" toml:"filters,omitempty" yaml:"filters,omitempty" export:"true"`
|
Filters *AccessLogFilters `description:"Access log filters, used to keep only specific access logs." json:"filters,omitempty" toml:"filters,omitempty" yaml:"filters,omitempty" export:"true"`
|
||||||
Fields *AccessLogFields `description:"AccessLogFields." json:"fields,omitempty" toml:"fields,omitempty" yaml:"fields,omitempty" export:"true"`
|
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"`
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults sets the default values.
|
// SetDefaults sets the default values.
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
|
|
||||||
// Metrics provides options to expose and send Traefik metrics to different third party monitoring systems.
|
// Metrics provides options to expose and send Traefik metrics to different third party monitoring systems.
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
|
AddInternals bool `description:"Enables metrics for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"`
|
||||||
|
|
||||||
Prometheus *Prometheus `description:"Prometheus metrics exporter type." json:"prometheus,omitempty" toml:"prometheus,omitempty" yaml:"prometheus,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
Prometheus *Prometheus `description:"Prometheus metrics exporter type." json:"prometheus,omitempty" toml:"prometheus,omitempty" yaml:"prometheus,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
Datadog *Datadog `description:"Datadog metrics exporter type." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
Datadog *Datadog `description:"Datadog metrics exporter type." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
StatsD *Statsd `description:"StatsD metrics exporter type." json:"statsD,omitempty" toml:"statsD,omitempty" yaml:"statsD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
StatsD *Statsd `description:"StatsD metrics exporter type." json:"statsD,omitempty" toml:"statsD,omitempty" yaml:"statsD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
|
|
Loading…
Reference in a new issue