Go - Echoでzapを利用してフォーマットされたログを出力
はじめに
echo.Context でcontextでデータを伝搬させる方法をご紹介しました。
echo.Contextの仕組みを利用して、zapでフォーマット化されたログを出力する方法を紹介します。
Echoでのログ出力
echo.Conetxt組み込みのloggerを利用
非常にシンプルなログ出力の方法です。
func TestMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Logger().Info("middleware TestMiddleware")
c.Logger().Error("middleware TestMiddleware ")
return next(c)
}
}
e := echo.New()
e.Logger.SetLevel(log.INFO)
e.Use(middleware.TestMiddleware)
下記のようなログが出力されます。
go_air | {"time":"2024-12-17T13:17:50.353768922Z","level":"INFO","prefix":"echo","file":"logger.go","line":"58","message":"middleware TestMiddleware"}
go_air | {"time":"2024-12-17T13:17:50.354068922Z","level":"ERROR","prefix":"echo","file":"logger.go","line":"59","message":"middleware TestMiddleware "}
echoのmiddleware.loggerを利用
echoのmiddleware.logger()を利用します。
e := echo.New()
e.Use(middleware.Logger())
全リクエストの前後(自動でログを記録)でログを出力します。
下記のようなHTTP リクエストのログを記録してくれます。
{"time":"2017-01-12T08:58:07.372015644-08:00","remote_ip":"::1","host":"localhost:1323","method":"GET","uri":"/","status":200,"error":"","latency":14743,"latency_human":"14.743µs","bytes_in":0,"bytes_out":2}
middleware.loggerでformatを利用
configで設定を変更出来ます。
e := echo.New()
e.Use(middleware.LoggerWithConfig(echomiddleware.LoggerConfig{
Format: "method=${method}, uri=${uri}, status=${status}\n",
}))
formatを変更することができます。下記の形式でログ出力されます。
method=GET, uri=/, status=200
zapとは
uberが開発したパッケージ。
カスタマイズ性と処理速度に優れた、ロギングパッケージです。
zapを利用してログ出力
package middleware
import (
"layered/logger"
"go.uber.org/zap"
)
func TestMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
zapLogger, _ := zap.NewProduction()
// シンプルなログ出力
zapLogger.Info("This is an info message")
zapLogger.Error("This is an error message")
return next(c)
}
}
e := echo.New()
e.Use(middleware.TestMiddleware)
フォーマット化されたloggerでログを統一 / contextにloggerを渡して任意なタイミングでログを出力
loggerの設定
contextに対して、zapで作成したloggerを渡します。
context経由でloggerを取り出せるようにします。
package logger
import (
"context"
"go.uber.org/zap"
)
// Context のキー
type contextKey struct{}
// グローバルロガー
var TestLogger *zap.Logger
// ロガーを初期化
func InitLogger() {
cfg := zap.NewProductionConfig()
cfg.Encoding = "json" // JSON フォーマット
cfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
cfg.OutputPaths = []string{"stdout"}
var err error
TestLogger, err = cfg.Build()
if err != nil {
panic(err)
}
}
// Context にロガーをセットする
func SetLoggerToContext(ctx context.Context, logger *zap.Logger) context.Context {
var lKey ContextLoggerKey
return context.WithValue(ctx, lKey, logger)
}
// Context からロガーを取得する
func GetLoggerFromContext(ctx context.Context) *zap.Logger {
var lKey ContextLoggerKey
if logger, ok := ctx.Value(lKey).(*zap.Logger); ok {
return logger
}
return TestLogger // デフォルトのロガーを返す
}
middleware
func TestMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
req := c.Request()
res := c.Response()
id := req.Header.Get(echo.HeaderXRequestID)
if id == "" {
id = res.Header().Get(echo.HeaderXRequestID)
}
// リクエストごとに `request_id` を追加したロガーを作成
reqLogger := logger.TestLogger.With(
zap.String("method", req.Method),
zap.String("uri", req.RequestURI),
zap.String("request_id", id),
// zap.String("request_id", req.Header.Get(echo.HeaderXRequestID)),
)
// `context` にロガーをセット
ctx := logger.SetLoggerToContext(req.Context(), reqLogger)
c.SetRequest(req.WithContext(ctx))
reqLogger.Info("reqLogger by middleware")
return next(c)
}
}
controller
package controller
import (
"layered/logger"
"github.com/labstack/echo/v4"
)
func ZapLogger() echo.HandlerFunc {
return func(c echo.Context) error {
reqLogger := logger.GetLoggerFromContext(c.Request().Context())
// ログを出力
reqLogger.Info("reqLogger by controller")
return c.JSON(http.StatusOK)
}
}
main.go
e := echo.New()
logger.InitLogger()
e.Use(echomiddleware.RequestID())
e.Use(middleware.TestMiddleware)
下記のようなログが出力されます。
TestMiddlewareで設定したzapのフォーマットに従って、middleware・controllerでも出力されています。また、request_idも一連の処理で同一のものが付与されたログが出力されます。これによって一連の処理を紐づけることができます。
{"level":"info","ts":1739802523.572062,"caller":"middleware/logger.go:37","msg":"reqLogger by middleware","method":"GET","uri":"/","request_id":"XByMYCqgrHvMQNiNfoOEarPqpnxTyPmd"}
{"level":"info","ts":1739802523.5727336,"caller":"controller/zaplogger.go:1248","msg":"reqLogger by controller","method":"GET","uri":"/","request_id":"XByMYCqgrHvMQNiNfoOEarPqpnxTyPmd"}
まとめ
contextにフォーマット化されたlogger(ログ出力のメソッド)を渡すことで、一連の処理に同一の値を付与したり、同じフォーマットでログを出力できるようになります。
Discussion