diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 2df814eb..ddcab5bd 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -50,9 +50,20 @@ type Config struct { timeZoneLocation *time.Location - // Format defines the logging tags + // Format defines the logging format for the middleware. // - // Optional. Default: [${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error} + // You can customize the log output by defining a format string with placeholders + // such as: ${time}, ${ip}, ${status}, ${method}, ${path}, ${latency}, ${error}, etc. + // The full list of available placeholders can be found in 'tags.go' or at + // 'https://docs.gofiber.io/api/middleware/logger/#constants'. + // + // Alternatively, you can use one of the predefined formats: + // - "default" → Uses the default log format: "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}" + // - "common" → Uses the Common Log Format (CLF): "${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent}" + // - "combined" → Uses the Combined Log Format: "${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent} "${referer}" "${ua}"" + // - "json" → Uses the JSON structured log format: "{"time":"${time}","ip":"${ip}","method":"${method}","url":"${url}","status":${status},"bytesSent":${bytesSent}}" + // + // If no format is specified, the default format is used: "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}" Format string // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html diff --git a/middleware/logger/format.go b/middleware/logger/format.go new file mode 100644 index 00000000..2f4c2978 --- /dev/null +++ b/middleware/logger/format.go @@ -0,0 +1,16 @@ +package logger + +const ( + FormatDefault = "${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}" + FormatCommonLog = "${ip} - - [${time}] \"${method} ${url} ${protocol}\" ${status} ${bytesSent}\n" + FormatCombined = "${ip} - - [${time}] \"${method} ${url} ${protocol}\" ${status} ${bytesSent} \"${referer}\" \"${ua}\"\n" + FormatJSON = "{\"time\":\"${time}\",\"ip\":\"${ip}\",\"method\":\"${method}\",\"url\":\"${url}\",\"status\":${status},\"bytesSent\":${bytesSent}}\n" +) + +// LoggerConfig provides a mapping of predefined formats +var LoggerConfig = map[string]string{ + "default": FormatDefault, + "common": FormatCommonLog, + "combined": FormatCombined, + "json": FormatJSON, +} diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 793c16c2..05772b8a 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -31,6 +31,10 @@ func New(config ...Config) fiber.Handler { // Create correct timeformat timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat)) + if logFormat, exists := LoggerConfig[cfg.Format]; exists { + cfg.Format = logFormat + } + // Update date/time every 500 milliseconds in a separate go routine if strings.Contains(cfg.Format, "${"+TagTime+"}") { go func() { @@ -40,7 +44,6 @@ func New(config ...Config) fiber.Handler { } }() } - // Set PID once pid := strconv.Itoa(os.Getpid()) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index eb1b6a44..f04ba2b9 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -467,6 +467,31 @@ func Test_Logger_All(t *testing.T) { require.Equal(t, expected, buf.String()) } +func Test_Logger_CLF(t *testing.T) { + t.Parallel() + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + app := fiber.New() + + app.Use(New(Config{ + Format: "common", + Stream: buf, + })) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/?foo=bar", nil)) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) + + expected := fmt.Sprintf("0.0.0.0 - - [%s] \"%s %s %s\" %d %d\n", + time.Now().Format("15:04:05"), + fiber.MethodGet, "/?foo=bar", "HTTP/1.1", + fiber.StatusNotFound, + 0) + logAnswer := buf.String() + require.Equal(t, expected, logAnswer) +} + func getLatencyTimeUnits() []struct { unit string div time.Duration