mirror of
https://github.com/traefik/traefik
synced 2026-02-03 12:20:33 +00:00
Add a new option to allow Stdio access logs alongsige OTLP logging
This commit is contained in:
@@ -10,6 +10,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||
| <a id="opt-accesslog" href="#opt-accesslog" title="#opt-accesslog">accesslog</a> | Access log settings. | false |
|
||||
| <a id="opt-accesslog-addinternals" href="#opt-accesslog-addinternals" title="#opt-accesslog-addinternals">accesslog.addinternals</a> | Enables access log for internal services (ping, dashboard, etc...). | false |
|
||||
| <a id="opt-accesslog-bufferingsize" href="#opt-accesslog-bufferingsize" title="#opt-accesslog-bufferingsize">accesslog.bufferingsize</a> | Number of access log lines to process in a buffered way. | 0 |
|
||||
| <a id="opt-accesslog-dualoutput" href="#opt-accesslog-dualoutput" title="#opt-accesslog-dualoutput">accesslog.dualoutput</a> | Enables access log output alongside OTLP. By default, this output is disabled when OTLP is configured. | false |
|
||||
| <a id="opt-accesslog-fields-defaultmode" href="#opt-accesslog-fields-defaultmode" title="#opt-accesslog-fields-defaultmode">accesslog.fields.defaultmode</a> | Default mode for fields: keep | drop | keep |
|
||||
| <a id="opt-accesslog-fields-headers-defaultmode" href="#opt-accesslog-fields-headers-defaultmode" title="#opt-accesslog-fields-headers-defaultmode">accesslog.fields.headers.defaultmode</a> | Default mode for fields: keep | drop | redact | drop |
|
||||
| <a id="opt-accesslog-fields-headers-names-name" href="#opt-accesslog-fields-headers-names-name" title="#opt-accesslog-fields-headers-names-name">accesslog.fields.headers.names._name_</a> | Override mode for headers | |
|
||||
|
||||
@@ -141,6 +141,9 @@ Traefik also supports the `OTEL_RESOURCE_ATTRIBUTES` env variable to set up the
|
||||
|
||||
Access logs concern everything that happens to the requests handled by Traefik.
|
||||
|
||||
!!! note "Stdio logs are not enabled by default alongside OTLP exports"
|
||||
If you would like Stdio access logs to be available, use [accessLog.dualOutput](#opt-accesslog-dualOutput) option.
|
||||
|
||||
### Configuration Example
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
@@ -195,6 +198,7 @@ accessLog:
|
||||
|
||||
```sh tab="CLI"
|
||||
--accesslog=true
|
||||
--accesslog.dualoutput=true
|
||||
--accesslog.format=json
|
||||
--accesslog.filters.statuscodes=200,300-302
|
||||
--accesslog.filters.retryattempts
|
||||
@@ -213,6 +217,7 @@ The section below describes how to configure Traefik access logs using the stati
|
||||
| Field | Description | Default | Required |
|
||||
|:-----------|:--------------------------|:--------|:---------|
|
||||
| <a id="opt-accesslog-filePath" href="#opt-accesslog-filePath" title="#opt-accesslog-filePath">`accesslog.filePath`</a> | By default, the access logs are written to the standard output.<br />You can configure a file path instead using the `filePath` option.| | No |
|
||||
| <a id="opt-accesslog-dualOutput" href="#opt-accesslog-dualOutput" title="#opt-accesslog-dualOutput">`accesslog.dualOutput`</a> | Force Stdio logging, even if OTLP is configured. By default, Stdio logging is disabled when OTLP is enabled for performance reasons. | false | No |
|
||||
| <a id="opt-accesslog-format" href="#opt-accesslog-format" title="#opt-accesslog-format">`accesslog.format`</a> | By default, logs are written using the Traefik Common Log Format (CLF).<br />Available formats: [`common`](#traefik-clf-format-fields) (Traefik extended CLF), [`genericCLF`](#generic-clf-format-fields) (standard CLF compatible with analyzers), or [`json`](#json-format-fields).<br />If the given format is unsupported, the default (`common`) is used instead. | "common" | No |
|
||||
| <a id="opt-accesslog-bufferingSize" href="#opt-accesslog-bufferingSize" title="#opt-accesslog-bufferingSize">`accesslog.bufferingSize`</a> | To write the logs in an asynchronous fashion, specify a `bufferingSize` option.<br />This option represents the number of log lines Traefik will keep in memory before writing them to the selected output.<br />In some cases, this option can greatly help performances.| 0 | No |
|
||||
| <a id="opt-accesslog-addInternals" href="#opt-accesslog-addInternals" title="#opt-accesslog-addInternals">`accesslog.addInternals`</a> | Enables access logs for internal resources (e.g.: `ping@internal`). | false | No |
|
||||
@@ -252,6 +257,8 @@ experimental:
|
||||
otlpLogs: true
|
||||
|
||||
accesslog:
|
||||
# Keep Stdio logs alongside OTEL logging
|
||||
dualOutput: true
|
||||
otlp:
|
||||
http:
|
||||
endpoint: https://collector:4318/v1/logs
|
||||
@@ -263,6 +270,9 @@ accesslog:
|
||||
[experimental]
|
||||
otlpLogs = true
|
||||
|
||||
[accessLog]
|
||||
dualOutput = true
|
||||
|
||||
[accesslog.otlp]
|
||||
http.endpoint = "https://collector:4318/v1/logs"
|
||||
http.headers.Authorization = "Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL"
|
||||
|
||||
@@ -128,8 +128,10 @@ func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error)
|
||||
}
|
||||
|
||||
logger.Hooks.Add(otellogrus.NewHook("traefik", otellogrus.WithLoggerProvider(otelLoggerProvider)))
|
||||
if !config.DualOutput {
|
||||
logger.Out = io.Discard
|
||||
}
|
||||
}
|
||||
|
||||
// Transform header names to a canonical form, to be used as is without further transformations,
|
||||
// and transform field names to lower case, to enable case-insensitive lookup.
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containous/alice"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
@@ -56,41 +57,99 @@ var (
|
||||
testStart = time.Now()
|
||||
)
|
||||
|
||||
func TestOTelAccessLogWithBody(t *testing.T) {
|
||||
func TestOTelAccessLogWithBodyAndDualOutput(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
format string
|
||||
filePath string
|
||||
dualOutput bool
|
||||
bodyCheckFn func(*testing.T, string)
|
||||
outLoggerCheckFn func(*testing.T, *logrus.Logger)
|
||||
}{
|
||||
{
|
||||
desc: "Common format with log body",
|
||||
format: CommonFormat,
|
||||
filePath: "",
|
||||
dualOutput: false,
|
||||
bodyCheckFn: func(t *testing.T, log string) {
|
||||
t.Helper()
|
||||
|
||||
// For common format, verify the body contains the Traefik common log formatted string
|
||||
assert.Regexp(t, `"body":{"stringValue":".*- /health -.*200.*[0-9]+ms.*"}`, log)
|
||||
},
|
||||
outLoggerCheckFn: func(t *testing.T, l *logrus.Logger) {
|
||||
t.Helper()
|
||||
|
||||
assert.Equal(t, l.Out, io.Discard)
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Generic CLF format with log body",
|
||||
format: GenericCLFFormat,
|
||||
filePath: "",
|
||||
dualOutput: false,
|
||||
bodyCheckFn: func(t *testing.T, log string) {
|
||||
t.Helper()
|
||||
|
||||
// For generic CLF format, verify the body contains the CLF formatted string
|
||||
assert.Regexp(t, `"body":{"stringValue":".*- /health -.*200.*"}`, log)
|
||||
},
|
||||
outLoggerCheckFn: func(t *testing.T, l *logrus.Logger) {
|
||||
t.Helper()
|
||||
|
||||
assert.Equal(t, l.Out, io.Discard)
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "JSON format with log body",
|
||||
format: JSONFormat,
|
||||
filePath: "",
|
||||
dualOutput: false,
|
||||
bodyCheckFn: func(t *testing.T, log string) {
|
||||
t.Helper()
|
||||
|
||||
// For JSON format, verify the body contains the JSON formatted string
|
||||
assert.Regexp(t, `"body":{"stringValue":".*DownstreamStatus.*:200.*"}`, log)
|
||||
},
|
||||
outLoggerCheckFn: func(t *testing.T, l *logrus.Logger) {
|
||||
t.Helper()
|
||||
|
||||
assert.Equal(t, l.Out, io.Discard)
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Common format with log body and Dual Output (STDOUT + OTEL)",
|
||||
format: CommonFormat,
|
||||
filePath: "",
|
||||
dualOutput: true,
|
||||
bodyCheckFn: func(t *testing.T, log string) {
|
||||
t.Helper()
|
||||
|
||||
// For common format, verify the body contains the Traefik common log formatted string
|
||||
assert.Regexp(t, `"body":{"stringValue":".*- /health -.*200.*[0-9]+ms.*"}`, log)
|
||||
},
|
||||
outLoggerCheckFn: func(t *testing.T, l *logrus.Logger) {
|
||||
t.Helper()
|
||||
|
||||
assert.NotEqual(t, l.Out, io.Discard)
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Common format with log body and Dual Output (File logging + OTEL)",
|
||||
format: CommonFormat,
|
||||
filePath: filepath.Join(t.TempDir(), "traefik.log"),
|
||||
dualOutput: true,
|
||||
bodyCheckFn: func(t *testing.T, log string) {
|
||||
t.Helper()
|
||||
|
||||
// For common format, verify the body contains the Traefik common log formatted string
|
||||
assert.Regexp(t, `"body":{"stringValue":".*- /health -.*200.*[0-9]+ms.*"}`, log)
|
||||
},
|
||||
outLoggerCheckFn: func(t *testing.T, l *logrus.Logger) {
|
||||
t.Helper()
|
||||
|
||||
assert.NotEqual(t, l.Out, io.Discard)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -119,6 +178,8 @@ func TestOTelAccessLogWithBody(t *testing.T) {
|
||||
|
||||
config := &otypes.AccessLog{
|
||||
Format: test.format,
|
||||
DualOutput: test.dualOutput,
|
||||
FilePath: test.filePath,
|
||||
OTLP: &otypes.OTelLog{
|
||||
ServiceName: "test",
|
||||
ResourceAttributes: map[string]string{"resource": "attribute"},
|
||||
@@ -179,6 +240,9 @@ func TestOTelAccessLogWithBody(t *testing.T) {
|
||||
|
||||
// Run format-specific body checks
|
||||
test.bodyCheckFn(t, log)
|
||||
|
||||
// Run OUT logger checks
|
||||
test.outLoggerCheckFn(t, logHandler.logger)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ type AccessLog struct {
|
||||
Fields *AccessLogFields `description:"AccessLogFields." json:"fields,omitempty" toml:"fields,omitempty" yaml:"fields,omitempty" export:"true"`
|
||||
BufferingSize int64 `description:"Number of access log lines to process in a buffered way." json:"bufferingSize,omitempty" toml:"bufferingSize,omitempty" yaml:"bufferingSize,omitempty" export:"true"`
|
||||
AddInternals bool `description:"Enables access log for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"`
|
||||
DualOutput bool `description:"Enables access log output alongside OTLP. By default, this output is disabled when OTLP is configured." json:"dualOutput,omitempty" toml:"dualOutput,omitempty" yaml:"dualOutput,omitempty" export:"true"`
|
||||
|
||||
OTLP *OTelLog `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user