一、简介
开发的服务器代码总会出现未知的错误,那么排错及维护就是服务器开发重要且必不可少的一部分,但是服务器一般是以后台服务的方式运行的,没有控制台,不能直接看到日志,因此我们必须将日志存到磁盘上,设置存储的时间点,请求的接口名等信息。
二、在go
中使用日志模块
-
1、第三方包
-
2、安装依赖包
go get -u github.com/sirupsen/logrus
-
3、基本使用日志
package main import "github.com/sirupsen/logrus" func main() { logrus.WithFields(logrus.Fields{"name": "张三"}).Info("这是日志信息") }
INFO[0000] 这是日志信息 name="张三" Process finished with exit code 0
三、在gin
中使用日志
-
1、初始化日志方法
import ( "fmt" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "net/http" "os" ) var log = logrus.New() func initLogrus() error { // 设置为json格式的日志 log.Formatter = &logrus.JSONFormatter{} file, err := os.OpenFile("./gin_log.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { fmt.Println("创建日志文件/打开日志文件失败") return err } // 设置log默认文件输出 log.Out = file gin.SetMode(gin.ReleaseMode) // gin框架自己记录的日志也会输出 gin.DefaultWriter = log.Out // 设置日志级别 log.Level = logrus.InfoLevel return nil } func init() { err := initLogrus() if err != nil { fmt.Println(err) return } }
-
2、在
gin
中使用日志func main() { router := gin.Default() router.GET("/log", func(c *gin.Context) { // 写入日志信息 log.WithFields(logrus.Fields{ "url": c.Request.RequestURI, "method": c.Request.Method, "IP": c.ClientIP(), }) // 定义一个返回数据的结构体 responseData := struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data"` }{ Code: 1, Message: "请求成功", } c.JSON(http.StatusOK, responseData) }) router.Run(":9000") }
-
3、日志输出
[GIN] 2021/04/28 - 11:13:37 | 200 | 406.441µs | ::1 | GET "/log"
四、对logrus
参数配置
在实际日志中我们希望日志根据时间来切割,不然全部的日志都在一个文件里面,遇到问题也不好排查,设置过期时间,过期后自动从磁盘上删除。
在gin
中有一个东西叫中间件的,不管什么请求都会经过中间件,然后到达控制器,我们在实际的项目中可以将日志输出放到中间件中来一次性全部拦截,在控制器需要的地方根据自己需要再来打印日志
-
1、安装依赖包
go get -u github.com/lestrrat-go/file-rotatelogs go get -u github.com/rifflock/lfshook
-
2、定义中间件
func logMiddleWare() gin.HandlerFunc { var ( logFilePath = "./" //文件存储路径 logFileName = "system.log" ) // 日志文件 fileName := path.Join(logFilePath, logFileName) // 写入文件 file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { fmt.Println("打开/写入文件失败", err) return nil } // 实例化 logger := logrus.New() // 日志级别 logger.SetLevel(logrus.DebugLevel) // 设置输出 logger.Out = file // 设置 rotatelogs,实现文件分割 logWriter, err := rotatelogs.New( // 分割后的文件名称 fileName+".%Y%m%d.log", // 生成软链,指向最新日志文件 rotatelogs.WithLinkName(fileName), // 设置最大保存时间(7天) rotatelogs.WithMaxAge(7*24*time.Hour), //以hour为单位的整数 // 设置日志切割时间间隔(1天) rotatelogs.WithRotationTime(1*time.Hour), ) // hook机制的设置 writerMap := lfshook.WriterMap{ logrus.InfoLevel: logWriter, logrus.FatalLevel: logWriter, logrus.DebugLevel: logWriter, logrus.WarnLevel: logWriter, logrus.ErrorLevel: logWriter, logrus.PanicLevel: logWriter, } //给logrus添加hook logger.AddHook(lfshook.NewHook(writerMap, &logrus.JSONFormatter{ TimestampFormat: "2006-01-02 15:04:05", })) return func(c *gin.Context) { c.Next() //请求方式 method := c.Request.Method //请求路由 reqUrl := c.Request.RequestURI //状态码 statusCode := c.Writer.Status() //请求ip clientIP := c.ClientIP() // 打印日志 logger.WithFields(logrus.Fields{ "status_code": statusCode, "client_ip": clientIP, "req_method": method, "req_uri": reqUrl, }).Info() } }
-
3、在
gin
中使用中间件func main() { router := gin.Default() router.Use(logMiddleWare()) router.GET("/log", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "code": 1, "message": "请求成功", }) }) router.Run(":9000") }
-
4、打印出的日志
{"client_ip":"::1","level":"info","msg":"","req_method":"GET","req_uri":"/log","status_code":200,"time":"2021-04-28 11:41:31"}