gin: 用zap记录访问日志

一,安装zap

$ go get -u go.uber.org/zap
go: downloading go.uber.org/zap v1.27.0
go: downloading go.uber.org/multierr v1.10.0
go: downloading go.uber.org/multierr v1.11.0
go: added go.uber.org/multierr v1.11.0
go: added go.uber.org/zap v1.27.0

二,测试一个例子:

//得到列表
func (ic *ImageController) List(c *gin.Context) {

	//logger := zap.NewExample()

	cfg := zap.NewProductionConfig()
	cfg.OutputPaths = []string{
		"/data/gologs/logs/access.log",
		//"stderr",
		"stdout",
	}
	logger,err := cfg.Build()
	if err!=nil {
		fmt.Println("日志启动出错:",err.Error())
		return
	}

	defer logger.Sync()

	timeStr := time.Now().Format("2006-01-02 15:04:05")
	logger.Info("记录一条日志",
		zap.String("time", timeStr),
		zap.String("url", c.Request.URL.String()),
		)

	c.JSON(http.StatusOK, gin.H{
		"message": "image list",
	})
}

输出例子:

{"level":"info","ts":1738244531.3267372,"caller":"controller/ImageController.go:54","msg":"记录一条日志","time":"2025-01-30 21:42:11","url":"/image/list?a=1&bc=2"}

三,记录访问日志

middleware/accesslog.go

package middleware

import (
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"imagebank/global"
	"io"
	"time"
)

type responseWriter struct {
	gin.ResponseWriter
	b *bytes.Buffer
}

//重写 Write([]byte) (int, error) 方法
func(w responseWriter)Write(b []byte)(int, error) {
    //向一个bytes.buffer中写一份数据来为获取body使用
    w.b.Write(b)
    //完成gin.Context.Writer.Write()原有功能
    return w.ResponseWriter.Write(b)
}

func AccessLog() gin.HandlerFunc {
	return func(c *gin.Context) {
		//请求 header
		requestHeader := c.Request.Header
		requestHeaderStr:=fmt.Sprintf("%v",requestHeader)
        fmt.Println("请求头:",requestHeaderStr)


		//请求体 body
		requestBody := ""
		b, err := c.GetRawData()
		if err != nil {
			requestBody = "failed to get request body"
		} else {
			requestBody = string(b)
		}
		c.Request.Body = io.NopCloser(bytes.NewBuffer(b))
		fmt.Println("请求体:",requestBody)



		writer := responseWriter{
			c.Writer,
			bytes.NewBuffer([]byte{}),
		}
		c.Writer = writer

		beginTime := time.Now().UnixNano()
		c.Next()
		endTime := time.Now().UnixNano()
		duration:=endTime-beginTime

		cfg := zap.NewProductionConfig()
		cfg.OutputPaths = []string{
			"/data/gologs/logs/access.log",
			//"stderr",
			"stdout",
		}
		logger,err := cfg.Build()
		if err!=nil {
			fmt.Println("日志启动出错:",err.Error())
			return
		}

		defer logger.Sync()

		timeStr := time.Now().Format("2006-01-02 15:04:05")

		//响应状态码
		responseStatus := c.Writer.Status()
		//响应 header
		responseHeader := c.Writer.Header()
		responseHeaderStr := fmt.Sprintf("%v",responseHeader)
		//响应体大小
		responseBodySize := c.Writer.Size()
		//响应体 body
		responseBody := writer.b.String()

		logger.Info("记录一条日志",
			zap.String("time", timeStr),
			zap.String("ip", c.ClientIP()),
			zap.String("proto", c.Request.Proto),
			zap.String("method", c.Request.Method),
			zap.String("host", c.Request.Host),
			zap.String("url", c.Request.URL.String()),
			zap.String("get_params", global.GetAllGetParams(c)),
			zap.String("post_params", global.GetAllPostParams(c)),
			zap.String("UserAgent", c.Request.UserAgent()),
			//请求头,请求体
			zap.String("requestHeader", requestHeaderStr),
			zap.String("requestBody", requestBody),
			//响应头、响应体
			zap.Int("responseStatus", responseStatus),
			zap.String("responseHeader", responseHeaderStr),
			zap.Int("responseBodySize", responseBodySize),
			zap.String("responseBody", responseBody),
			//时长,微秒
			zap.Int64("duration", duration/1000),
		)
	}
}

routes/routes.go

package routes

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"imagebank/controller"
	"imagebank/global"
	"imagebank/middleware"
	"runtime/debug"
	"time"
)

func Routes() *gin.Engine {
	router := gin.Default()
	//router.Use(middleware.AccessLog())


	//处理找不到路由
	router.NoRoute(HandleNotFound)
	router.NoMethod(HandleNotFound)
    //处理发生异常
	router.Use(Recover)

	//image
	image := controller.NewImageController()
	imageGroup := router.Group("/image").Use(middleware.AccessLog())
	{
		imageGroup.GET("/detail", image.Detail)
		imageGroup.GET("/list", image.List)
		imageGroup.POST("/add", image.Add)
	}

	//访问根目录
	router.GET("/", image.List)

	//访问根目录
	router.GET("/favicon.ico", func(c *gin.Context) {
		c.String(404, "not exist")
	})

	return router
}

可以应用到全局,
也可以应用到组

四,测试效果:

{"level":"info","ts":1738288864.7150245,
 "caller":"middleware/accesslog.go:122",
 "msg":"记录一条日志","time":"2025-01-31 10:01:04",
 "ip":"192.168.219.1","proto":"HTTP/1.1",
 "method":"GET","host":"192.168.219.3:8080",
 "url":"/image/list?a=1&bc=2","get_params":"key:a,value:1\nkey:bc,value:2\n",
 "post_params":"",
 "UserAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
 "requestHeader":"map[Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7] Accept-Encoding:[gzip, deflate] Accept-Language:[zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6] Cache-Control:[max-age=0] Connection:[keep-alive] Cookie:[PHPSID=e239cbb4d5ddd941f501d2d3a9cca3d5] Upgrade-Insecure-Requests:[1] User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36]]",
 "requestBody":"","responseStatus":200,
 "responseHeader":"map[Content-Type:[application/json; charset=utf-8]]",
 "responseBodySize":24,"responseBody":"{\"message\":\"image list\"}","duration":42}

 

posted @ 2025-01-31 10:55  刘宏缔的架构森林  阅读(139)  评论(0)    收藏  举报