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)
|
||||
|
||||
// Metrics
|
||||
// Observability
|
||||
|
||||
metricRegistries := registerMetricClients(staticConfiguration.Metrics)
|
||||
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
|
||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||
tracer, tracerCloser := setupTracing(staticConfiguration.Tracing)
|
||||
observabilityMgr := middleware.NewObservabilityMgr(*staticConfiguration, metricsRegistry, accessLog, tracer, tracerCloser)
|
||||
|
||||
// Entrypoints
|
||||
|
||||
|
@ -263,14 +266,11 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||
roundTripperManager := service.NewRoundTripperManager(spiffeX509Source)
|
||||
dialerManager := tcp.NewDialerManager(spiffeX509Source)
|
||||
acmeHTTPHandler := getHTTPChallengeHandler(acmeProviders, httpChallengeProvider)
|
||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry, roundTripperManager, acmeHTTPHandler)
|
||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, observabilityMgr, roundTripperManager, acmeHTTPHandler)
|
||||
|
||||
// Router factory
|
||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||
|
||||
tracer, tracerCloser := setupTracing(staticConfiguration.Tracing)
|
||||
chainBuilder := middleware.NewChainBuilder(metricsRegistry, accessLog, tracer)
|
||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder, pluginBuilder, metricsRegistry, dialerManager)
|
||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, observabilityMgr, pluginBuilder, dialerManager)
|
||||
|
||||
// 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 {
|
||||
|
|
|
@ -693,3 +693,13 @@ Here are two possible transition strategies:
|
|||
This allows continued compatibility with the existing infrastructure.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
### `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`
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
| 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.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
To enable the tracing:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
|
@ -34,6 +32,26 @@ tracing: {}
|
|||
|
||||
### 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`
|
||||
|
||||
_Required, Default="traefik"_
|
||||
|
|
|
@ -6,6 +6,9 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
|||
`--accesslog`:
|
||||
Access log settings. (Default: ```false```)
|
||||
|
||||
`--accesslog.addinternals`:
|
||||
Enables access log for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||
|
||||
`--accesslog.bufferingsize`:
|
||||
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`:
|
||||
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`:
|
||||
Datadog metrics exporter type. (Default: ```false```)
|
||||
|
||||
|
@ -993,6 +999,9 @@ Defines the allowed SPIFFE trust domain.
|
|||
`--tracing`:
|
||||
OpenTracing configuration. (Default: ```false```)
|
||||
|
||||
`--tracing.addinternals`:
|
||||
Enables tracing for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||
|
||||
`--tracing.globalattributes.<name>`:
|
||||
Defines additional attributes (key:value) on all spans.
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
|||
`TRAEFIK_ACCESSLOG`:
|
||||
Access log settings. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_ACCESSLOG_ADDINTERNALS`:
|
||||
Enables access log for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||
|
||||
`TRAEFIK_ACCESSLOG_BUFFERINGSIZE`:
|
||||
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`:
|
||||
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`:
|
||||
Datadog metrics exporter type. (Default: ```false```)
|
||||
|
||||
|
@ -993,6 +999,9 @@ Defines the allowed SPIFFE trust domain.
|
|||
`TRAEFIK_TRACING`:
|
||||
OpenTracing configuration. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_TRACING_ADDINTERNALS`:
|
||||
Enables tracing for internal services (ping, dashboard, etc...). (Default: ```false```)
|
||||
|
||||
`TRAEFIK_TRACING_GLOBALATTRIBUTES_<NAME>`:
|
||||
Defines additional attributes (key:value) on all spans.
|
||||
|
||||
|
|
|
@ -277,6 +277,7 @@
|
|||
disableDashboardAd = true
|
||||
|
||||
[metrics]
|
||||
addInternals = true
|
||||
[metrics.prometheus]
|
||||
buckets = [42.0, 42.0]
|
||||
addEntryPointsLabels = true
|
||||
|
@ -351,6 +352,7 @@
|
|||
filePath = "foobar"
|
||||
format = "foobar"
|
||||
bufferingSize = 42
|
||||
addInternals = true
|
||||
[accessLog.filters]
|
||||
statusCodes = ["foobar", "foobar"]
|
||||
retryAttempts = true
|
||||
|
@ -369,6 +371,7 @@
|
|||
[tracing]
|
||||
serviceName = "foobar"
|
||||
sampleRate = 42.0
|
||||
addInternals = true
|
||||
[tracing.headers]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
|
|
|
@ -308,6 +308,7 @@ api:
|
|||
debug: true
|
||||
disableDashboardAd: true
|
||||
metrics:
|
||||
addInternals: true
|
||||
prometheus:
|
||||
buckets:
|
||||
- 42
|
||||
|
@ -399,6 +400,7 @@ accessLog:
|
|||
name0: foobar
|
||||
name1: foobar
|
||||
bufferingSize: 42
|
||||
addInternals: true
|
||||
tracing:
|
||||
serviceName: foobar
|
||||
headers:
|
||||
|
@ -408,6 +410,7 @@ tracing:
|
|||
name0: foobar
|
||||
name1: foobar
|
||||
sampleRate: 42
|
||||
addInternals: true
|
||||
otlp:
|
||||
grpc:
|
||||
endpoint: foobar
|
||||
|
|
|
@ -149,6 +149,7 @@ nav:
|
|||
- 'API': 'operations/api.md'
|
||||
- 'Ping': 'operations/ping.md'
|
||||
- 'Observability':
|
||||
- 'Overview': 'observability/overview.md'
|
||||
- 'Logs': 'observability/logs.md'
|
||||
- 'Access Logs': 'observability/access-logs.md'
|
||||
- 'Metrics':
|
||||
|
|
|
@ -61,7 +61,7 @@ func (s *AccessLogSuite) TestAccessLog() {
|
|||
ensureWorkingDirectoryIsClean()
|
||||
|
||||
// Start Traefik
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||
|
||||
defer func() {
|
||||
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||
|
@ -130,7 +130,7 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend() {
|
|||
}
|
||||
|
||||
// Start Traefik
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||
|
||||
s.checkStatsForLogFile()
|
||||
|
||||
|
@ -194,7 +194,7 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware() {
|
|||
}
|
||||
|
||||
// Start Traefik
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||
|
||||
s.checkStatsForLogFile()
|
||||
|
||||
|
@ -304,7 +304,7 @@ func (s *AccessLogSuite) TestAccessLogFrontendRedirect() {
|
|||
}
|
||||
|
||||
// Start Traefik
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||
|
||||
s.checkStatsForLogFile()
|
||||
|
||||
|
@ -410,7 +410,7 @@ func (s *AccessLogSuite) TestAccessLogRateLimit() {
|
|||
}
|
||||
|
||||
// Start Traefik
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||
|
||||
s.checkStatsForLogFile()
|
||||
|
||||
|
@ -454,7 +454,7 @@ func (s *AccessLogSuite) TestAccessLogBackendNotFound() {
|
|||
}
|
||||
|
||||
// Start Traefik
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||
|
||||
s.waitForTraefik("server1")
|
||||
|
||||
|
@ -494,7 +494,7 @@ func (s *AccessLogSuite) TestAccessLogFrontendAllowlist() {
|
|||
}
|
||||
|
||||
// Start Traefik
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||
|
||||
s.checkStatsForLogFile()
|
||||
|
||||
|
@ -534,7 +534,7 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontendSuccess() {
|
|||
}
|
||||
|
||||
// Start Traefik
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||
|
||||
s.checkStatsForLogFile()
|
||||
|
||||
|
@ -575,7 +575,7 @@ func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware() {
|
|||
}
|
||||
|
||||
// Start Traefik
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
|
||||
s.traefikCmd(withConfigFile("fixtures/access_log/access_log_base.toml"))
|
||||
|
||||
s.checkStatsForLogFile()
|
||||
|
||||
|
@ -603,6 +603,56 @@ func (s *AccessLogSuite) TestAccessLogPreflightHeadersMiddleware() {
|
|||
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() {
|
||||
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||
require.NoError(s.T(), err)
|
||||
|
@ -612,6 +662,8 @@ func (s *AccessLogSuite) checkNoOtherTraefikProblems() {
|
|||
}
|
||||
|
||||
func (s *AccessLogSuite) checkAccessLogOutput() int {
|
||||
s.T().Helper()
|
||||
|
||||
lines := s.extractLines()
|
||||
count := 0
|
||||
for i, line := range lines {
|
||||
|
@ -624,6 +676,8 @@ func (s *AccessLogSuite) checkAccessLogOutput() int {
|
|||
}
|
||||
|
||||
func (s *AccessLogSuite) checkAccessLogExactValuesOutput(values []accessLogValue) int {
|
||||
s.T().Helper()
|
||||
|
||||
lines := s.extractLines()
|
||||
count := 0
|
||||
for i, line := range lines {
|
||||
|
@ -641,6 +695,8 @@ func (s *AccessLogSuite) checkAccessLogExactValuesOutput(values []accessLogValue
|
|||
}
|
||||
|
||||
func (s *AccessLogSuite) extractLines() []string {
|
||||
s.T().Helper()
|
||||
|
||||
accessLog, err := os.ReadFile(traefikTestAccessLogFile)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
|
@ -656,6 +712,8 @@ func (s *AccessLogSuite) extractLines() []string {
|
|||
}
|
||||
|
||||
func (s *AccessLogSuite) checkStatsForLogFile() {
|
||||
s.T().Helper()
|
||||
|
||||
err := try.Do(1*time.Second, func() error {
|
||||
if _, errStat := os.Stat(traefikTestLogFile); errStat != nil {
|
||||
return fmt.Errorf("could not get stats for log file: %w", errStat)
|
||||
|
@ -671,6 +729,8 @@ func ensureWorkingDirectoryIsClean() {
|
|||
}
|
||||
|
||||
func (s *AccessLogSuite) checkTraefikStarted() []byte {
|
||||
s.T().Helper()
|
||||
|
||||
traefikLog, err := os.ReadFile(traefikTestLogFile)
|
||||
require.NoError(s.T(), err)
|
||||
if len(traefikLog) > 0 {
|
||||
|
@ -680,6 +740,8 @@ func (s *AccessLogSuite) checkTraefikStarted() []byte {
|
|||
}
|
||||
|
||||
func (s *BaseSuite) CheckAccessLogFormat(line string, i int) {
|
||||
s.T().Helper()
|
||||
|
||||
results, err := accesslog.ParseAccessLog(line)
|
||||
require.NoError(s.T(), err)
|
||||
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) {
|
||||
s.T().Helper()
|
||||
|
||||
results, err := accesslog.ParseAccessLog(line)
|
||||
require.NoError(s.T(), err)
|
||||
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
|
||||
|
||||
[metrics]
|
||||
addInternals = true
|
||||
[metrics.prometheus]
|
||||
buckets = [0.1,0.3,1.2,5.0]
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
[api]
|
||||
insecure = true
|
||||
|
||||
[ping]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":8000"
|
||||
|
@ -47,6 +49,10 @@
|
|||
Service = "service3"
|
||||
Middlewares = ["retry", "basic-auth"]
|
||||
Rule = "Path(`/auth`)"
|
||||
[http.routers.customPing]
|
||||
entryPoints = ["web"]
|
||||
rule = "PathPrefix(`/ping`)"
|
||||
service = "ping@internal"
|
||||
|
||||
[http.middlewares]
|
||||
[http.middlewares.retry.retry]
|
||||
|
|
|
@ -57,7 +57,7 @@ func (s *LogRotationSuite) TearDownSuite() {
|
|||
|
||||
func (s *LogRotationSuite) TestAccessLogRotation() {
|
||||
// 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)
|
||||
|
||||
// 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_"))
|
||||
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() {
|
||||
|
|
|
@ -414,6 +414,67 @@ func (s *TracingSuite) TestOpentelemetryAuth() {
|
|||
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) {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
@ -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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
|
|
|
@ -12,9 +12,10 @@ import (
|
|||
|
||||
// Muxer handles routing with rules.
|
||||
type Muxer struct {
|
||||
routes routes
|
||||
parser predicate.Parser
|
||||
parserV2 predicate.Parser
|
||||
routes routes
|
||||
parser predicate.Parser
|
||||
parserV2 predicate.Parser
|
||||
defaultHandler http.Handler
|
||||
}
|
||||
|
||||
// NewMuxer returns a new muxer instance.
|
||||
|
@ -40,8 +41,9 @@ func NewMuxer() (*Muxer, error) {
|
|||
}
|
||||
|
||||
return &Muxer{
|
||||
parser: parser,
|
||||
parserV2: parserV2,
|
||||
parser: parser,
|
||||
parserV2: parserV2,
|
||||
defaultHandler: http.NotFoundHandler(),
|
||||
}, 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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
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/traefik/traefik/v3/pkg/config/runtime"
|
||||
"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/denyrouterrecursion"
|
||||
metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
|
||||
|
@ -35,21 +34,19 @@ type serviceManager interface {
|
|||
type Manager struct {
|
||||
routerHandlers map[string]http.Handler
|
||||
serviceManager serviceManager
|
||||
metricsRegistry metrics.Registry
|
||||
observabilityMgr *middleware.ObservabilityMgr
|
||||
middlewaresBuilder middlewareBuilder
|
||||
chainBuilder *middleware.ChainBuilder
|
||||
conf *runtime.Configuration
|
||||
tlsManager *tls.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{
|
||||
routerHandlers: make(map[string]http.Handler),
|
||||
serviceManager: serviceManager,
|
||||
metricsRegistry: metricsRegistry,
|
||||
observabilityMgr: observabilityMgr,
|
||||
middlewaresBuilder: middlewaresBuilder,
|
||||
chainBuilder: chainBuilder,
|
||||
conf: conf,
|
||||
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()
|
||||
ctx := logger.WithContext(rootCtx)
|
||||
|
||||
handler, err := m.buildEntryPointHandler(ctx, routers)
|
||||
handler, err := m.buildEntryPointHandler(ctx, entryPointName, routers)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
continue
|
||||
}
|
||||
|
||||
handlerWithAccessLog, err := alice.New(func(next http.Handler) (http.Handler, error) {
|
||||
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
|
||||
}
|
||||
entryPointHandlers[entryPointName] = handler
|
||||
}
|
||||
|
||||
// Create default handlers.
|
||||
for _, entryPointName := range entryPoints {
|
||||
logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
|
||||
ctx := logger.WithContext(rootCtx)
|
||||
|
||||
handler, ok := entryPointHandlers[entryPointName]
|
||||
if !ok || handler == nil {
|
||||
handler = BuildDefaultHTTPRouter()
|
||||
if ok || handler != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
handlerWithMiddlewares, err := m.chainBuilder.Build(ctx, entryPointName).Then(handler)
|
||||
handler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "").Then(BuildDefaultHTTPRouter())
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
continue
|
||||
}
|
||||
entryPointHandlers[entryPointName] = handlerWithMiddlewares
|
||||
entryPointHandlers[entryPointName] = handler
|
||||
}
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
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 {
|
||||
logger := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
|
||||
ctxRouter := logger.WithContext(provider.AddInContext(ctx, routerName))
|
||||
|
@ -131,6 +128,14 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
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 {
|
||||
routerConfig.AddError(err, true)
|
||||
logger.Error().Err(err).Send()
|
||||
|
@ -167,6 +172,12 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, rou
|
|||
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) {
|
||||
return accesslog.NewFieldHandler(next, accesslog.RouterName, routerName, nil), nil
|
||||
}).Then(handler)
|
||||
|
@ -200,10 +211,20 @@ func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterIn
|
|||
|
||||
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)))
|
||||
|
||||
if m.metricsRegistry != nil && m.metricsRegistry.IsRouterEnabled() {
|
||||
metricsHandler := metricsMiddle.WrapRouterHandler(ctx, m.metricsRegistry, routerName, provider.GetQualifiedName(ctx, router.Service))
|
||||
if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsRouterEnabled() {
|
||||
metricsHandler := metricsMiddle.WrapRouterHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, provider.GetQualifiedName(ctx, router.Service))
|
||||
chain = chain.Append(tracing.WrapMiddleware(ctx, metricsHandler))
|
||||
}
|
||||
|
||||
|
|
|
@ -9,21 +9,15 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/alice"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"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/server/middleware"
|
||||
"github.com/traefik/traefik/v3/pkg/server/service"
|
||||
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
||||
"github.com/traefik/traefik/v3/pkg/tls"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
)
|
||||
|
||||
func TestRouterManager_Get(t *testing.T) {
|
||||
|
@ -319,10 +313,9 @@ func TestRouterManager_Get(t *testing.T) {
|
|||
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)
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager)
|
||||
|
||||
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) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -788,11 +661,10 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
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()
|
||||
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, true)
|
||||
|
@ -866,10 +738,9 @@ func TestProviderOnMiddlewares(t *testing.T) {
|
|||
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)
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager)
|
||||
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
|
@ -935,10 +806,9 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
|
||||
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)
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
||||
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||
tcpmiddleware "github.com/traefik/traefik/v3/pkg/server/middleware/tcp"
|
||||
"github.com/traefik/traefik/v3/pkg/server/router"
|
||||
|
@ -25,13 +24,12 @@ type RouterFactory struct {
|
|||
entryPointsTCP []string
|
||||
entryPointsUDP []string
|
||||
|
||||
managerFactory *service.ManagerFactory
|
||||
metricsRegistry metrics.Registry
|
||||
managerFactory *service.ManagerFactory
|
||||
|
||||
pluginBuilder middleware.PluginsBuilder
|
||||
|
||||
chainBuilder *middleware.ChainBuilder
|
||||
tlsManager *tls.Manager
|
||||
observabilityMgr *middleware.ObservabilityMgr
|
||||
tlsManager *tls.Manager
|
||||
|
||||
dialerManager *tcp.DialerManager
|
||||
|
||||
|
@ -40,7 +38,7 @@ type RouterFactory struct {
|
|||
|
||||
// NewRouterFactory creates a new RouterFactory.
|
||||
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 {
|
||||
var entryPointsTCP, entryPointsUDP []string
|
||||
for name, cfg := range staticConfiguration.EntryPoints {
|
||||
|
@ -58,14 +56,13 @@ func NewRouterFactory(staticConfiguration static.Configuration, managerFactory *
|
|||
}
|
||||
|
||||
return &RouterFactory{
|
||||
entryPointsTCP: entryPointsTCP,
|
||||
entryPointsUDP: entryPointsUDP,
|
||||
managerFactory: managerFactory,
|
||||
metricsRegistry: metricsRegistry,
|
||||
tlsManager: tlsManager,
|
||||
chainBuilder: chainBuilder,
|
||||
pluginBuilder: pluginBuilder,
|
||||
dialerManager: dialerManager,
|
||||
entryPointsTCP: entryPointsTCP,
|
||||
entryPointsUDP: entryPointsUDP,
|
||||
managerFactory: managerFactory,
|
||||
observabilityMgr: observabilityMgr,
|
||||
tlsManager: tlsManager,
|
||||
pluginBuilder: pluginBuilder,
|
||||
dialerManager: dialerManager,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,7 +80,7 @@ func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string
|
|||
|
||||
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)
|
||||
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/runtime"
|
||||
"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/service"
|
||||
"github.com/traefik/traefik/v3/pkg/tcp"
|
||||
|
@ -51,12 +50,12 @@ func TestReuseService(t *testing.T) {
|
|||
|
||||
roundTripperManager := service.NewRoundTripperManager(nil)
|
||||
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()
|
||||
|
||||
dialerManager := tcp.NewDialerManager(nil)
|
||||
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}))
|
||||
|
||||
|
@ -189,12 +188,13 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||
|
||||
roundTripperManager := service.NewRoundTripperManager(nil)
|
||||
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()
|
||||
|
||||
dialerManager := tcp.NewDialerManager(nil)
|
||||
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)}))
|
||||
|
||||
|
@ -232,14 +232,12 @@ func TestInternalServices(t *testing.T) {
|
|||
|
||||
roundTripperManager := service.NewRoundTripperManager(nil)
|
||||
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()
|
||||
|
||||
voidRegistry := metrics.NewVoidRegistry()
|
||||
|
||||
dialerManager := tcp.NewDialerManager(nil)
|
||||
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}))
|
||||
|
||||
|
|
|
@ -3,47 +3,39 @@ package server
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"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/safe"
|
||||
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||
)
|
||||
|
||||
// Server is the reverse-proxy/load-balancer engine.
|
||||
type Server struct {
|
||||
watcher *ConfigurationWatcher
|
||||
tcpEntryPoints TCPEntryPoints
|
||||
udpEntryPoints UDPEntryPoints
|
||||
chainBuilder *middleware.ChainBuilder
|
||||
|
||||
accessLoggerMiddleware *accesslog.Handler
|
||||
watcher *ConfigurationWatcher
|
||||
tcpEntryPoints TCPEntryPoints
|
||||
udpEntryPoints UDPEntryPoints
|
||||
observabilityMgr *middleware.ObservabilityMgr
|
||||
|
||||
signals chan os.Signal
|
||||
stopChan chan bool
|
||||
|
||||
routinesPool *safe.Pool
|
||||
|
||||
tracerCloser io.Closer
|
||||
}
|
||||
|
||||
// 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{
|
||||
watcher: watcher,
|
||||
tcpEntryPoints: entryPoints,
|
||||
chainBuilder: chainBuilder,
|
||||
accessLoggerMiddleware: accessLoggerMiddleware,
|
||||
signals: make(chan os.Signal, 1),
|
||||
stopChan: make(chan bool, 1),
|
||||
routinesPool: routinesPool,
|
||||
udpEntryPoints: entryPointsUDP,
|
||||
tracerCloser: tracerCloser,
|
||||
watcher: watcher,
|
||||
tcpEntryPoints: entryPoints,
|
||||
observabilityMgr: observabilityMgr,
|
||||
signals: make(chan os.Signal, 1),
|
||||
stopChan: make(chan bool, 1),
|
||||
routinesPool: routinesPool,
|
||||
udpEntryPoints: entryPointsUDP,
|
||||
}
|
||||
|
||||
srv.configureSignals()
|
||||
|
@ -105,13 +97,7 @@ func (s *Server) Close() {
|
|||
|
||||
close(s.stopChan)
|
||||
|
||||
s.chainBuilder.Close()
|
||||
|
||||
if s.tracerCloser != nil {
|
||||
if err := s.tracerCloser.Close(); err != nil {
|
||||
log.Error().Err(err).Msg("Could not close the tracer")
|
||||
}
|
||||
}
|
||||
s.observabilityMgr.Close()
|
||||
|
||||
cancel()
|
||||
}
|
||||
|
|
|
@ -24,10 +24,8 @@ func (s *Server) listenSignals(ctx context.Context) {
|
|||
if sig == syscall.SIGUSR1 {
|
||||
log.Info().Msgf("Closing and re-opening log files for rotation: %+v", sig)
|
||||
|
||||
if s.accessLoggerMiddleware != nil {
|
||||
if err := s.accessLoggerMiddleware.Rotate(); err != nil {
|
||||
log.Error().Err(err).Msg("Error rotating access log")
|
||||
}
|
||||
if err := s.observabilityMgr.RotateAccessLogs(); err != nil {
|
||||
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/metrics"
|
||||
"github.com/traefik/traefik/v3/pkg/safe"
|
||||
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||
)
|
||||
|
||||
// ManagerFactory a factory of service manager.
|
||||
type ManagerFactory struct {
|
||||
metricsRegistry metrics.Registry
|
||||
observabilityMgr *middleware.ObservabilityMgr
|
||||
|
||||
roundTripperManager *RoundTripperManager
|
||||
|
||||
|
@ -29,9 +30,9 @@ type ManagerFactory struct {
|
|||
}
|
||||
|
||||
// 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{
|
||||
metricsRegistry: metricsRegistry,
|
||||
observabilityMgr: observabilityMgr,
|
||||
routinesPool: routinesPool,
|
||||
roundTripperManager: roundTripperManager,
|
||||
acmeHTTPHandler: acmeHTTPHandler,
|
||||
|
@ -72,7 +73,7 @@ func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *s
|
|||
|
||||
// Build creates a service manager.
|
||||
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
|
||||
if f.api != nil {
|
||||
|
|
|
@ -20,12 +20,12 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||
"github.com/traefik/traefik/v3/pkg/healthcheck"
|
||||
"github.com/traefik/traefik/v3/pkg/logs"
|
||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
||||
metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics"
|
||||
tracingMiddle "github.com/traefik/traefik/v3/pkg/middlewares/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/safe"
|
||||
"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/service/loadbalancer/failover"
|
||||
"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/mirror"
|
||||
|
@ -42,7 +42,7 @@ type RoundTripperGetter interface {
|
|||
// Manager The service manager.
|
||||
type Manager struct {
|
||||
routinePool *safe.Pool
|
||||
metricsRegistry metrics.Registry
|
||||
observabilityMgr *middleware.ObservabilityMgr
|
||||
bufferPool httputil.BufferPool
|
||||
roundTripperManager RoundTripperGetter
|
||||
|
||||
|
@ -53,10 +53,10 @@ type Manager struct {
|
|||
}
|
||||
|
||||
// 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{
|
||||
routinePool: routinePool,
|
||||
metricsRegistry: metricsRegistry,
|
||||
observabilityMgr: observabilityMgr,
|
||||
bufferPool: newBufferPool(),
|
||||
roundTripperManager: roundTripperManager,
|
||||
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 = 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)
|
||||
// Prevents from enabling observability for internal resources.
|
||||
|
||||
if m.metricsRegistry != nil && m.metricsRegistry.IsSvcEnabled() {
|
||||
metricsHandler := metricsMiddle.WrapServiceHandler(ctx, m.metricsRegistry, serviceName)
|
||||
if m.observabilityMgr.ShouldAddAccessLogs(provider.GetQualifiedName(ctx, 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().
|
||||
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)
|
||||
|
||||
|
@ -330,7 +337,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
|
|||
if service.HealthCheck != nil {
|
||||
m.healthCheckers[serviceName] = healthcheck.NewServiceHealthChecker(
|
||||
ctx,
|
||||
m.metricsRegistry,
|
||||
m.observabilityMgr.MetricsRegistry(),
|
||||
service.HealthCheck,
|
||||
lb,
|
||||
info,
|
||||
|
|
|
@ -42,6 +42,11 @@ func NewTracing(conf *static.Tracing) (trace.Tracer, io.Closer, error) {
|
|||
|
||||
// TracerFromContext extracts the trace.Tracer from the given context.
|
||||
func TracerFromContext(ctx context.Context) trace.Tracer {
|
||||
// Prevent picking trace.noopSpan tracer.
|
||||
if !trace.SpanContextFromContext(ctx).IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if span != nil && span.TracerProvider() != nil {
|
||||
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"`
|
||||
Fields *AccessLogFields `description:"AccessLogFields." json:"fields,omitempty" toml:"fields,omitempty" yaml:"fields,omitempty" export:"true"`
|
||||
BufferingSize int64 `description:"Number of access log lines to process in a buffered way." json:"bufferingSize,omitempty" toml:"bufferingSize,omitempty" yaml:"bufferingSize,omitempty" export:"true"`
|
||||
AddInternals bool `description:"Enables access log for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
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"`
|
||||
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"`
|
||||
|
|
Loading…
Reference in a new issue