⚙️

Zapを使いginのlogging middlewareを実装する

2022/11/30に公開

Zap Logger Middleware

middlewares/logger.go

func Logger(l *zap.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		fmt.Println("starting logger", start)
		c.Next()
		l.Info("incoming request",
			zap.Int("status", c.Writer.Status()),
			zap.Int64("content_length", c.Request.ContentLength),
			zap.String("method", c.Request.Method),
			zap.String("path", c.Request.URL.Path),
			zap.String("query", c.Request.URL.RawQuery),
			zap.String("ip", c.ClientIP()),
			zap.String("user_agent", c.Request.UserAgent()),
			zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
			zap.Duration("elapsed", time.Since(start)),
		)
	}
}

main.go

func main() {
	logger, err := zap.NewProduction()
	if err != nil {
		log.Fatal(err)
	}
	
	defer logger.Sync()
	
	r := gin.New()
	r.Use(middleware.Logger(logger))
	
	r.GET("/ping", func(c *gin.Context) {
		fmt.Println("ping called")
		c.JSON(200, "pong")
	})
	
	if err := r.Run(); err != nil {
		fmt.Println("Failed", err)
	}
}

出力結果は以下の通りです。

curl -s 'localhost:8080/ping?test=testing'
"pong"
starting logger 2022-11-30 08:00:32.694792773 +0900 JST m=+44.982572328

ping called

{"level":"info","ts":1669762832.6951628,"caller":"middlewares/logger.go:47","msg":"incoming request","status":200,"content_length":0,"method":"GET","path":"/ping","query":"test=testing","ip":"127.0.0.1","user_agent":"curl/7.64.1","errors":"","elapsed":0.000345985}

解説

start := time.Now()

は処理にかかった時間を計測するために、リクエストの処理を行う前に時刻を保持する。処理が終了したタイミングでtime.Since()で差分を算出します。loggerミドルウェアーの実行の処理しか計測できないので、実行順番を考慮して.Use()を設定しましょう。

c.Next()

次のginhandlerを実行するために.Next()を呼びます。fmt.Println()stdoutの結果でもご覧になれるように、.GET()で登録されているhandlerが呼ばれています。

以上でzapを使用したgin logger middlewareの実装でした。

参考

Discussion