labstack/echoには、以前から middleware.LoggerWithConfig が存在していて、リクエストログのカスタマイズがある程度は可能でした。公式のLogger Middlewareドキュメントより引用します。
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ Format: "method=${method}, uri=${uri}, status=${status}\n", }))
この Format は fasttemplate で処理されます。テンプレートの変数はレイテンシやリクエストサイズ、IPアドレスなどミドルウェア側で用意されている値を選択することはできますが、アプリケーション側で用意した任意の値を出力させることができませんでした。また、 io.Writer で出力先の変更はできますが、io.Writer インターフェイスを満たさない任意のロガーを使うことはできませんでした。なので任意の値を追加するためには、独自のミドルウェアを実装して、LoggerWithConfigが行っている処理と同じようにリクエストログで必要な値を echo.Context から計算する必要がありました。
RequestLoggerWithConfig
echoのv4.6で middleware.RequestLoggerWithConfig が追加されて、リクエストログのカスタマイズが少しだけ簡単になりました。以下に例を示します。
package main import ( "log" "github.com/go-logr/stdr" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) var logger = stdr.New(log.Default()) func writeRequestLog(c echo.Context, v middleware.RequestLoggerValues) error { logger.Info("finished", "method", v.Method, "path", v.URIPath, "remote_ip", v.RemoteIP, "user_agent", v.UserAgent, "protocol", v.Protocol, "status", v.Status, "latency", v.Latency, "content_length", v.ContentLength, // ContentLengthの型はstringで、GETの場合は空文字列 "response_size", v.ResponseSize, ) return nil } func requestLogger() echo.MiddlewareFunc { return middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ LogValuesFunc: writeRequestLog, LogMethod: true, LogURIPath: true, LogRemoteIP: true, LogUserAgent: true, LogProtocol: true, LogStatus: true, LogLatency: true, LogContentLength: true, LogResponseSize: true, }) } func main() { e := echo.New() e.Use(requestLogger()) e.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, "hello") }) e.Start(":8080") }
これで、任意のロガーを通してアクセスログを出力できるようになりました。middleware.RequestLoggerValues で必要な値に対応する middleware.RequestLoggerConfig のフラグを true
にセットしておかないとゼロ値になってしまうので、値がおかしい場合は見直してみましょう。
echo.Context.Logger
ところで echo.Context にはリクエストログとは別に
type Context interface { Logger() echo.Logger }
が用意されていて、アプリケーションで自由に使うことができます。この echo.Logger の実態はデフォルトだと gommon.Logger になっていて echo.Context.SetLogger で変更が可能です。ただし、echo.Logger インターフェイスは
type Logger interface { Output() io.Writer SetOutput(w io.Writer) Prefix() string SetPrefix(p string) Level() log.Lvl SetLevel(v log.Lvl) SetHeader(h string) Print(i ...interface{}) Printf(format string, args ...interface{}) Printj(j log.JSON) Debug(i ...interface{}) Debugf(format string, args ...interface{}) Debugj(j log.JSON) Info(i ...interface{}) ... この後もログレベルごとに3つメソッドが定義されている...
のようにとても大きくて、JSONログが必要なだけでも全部満たす必要があるのは不毛なので、もう少しこれもどうにかならないかなと思っています。