@@ -10,55 +10,88 @@ import (
1010
1111// Example for `fmt.Printf`
1212// e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
13- // LogStatus: true,
14- // LogURI: true,
13+ // LogStatus: true,
14+ // LogURI: true,
15+ // LogError: true,
16+ // HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code
1517// LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
16- // fmt.Printf("REQUEST: uri: %v, status: %v\n", v.URI, v.Status)
18+ // if v.Error == nil {
19+ // fmt.Printf("REQUEST: uri: %v, status: %v\n", v.URI, v.Status)
20+ // } else {
21+ // fmt.Printf("REQUEST_ERROR: uri: %v, status: %v, err: %v\n", v.URI, v.Status, v.Error)
22+ // }
1723// return nil
1824// },
1925// }))
2026//
2127// Example for Zerolog (https://github.com/rs/zerolog)
2228// logger := zerolog.New(os.Stdout)
2329// e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
24- // LogURI: true,
25- // LogStatus: true,
30+ // LogURI: true,
31+ // LogStatus: true,
32+ // LogError: true,
33+ // HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code
2634// LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
27- // logger.Info().
28- // Str("URI", v.URI).
29- // Int("status", v.Status).
30- // Msg("request")
31- //
35+ // if v.Error == nil {
36+ // logger.Info().
37+ // Str("URI", v.URI).
38+ // Int("status", v.Status).
39+ // Msg("request")
40+ // } else {
41+ // logger.Error().
42+ // Err(v.Error).
43+ // Str("URI", v.URI).
44+ // Int("status", v.Status).
45+ // Msg("request error")
46+ // }
3247// return nil
3348// },
3449// }))
3550//
3651// Example for Zap (https://github.com/uber-go/zap)
3752// logger, _ := zap.NewProduction()
3853// e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
39- // LogURI: true,
40- // LogStatus: true,
54+ // LogURI: true,
55+ // LogStatus: true,
56+ // LogError: true,
57+ // HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code
4158// LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
42- // logger.Info("request",
43- // zap.String("URI", v.URI),
44- // zap.Int("status", v.Status),
45- // )
46- //
59+ // if v.Error == nil {
60+ // logger.Info("request",
61+ // zap.String("URI", v.URI),
62+ // zap.Int("status", v.Status),
63+ // )
64+ // } else {
65+ // logger.Error("request error",
66+ // zap.String("URI", v.URI),
67+ // zap.Int("status", v.Status),
68+ // zap.Error(v.Error),
69+ // )
70+ // }
4771// return nil
4872// },
4973// }))
5074//
5175// Example for Logrus (https://github.com/sirupsen/logrus)
52- // log := logrus.New()
76+ // log := logrus.New()
5377// e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
54- // LogURI: true,
55- // LogStatus: true,
56- // LogValuesFunc: func(c echo.Context, values middleware.RequestLoggerValues) error {
57- // log.WithFields(logrus.Fields{
58- // "URI": values.URI,
59- // "status": values.Status,
60- // }).Info("request")
61- //
78+ // LogURI: true,
79+ // LogStatus: true,
80+ // LogError: true,
81+ // HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code
82+ // LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
83+ // if v.Error == nil {
84+ // log.WithFields(logrus.Fields{
85+ // "URI": v.URI,
86+ // "status": v.Status,
87+ // }).Info("request")
88+ // } else {
89+ // log.WithFields(logrus.Fields{
90+ // "URI": v.URI,
91+ // "status": v.Status,
92+ // "error": v.Error,
93+ // }).Error("request error")
94+ // }
6295// return nil
6396// },
6497// }))
@@ -74,6 +107,13 @@ type RequestLoggerConfig struct {
74107 // Mandatory.
75108 LogValuesFunc func (c echo.Context , v RequestLoggerValues ) error
76109
110+ // HandleError instructs logger to call global error handler when next middleware/handler returns an error.
111+ // This is useful when you have custom error handler that can decide to use different status codes.
112+ //
113+ // A side-effect of calling global error handler is that now Response has been committed and sent to the client
114+ // and middlewares up in chain can not change Response status code or response body.
115+ HandleError bool
116+
77117 // LogLatency instructs logger to record duration it took to execute rest of the handler chain (next(c) call).
78118 LogLatency bool
79119 // LogProtocol instructs logger to extract request protocol (i.e. `HTTP/1.1` or `HTTP/2`)
@@ -217,6 +257,9 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
217257 config .BeforeNextFunc (c )
218258 }
219259 err := next (c )
260+ if config .HandleError {
261+ c .Error (err )
262+ }
220263
221264 v := RequestLoggerValues {
222265 StartTime : start ,
@@ -264,7 +307,9 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
264307 }
265308 if config .LogStatus {
266309 v .Status = res .Status
267- if err != nil {
310+ if err != nil && ! config .HandleError {
311+ // this block should not be executed in case of HandleError=true as the global error handler will decide
312+ // the status code. In that case status code could be different from what err contains.
268313 var httpErr * echo.HTTPError
269314 if errors .As (err , & httpErr ) {
270315 v .Status = httpErr .Code
@@ -310,6 +355,9 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
310355 return errOnLog
311356 }
312357
358+ // in case of HandleError=true we are returning the error that we already have handled with global error handler
359+ // this is deliberate as this error could be useful for upstream middlewares and default global error handler
360+ // will ignore that error when it bubbles up in middleware chain.
313361 return err
314362 }
315363 }, nil
0 commit comments