diff --git a/middlewares/accesslog/logger_formatters.go b/middlewares/accesslog/logger_formatters.go index a19dffc46..fcc63e6c9 100644 --- a/middlewares/accesslog/logger_formatters.go +++ b/middlewares/accesslog/logger_formatters.go @@ -9,7 +9,10 @@ import ( ) // default format for time presentation -const commonLogTimeFormat = "02/Jan/2006:15:04:05 -0700" +const ( + commonLogTimeFormat = "02/Jan/2006:15:04:05 -0700" + defaultValue = "-" +) // CommonLogFormatter provides formatting in the Traefik common log format type CommonLogFormatter struct{} @@ -21,27 +24,26 @@ func (f *CommonLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { timestamp := entry.Data[StartUTC].(time.Time).Format(commonLogTimeFormat) elapsedMillis := entry.Data[Duration].(time.Duration).Nanoseconds() / 1000000 - _, err := fmt.Fprintf(b, "%s - %s [%s] \"%s %s %s\" %d %d %s %s %d %s %s %dms\n", + _, err := fmt.Fprintf(b, "%s - %s [%s] \"%s %s %s\" %v %v %s %s %v %s %s %dms\n", entry.Data[ClientHost], entry.Data[ClientUsername], timestamp, entry.Data[RequestMethod], entry.Data[RequestPath], entry.Data[RequestProtocol], - entry.Data[OriginStatus], - entry.Data[OriginContentSize], - toLogString(entry.Data["request_Referer"]), - toLogString(entry.Data["request_User-Agent"]), - entry.Data[RequestCount], - toLogString(entry.Data[FrontendName]), - toLogString(entry.Data[BackendURL]), + toLog(entry.Data[OriginStatus]), + toLog(entry.Data[OriginContentSize]), + toLog(entry.Data["request_Referer"]), + toLog(entry.Data["request_User-Agent"]), + toLog(entry.Data[RequestCount]), + toLog(entry.Data[FrontendName]), + toLog(entry.Data[BackendURL]), elapsedMillis) return b.Bytes(), err } -func toLogString(v interface{}) string { - defaultValue := "-" +func toLog(v interface{}) interface{} { if v == nil { return defaultValue } @@ -54,7 +56,7 @@ func toLogString(v interface{}) string { return quoted(s.String(), defaultValue) default: - return defaultValue + return v } } diff --git a/middlewares/accesslog/logger_formatters_test.go b/middlewares/accesslog/logger_formatters_test.go new file mode 100644 index 000000000..051d73850 --- /dev/null +++ b/middlewares/accesslog/logger_formatters_test.go @@ -0,0 +1,114 @@ +package accesslog + +import ( + "net/http" + "testing" + "time" + + "github.com/Sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestCommonLogFormatter_Format(t *testing.T) { + clf := CommonLogFormatter{} + + testCases := []struct { + name string + data map[string]interface{} + expectedLog string + }{ + { + name: "OriginStatus & OriginContentSize are nil", + data: map[string]interface{}{ + StartUTC: 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: nil, + OriginContentSize: nil, + "request_Referer": "", + "request_User-Agent": "", + RequestCount: 0, + FrontendName: "", + BackendURL: "", + }, + expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" - - - - 0 - - 123000ms +`, + }, + { + name: "all data", + data: map[string]interface{}{ + StartUTC: 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, + "request_Referer": "referer", + "request_User-Agent": "agent", + RequestCount: nil, + FrontendName: "foo", + 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 +`, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + entry := &logrus.Entry{Data: test.data} + + raw, err := clf.Format(entry) + assert.NoError(t, err) + + assert.Equal(t, test.expectedLog, string(raw)) + }) + } + +} + +func Test_toLog(t *testing.T) { + + testCases := []struct { + name string + value interface{} + expectedLog interface{} + }{ + { + name: "", + value: 1, + expectedLog: 1, + }, + { + name: "", + value: "foo", + expectedLog: `"foo"`, + }, + { + name: "", + value: nil, + expectedLog: "-", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + lg := toLog(test.value) + + assert.Equal(t, test.expectedLog, lg) + }) + } +}