labstack / echo

High performance, minimalist Go web framework
https://echo.labstack.com
MIT License
28.98k stars 2.2k forks source link

unable to skip log based on the response status #2649

Open shortcuts opened 2 weeks ago

shortcuts commented 2 weeks ago

Issue Description

I've tried to skip logs for 404 and 429 but was unable to by using a simple Skipper because it is evaluated before next has been called and therefore the status is always 200.

Checklist

Expected behaviour

Being able to skip based on the status

Actual behaviour

Skipper is called before the status has been set

Steps to reproduce

  1. Trigger a 404
  2. See the log being outputted
  3. Add a print in the middleware, see status 200 on 404 requests

Working code to debug

package main

func main() {
    e := echo.New()

    e.Use(
        middleware.LoggerWithConfig(middleware.LoggerConfig{
            Skipper: func(c echo.Context) bool {
                return c.Response().Status == http.StatusNotFound || c.Response().Status == http.StatusTooManyRequests
            },
        }),
    )

    e.Start(":1313")

}

See https://github.com/shortcuts/codes/pull/9/files also if you want to see the full file

Version/commit

Latest

aldas commented 1 week ago

This is working as intended. Skipper is called always before next middleware/handler is called. At this time there is no response (code) yet.

You can use RequestLogger middleware to ignore some log lines. See

    logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
    e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
        LogStatus:   true,
        LogURI:      true,
        LogError:    true,
        HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code
        LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
            if v.Status == http.StatusNotFound || v.Status == http.StatusTooManyRequests {
                return nil
            }

            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
        },
    }))
shortcuts commented 1 week ago

Thanks for the fast answer @aldas !

Naively I thought that the skipper (in the log context) scope was to skip logs for any situation and the request logger reserved to 3rd party integrations and/or customization.

I think we can close this issue then, I'll go with your suggested solution, thanks!