Go Echoでのログ管理プラクティス:標準Loggerからslog連携まで

に公開

Echoが標準で提供しているログ管理機能

Logger

✅ 実装例 (実際のコミット)

e.Use(middleware.Logger())

🚀 出力例

⇨ http server started on [::]:1323
{"time":"2025-06-13T05:58:06.15304+09:00","id":"","remote_ip":"::1","host":"localhost:1323","method":"GET","uri":"/","user_agent":"curl/8.7.1","status":200,"error":"","latency":2375,"latency_human":"2.375µs","bytes_in":0,"bytes_out":11}

Loggerの出力をカスタマイズする

✅ 実装例 (実際のコミット例)

e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
		Format: "method=${method}, uri=${uri}, status=${status}\n",
	}))

🚀 出力例

⇨ http server started on [::]:1323
method=GET, uri=/, status=200

RequestLogger

✅ 実装例-1 (実際のコミット)

skipper := func(c echo.Context) bool {
	// Skip 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
	},
})) 

🚀 出力例 (REQUEST curl localhost:1323/ )

⇨ http server started on [::]:1323
REQUEST: uri: /, status: 200, custom-value: 42

curl localhost:1323/health へのリクエスト時のログ出力はskip設定されているので、出力されません

✅ 実装例-2 (実際のコミット)

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.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
    },
}))

🚀 出力例 (REQUEST curl localhost:1323/ )

⇨ http server started on [::]:1323
{"time":"2025-06-13T06:10:15.515028+09:00","level":"INFO","msg":"REQUEST","uri":"/","status":200}

ここで、あえてエラーを出力してみます (エラー検証コミット)

🚀 出力例 (REQUEST curl localhost:1323/error )

⇨ http server started on [::]:1323
{"time":"2025-06-13T06:15:23.617689+09:00","level":"ERROR","msg":"REQUEST_ERROR","uri":"/error","status":500,"err":"code=500, message=intentional error for testing"}

レベルがエラー ( "level":"ERROR" ) として出力されていることがわかります

まとめ

以上のように、echoで用意されているログ機能では、ログレベルの出力を分けらるように作られていません。

よって、単純なログの場合は Logger を利用し、エラーログをレベルカスタマイズ等したい場合は RequestLogger を利用し、3rd partyと組み合わせましょう

Discussion