一,zap库安装
$ go get -u go.uber.org/zap
go: added go.uber.org/multierr v1.11.0
go: added go.uber.org/zap v1.27.0
二,代码
accesslog
package middlewareimport ("bytes""fmt""github.com/gin-gonic/gin""go.uber.org/zap""io""mediabank/global""os""time"
)type responseWriter struct {gin.ResponseWriterb *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) {//请求 headerrequestHeader := c.Request.HeaderrequestHeaderStr:=fmt.Sprintf("%v",requestHeader)fmt.Println("请求头:",requestHeaderStr)//请求体 bodyrequestBody := ""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 = writerbeginTime := time.Now().UnixNano()c.Next()endTime := time.Now().UnixNano()duration:=endTime-beginTime//判断当前日志目录是否存在var curDir = "./logs"if global.IsDirExists(curDir) == false {// 创建多级目录err := os.MkdirAll(curDir, 0755)if err != nil {fmt.Println(err)}}//得到当前日期var curDate = global.FormattedNow("20060102")cfg := zap.NewProductionConfig()cfg.OutputPaths = []string{"./logs/access_"+curDate+".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()//响应 headerresponseHeader := c.Writer.Header()responseHeaderStr := fmt.Sprintf("%v",responseHeader)//响应体大小responseBodySize := c.Writer.Size()//响应体 bodyresponseBody := 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),)}
}
global/functions.go
package globalimport ("github.com/gin-gonic/gin""os""strings""time"
)//格式化当前时间
func FormattedNow(format string) string {// 获取当前时间now := time.Now()// 格式化时间formatted := now.Format(format)return formatted
}//判断目录是否存在
func IsDirExists(path string) bool {_, err := os.Stat(path)if err != nil {if os.IsNotExist(err) {return false}}return true
}//得到所有get参数
func GetAllGetParams(c *gin.Context) (string) {params := c.Request.URL.Query()resStr := ""// 遍历并打印所有参数及其值for key, values := range params {for _, value := range values {resStr = resStr+"key:"+key+",value:"+value+"\n"}}return resStr
}//得到所有post参数
func GetAllPostParams(c *gin.Context) (string) {c.Request.ParseMultipartForm(32 << 20)resStr := ""for k, v := range c.Request.PostForm {resStr = resStr+"key:"+k+",value:"+strings.Join(v, ",")+"\n"}return resStr
}
routes.go 启用日志中间件
package routesimport ("embed""fmt""github.com/gin-gonic/gin""html/template""io/fs""mediabank/controller""mediabank/global""mediabank/middleware""net/http""runtime/debug""time"
)func Routes(embedFs embed.FS) *gin.Engine {router := gin.Default()//全局使用访问日志//router.Use(middleware.AccessLog())//处理找不到路由router.NoRoute(HandleNotFound)router.NoMethod(HandleNotFound)//处理发生异常router.Use(Recover)//1, 加载模板文件tmpl := template.Must(template.New("").ParseFS(embedFs, "templates/**/*.html"))router.SetHTMLTemplate(tmpl)//2, 加载静态文件fp, _ := fs.Sub(embedFs, "static")router.StaticFS("/static", http.FS(fp))//3,加载favicon//static是存放favicon.ico的目录faviconHandler := http.FileServer(http.FS(fp))router.GET("/favicon.ico", func(c *gin.Context) {faviconHandler.ServeHTTP(c.Writer, c.Request)})//media,使用自定义的访问日志中间件media := controller.NewMediaController()mediaGroup := router.Group("/media").Use(middleware.AccessLog()){mediaGroup.GET("/detail", media.Detail)mediaGroup.GET("/list", media.List)mediaGroup.GET("/user", media.User)}return router
}
三,测试效果
{"level":"info","ts":1757388627.6531723,"caller":"middleware/accesslog.go:98","msg":"记录一条日志","time":"2025-09-09 11:30:27","ip":"192.168.219.1","proto":"HTTP/1.1","method":"GET","host":"192.168.219.3:8080","url":"/media/detail","get_params":"","post_params":"","UserAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.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/139.0.0.0 Safari/537.36]]","requestBody":"","responseStatus":200,"responseHeader":"map[Content-Type:[text/html; charset=utf-8]]","responseBodySize":1224,"responseBody":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Gin 模板示例</title>\n <link rel=\"stylesheet\" href=\"/static/css/global.css\">\n <script src=\"/static/js/jquery-3.7.1.min.js\"></script>\n</head>\n<body>\n<h1>欢迎来到 Gin 的世界!</h1>\n<button onclick=\"getName()\">获取当前用户名字</button>\n<script>\n function getName() {\n var paramsData = {\n a:1,\n b:2\n }\n var url = \"/media/user\";\n $.ajax({\n type: 'GET',\n url: url,\n data: paramsData,\n dataType: 'json',\n success: function(data) {\n\n console.log(\"成功\");\n console.log(data);\n if (data.hasOwnProperty('name')) {\n alert('name:'+data.name)\n } else {\n alert('数据获取失败')\n }\n },\n error: function(jqXHR, textStatus, errorThrown) {\n console.log(\"失败\");\n\n console.error('Error: ' + textStatus + ' - ' + errorThrown);\n }\n });\n }\n\n</script>\n</body>\n</html>","duration":257}