diff --git a/docs/configuration/logs.md b/docs/configuration/logs.md index 6a3ee2fc6..d983f4952 100644 --- a/docs/configuration/logs.md +++ b/docs/configuration/logs.md @@ -292,3 +292,34 @@ This allows the logs to be rotated and processed by an external program, such as !!! note This does not work on Windows due to the lack of USR signals. + +## Time Zones + +The timestamp of each log line is in UTC time by default. + +If you want to use local timezone, you need to ensure the 3 following elements: + +1. Provide the timezone data into /usr/share/zoneinfo +2. Set the environement variable TZ to the timezone to be used +3. Specify the field StartLocal instead of StartUTC (works on default Common Log Format (CLF) as well as JSON) + +Example using docker-compose: + +```yml +version: '3' + +services: + traefik: + image: containous/traefik:[latest stable version] + ports: + - "80:80" + environment: + - "TZ=US/Alaska" + command: + - --docker + - --accesslog + - --accesslog.fields.names="StartUTC=drop" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + - "/usr/share/zoneinfo:/usr/share/zoneinfo:ro" +``` diff --git a/middlewares/accesslog/logger_formatters.go b/middlewares/accesslog/logger_formatters.go index 4755079fe..2e0add609 100644 --- a/middlewares/accesslog/logger_formatters.go +++ b/middlewares/accesslog/logger_formatters.go @@ -24,6 +24,8 @@ func (f *CommonLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { var timestamp = defaultValue if v, ok := entry.Data[StartUTC]; ok { timestamp = v.(time.Time).Format(commonLogTimeFormat) + } else if v, ok := entry.Data[StartLocal]; ok { + timestamp = v.(time.Time).Local().Format(commonLogTimeFormat) } var elapsedMillis int64 diff --git a/middlewares/accesslog/logger_formatters_test.go b/middlewares/accesslog/logger_formatters_test.go index 22b68da58..3d2b125c0 100644 --- a/middlewares/accesslog/logger_formatters_test.go +++ b/middlewares/accesslog/logger_formatters_test.go @@ -2,6 +2,7 @@ package accesslog import ( "net/http" + "os" "testing" "time" @@ -57,10 +58,34 @@ func TestCommonLogFormatter_Format(t *testing.T) { BackendURL: "http://10.0.0.2/toto", }, expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" 123 132 "referer" "agent" - "foo" "http://10.0.0.2/toto" 123000ms +`, + }, + { + name: "all data with local time", + data: map[string]interface{}{ + StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), + Duration: 123 * time.Second, + ClientHost: "10.0.0.1", + ClientUsername: "Client", + RequestMethod: http.MethodGet, + RequestPath: "/foo", + RequestProtocol: "http", + OriginStatus: 123, + OriginContentSize: 132, + RequestRefererHeader: "referer", + RequestUserAgentHeader: "agent", + RequestCount: nil, + FrontendName: "foo", + BackendURL: "http://10.0.0.2/toto", + }, + expectedLog: `10.0.0.1 - Client [10/Nov/2009:14:00:00 -0900] "GET /foo http" 123 132 "referer" "agent" - "foo" "http://10.0.0.2/toto" 123000ms `, }, } + // Set timezone to Alaska to have a constant behavior + os.Setenv("TZ", "US/Alaska") + for _, test := range testCases { test := test t.Run(test.name, func(t *testing.T) {