Request Logger
RequestLogger middleware logs information about each HTTP request. It lets you fully
customize what is logged and how, making it well suited for use with third-party
(structured logging) libraries.
The values the logger can extract are controlled by the boolean and slice fields of
RequestLoggerConfig. Enable a field (for example LogStatus: true) to have its value
populated on the RequestLoggerValues passed to your LogValuesFunc.
type RequestLoggerConfig struct { // Skipper defines a function to skip middleware. Skipper Skipper
// BeforeNextFunc is called before the next middleware or handler in the chain. BeforeNextFunc func(c *echo.Context)
// LogValuesFunc is called with the values extracted by the logger from the // request/response. // Mandatory. LogValuesFunc func(c *echo.Context, v RequestLoggerValues) error
// HandleError instructs the logger to call the global error handler when the next // middleware/handler returns an error. A side effect is that the response is then // committed and sent, so middlewares up the chain can no longer change the status // code or body. HandleError bool
// LogLatency records the duration of the rest of the handler chain (the next(c) call). LogLatency bool // LogProtocol extracts the request protocol (for example HTTP/1.1 or HTTP/2). LogProtocol bool // LogRemoteIP extracts the request remote IP. See echo.Context.RealIP() for details. LogRemoteIP bool // LogHost extracts the request host value (for example example.com). LogHost bool // LogMethod extracts the request method (for example GET). LogMethod bool // LogURI extracts the request URI (for example /list?lang=en&page=1). LogURI bool // LogURIPath extracts the request URI path part (for example /list). LogURIPath bool // LogRoutePath extracts the route path the request matched (for example /user/:id). LogRoutePath bool // LogRequestID extracts the request ID from the X-Request-ID request header, or the // response if the request did not have a value. LogRequestID bool // LogReferer extracts the request referer value. LogReferer bool // LogUserAgent extracts the request user agent value. LogUserAgent bool // LogStatus extracts the response status code. If the chain returns an echo.HTTPError, // the status code is taken from it. LogStatus bool // LogError extracts the error returned from the handler chain. LogError bool // LogContentLength extracts the Content-Length header value. Note: this can differ // from the actual request body size as it may be spoofed. LogContentLength bool // LogResponseSize extracts the response content length. Note: when used with Gzip // middleware this value may not always be correct. LogResponseSize bool // LogHeaders extracts the given list of request headers. A slice of values is logged // per header since a request can contain more than one. Names are canonicalized with // http.CanonicalHeaderKey (for example "accept-encoding" becomes "Accept-Encoding"). LogHeaders []string // LogQueryParams extracts the given list of query parameters from the request URI. A // slice of values is logged per name since a request can repeat a parameter. LogQueryParams []string // LogFormValues extracts the given list of form values from the request body and URI. // A slice of values is logged per name since a request can repeat a value. LogFormValues []string}All core middleware lives in the middleware package:
import "github.com/labstack/echo/v5/middleware"Examples
Section titled “Examples”fmt.Printf
Section titled “fmt.Printf”skipper := func(c *echo.Context) bool { // Skip the health check endpoint. return c.Request().URL.Path == "/health"}e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ LogStatus: true, LogURI: true, Skipper: skipper, BeforeNextFunc: func(c *echo.Context) { c.Set("customValueFromContext", 42) }, LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error { value, _ := c.Get("customValueFromContext").(int) fmt.Printf("REQUEST: uri: %v, status: %v, custom-value: %v\n", v.URI, v.Status, value) return nil },}))Sample output:
REQUEST: uri: /hello, status: 200, custom-value: 42logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ LogStatus: true, LogURI: true, LogError: true, HandleError: true, // forwards the error to the global error handler so it can pick the status code LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error { if v.Error == nil { logger.LogAttrs(context.Background(), slog.LevelInfo, "REQUEST", slog.String("uri", v.URI), slog.Int("status", v.Status), ) } else { logger.LogAttrs(context.Background(), slog.LevelError, "REQUEST_ERROR", slog.String("uri", v.URI), slog.Int("status", v.Status), slog.String("err", v.Error.Error()), ) } return nil },}))Sample output:
{"time":"2024-12-30T20:55:46.2399999+08:00","level":"INFO","msg":"REQUEST","uri":"/hello","status":200}logger := zerolog.New(os.Stdout)e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ LogURI: true, LogStatus: true, LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error { logger.Info(). Str("URI", v.URI). Int("status", v.Status). Msg("request") return nil },}))Sample output:
{"level":"info","URI":"/hello","status":200,"message":"request"}logger, _ := zap.NewProduction()e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ LogURI: true, LogStatus: true, LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error { logger.Info("request", zap.String("URI", v.URI), zap.Int("status", v.Status), ) return nil },}))Sample output:
{"level":"info","ts":1735564026.3197417,"caller":"cmd/main.go:20","msg":"request","URI":"/hello","status":200}log := logrus.New()e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ LogURI: true, LogStatus: true, LogValuesFunc: func(c *echo.Context, values middleware.RequestLoggerValues) error { log.WithFields(logrus.Fields{ "URI": values.URI, "status": values.Status, }).Info("request") return nil },}))Sample output:
time="2024-12-30T21:08:49+08:00" level=info msg=request URI=/hello status=200Troubleshooting
Section titled “Troubleshooting”panic: missing LogValuesFunc callback function for request logger middleware
Section titled “panic: missing LogValuesFunc callback function for request logger middleware”This panic occurs when the mandatory LogValuesFunc callback is left unset. Define a
function matching the LogValuesFunc signature and assign it in the configuration:
func logValues(c *echo.Context, v middleware.RequestLoggerValues) error { fmt.Printf("Request Method: %s, URI: %s\n", v.Method, v.URI) return nil}
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ LogValuesFunc: logValues,}))Parameters in logs are empty
Section titled “Parameters in logs are empty”If values such as v.URI and v.Status are empty inside LogValuesFunc, check that the
corresponding extraction flags (LogStatus, LogURI, and so on) are set to true in
the configuration. Each value is only populated when its flag is enabled.