导入
import "github.com/sirupsen/logrus"
准备上手用了,上手之前咱们先规划一下,将这个功能设置成一个中间件,比如:logger.go。
日志可以记录到 File 中,定义一个 LoggerToFile 方法。
日志可以记录到 MongoDB 中,定义一个 LoggerToMongo 方法。
日志可以记录到 ES 中,定义一个 LoggerToES 方法。
日志可以记录到 MQ 中,定义一个 LoggerToMQ 方法。
…
这次咱们先实现记录到文件, 实现 LoggerToFile 方法,其他的可以根据自己的需求进行实现。
这个 logger 中间件,创建好了,可以任意在其他项目中进行迁移使用。
废话不多说,直接看代码。
package middlewareimport ("fmt""ginDemo/config""github.com/gin-gonic/gin""github.com/sirupsen/logrus""os""path""time")// 日志记录到文件func LoggerToFile() gin.HandlerFunc {logFilePath := config.Log_FILE_PATHlogFileName := config.LOG_FILE_NAME//日志文件fileName := path.Join(logFilePath, logFileName)//写入文件src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)if err != nil {fmt.Println("err", err)}//实例化logger := logrus.New()//设置输出logger.Out = src//设置日志级别logger.SetLevel(logrus.DebugLevel)//设置日志格式logger.SetFormatter(&logrus.TextFormatter{})return func(c *gin.Context) {// 开始时间startTime := time.Now()// 处理请求c.Next()// 结束时间endTime := time.Now()// 执行时间latencyTime := endTime.Sub(startTime)// 请求方式reqMethod := c.Request.Method// 请求路由reqUri := c.Request.RequestURI// 状态码statusCode := c.Writer.Status()// 请求IPclientIP := c.ClientIP()// 日志格式logger.Infof("| %3d | %13v | %15s | %s | %s |",statusCode,latencyTime,clientIP,reqMethod,reqUri,)}}// 日志记录到 MongoDBfunc LoggerToMongo() gin.HandlerFunc {return func(c *gin.Context) {}}// 日志记录到 ESfunc LoggerToES() gin.HandlerFunc {return func(c *gin.Context) {}}// 日志记录到 MQfunc LoggerToMQ() gin.HandlerFunc {return func(c *gin.Context) {}}
日志中间件写好了,怎么调用呢?
只需在 main.go 中新增:
engine := gin.Default() //在这行后新增engine.Use(middleware.LoggerToFile())
运行一下,看看日志:
time="2019-07-17T22:10:45+08:00" level=info msg="| 200 | 27.698µs | ::1 | GET | /v1/product/add?name=a&price=10 |"time="2019-07-17T22:10:46+08:00" level=info msg="| 200 | 27.239µs | ::1 | GET | /v1/product/add?name=a&price=10 |"
这个 time="2019-07-17T22:10:45+08:00" ,这个时间格式不是咱们想要的,怎么办?
时间需要格式化一下,修改 logger.SetFormatter
//设置日志格式logger.SetFormatter(&logrus.TextFormatter{TimestampFormat:"2006-01-02 15:04:05",})
执行以下,再看日志:
time="2019-07-17 22:15:57" level=info msg="| 200 | 185.027µs | ::1 | GET | /v1/product/add?name=a&price=10 |"time="2019-07-17 22:15:58" level=info msg="| 200 | 56.989µs | ::1 | GET | /v1/product/add?name=a&price=10 |"
时间变得正常了。
我不喜欢文本格式,喜欢 JSON 格式,怎么办?
//设置日志格式logger.SetFormatter(&logrus.JSONFormatter{TimestampFormat:"2006-01-02 15:04:05",})
执行以下,再看日志:
{"level":"info","msg":"| 200 | 24.78µs | ::1 | GET | /v1/product/add?name=a\u0026price=10 |","time":"2019-07-17 22:23:55"}{"level":"info","msg":"| 200 | 26.946µs | ::1 | GET | /v1/product/add?name=a\u0026price=10 |","time":"2019-07-17 22:23:56"}
msg 信息太多,不方便看,怎么办?
// 日志格式logger.WithFields(logrus.Fields{"status_code" : statusCode,"latency_time" : latencyTime,"client_ip" : clientIP,"req_method" : reqMethod,"req_uri" : reqUri,}).Info()
执行以下,再看日志:
{"client_ip":"::1","latency_time":26681,"level":"info","msg":"","req_method":"GET","req_uri":"/v1/product/add?name=a\u0026price=10","status_code":200,"time":"2019-07-17 22:37:54"}{"client_ip":"::1","latency_time":24315,"level":"info","msg":"","req_method":"GET","req_uri":"/v1/product/add?name=a\u0026price=10","status_code":200,"time":"2019-07-17 22:37:55"}
说明一下:time、msg、level 这些参数是 logrus 自动加上的。
logrus 支持输出文件名和行号吗?
不支持,作者的回复是太耗性能。
不过网上也有人通过 Hook 的方式实现了,选择在生产环境使用的时候,记得做性能测试。
logrus 支持日志分割吗?
不支持,但有办法实现它。
1、可以利用 Linux logrotate,统一由运维进行处理。
2、可以利用 file-rotatelogs 实现。
需要导入包:github.com/lestrrat-go/file-rotatelogsgithub.com/rifflock/lfshook
奉上完整代码:
package middlewareimport ("fmt""ginDemo/config""github.com/gin-gonic/gin"rotatelogs "github.com/lestrrat-go/file-rotatelogs""github.com/rifflock/lfshook""github.com/sirupsen/logrus""os""path""time")// 日志记录到文件func LoggerToFile() gin.HandlerFunc {logFilePath := config.Log_FILE_PATHlogFileName := config.LOG_FILE_NAME// 日志文件fileName := path.Join(logFilePath, logFileName)// 写入文件src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)if err != nil {fmt.Println("err", err)}// 实例化logger := logrus.New()// 设置输出logger.Out = src// 设置日志级别logger.SetLevel(logrus.DebugLevel)// 设置 rotatelogslogWriter, err := rotatelogs.New(// 分割后的文件名称fileName + ".%Y%m%d.log",// 生成软链,指向最新日志文件rotatelogs.WithLinkName(fileName),// 设置最大保存时间(7天)rotatelogs.WithMaxAge(7*24*time.Hour),// 设置日志切割时间间隔(1天)rotatelogs.WithRotationTime(24*time.Hour),)writeMap := lfshook.WriterMap{logrus.InfoLevel: logWriter,logrus.FatalLevel: logWriter,logrus.DebugLevel: logWriter,logrus.WarnLevel: logWriter,logrus.ErrorLevel: logWriter,logrus.PanicLevel: logWriter,}lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{TimestampFormat:"2006-01-02 15:04:05",})// 新增 Hooklogger.AddHook(lfHook)return func(c *gin.Context) {// 开始时间startTime := time.Now()// 处理请求c.Next()// 结束时间endTime := time.Now()// 执行时间latencyTime := endTime.Sub(startTime)// 请求方式reqMethod := c.Request.Method// 请求路由reqUri := c.Request.RequestURI// 状态码statusCode := c.Writer.Status()// 请求IPclientIP := c.ClientIP()// 日志格式logger.WithFields(logrus.Fields{"status_code" : statusCode,"latency_time" : latencyTime,"client_ip" : clientIP,"req_method" : reqMethod,"req_uri" : reqUri,}).Info()}}// 日志记录到 MongoDBfunc LoggerToMongo() gin.HandlerFunc {return func(c *gin.Context) {}}// 日志记录到 ESfunc LoggerToES() gin.HandlerFunc {return func(c *gin.Context) {}}// 日志记录到 MQfunc LoggerToMQ() gin.HandlerFunc {return func(c *gin.Context) {}}
