[系列] Gin框架 - 使用 Logrus 进行日志记录

概述

上篇文章分享了 Gin 框架的路由配置,这篇文章分享日志记录。git

查了不少资料,Go 的日志记录用的最多的仍是 github.com/sirupsen/logrusgithub

Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger.golang

Gin 框架的日志默认只会在控制台输出,我们利用 Logrus 封装一个中间件,将日志记录到文件中。数组

这篇文章就是学习和使用 Logrusbash

日志格式

好比,咱们约定日志格式为 Text,包含字段以下:框架

请求时间日志级别状态码执行时间请求IP请求方式请求路由运维

接下来,我们利用 Logrus 实现它。函数

Logrus 使用

dep 方式进行安装。性能

Gopkg.toml 文件新增:学习

[[constraint]]
  name = "github.com/sirupsen/logrus"
  version = "1.4.2"
复制代码

在项目中导入:

import "github.com/sirupsen/logrus"
复制代码

在项目命令行执行:

dep ensure
复制代码

这时,在 vendor/github.com/ 目录中就会看到 sirupsen 目录。

准备上手用了,上手以前我们先规划一下,将这个功能设置成一个中间件,好比:logger.go

日志能够记录到 File 中,定义一个 LoggerToFile 方法。

日志能够记录到 MongoDB 中,定义一个 LoggerToMongo 方法。

日志能够记录到 ES 中,定义一个 LoggerToES 方法。

日志能够记录到 MQ 中,定义一个 LoggerToMQ 方法。

...

此次我们先实现记录到文件, 实现 LoggerToFile 方法,其余的能够根据本身的需求进行实现。

这个 logger 中间件,建立好了,能够任意在其余项目中进行迁移使用。

废话很少说,直接看代码。

package middleware

import (
	"fmt"
	"ginDemo/config"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"os"
	"path"
	"time"
)

// 日志记录到文件
func LoggerToFile() gin.HandlerFunc {

	logFilePath := config.Log_FILE_PATH
	logFileName := 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()

		// 请求IP
		clientIP := c.ClientIP()

		// 日志格式
		logger.Infof("| %3d | %13v | %15s | %s | %s |",
			statusCode,
			latencyTime,
			clientIP,
			reqMethod,
			reqUri,
		)
	}
}

// 日志记录到 MongoDB
func LoggerToMongo() gin.HandlerFunc {
	return func(c *gin.Context) {
		
	}
}

// 日志记录到 ES
func LoggerToES() gin.HandlerFunc {
	return func(c *gin.Context) {

	}
}

// 日志记录到 MQ
func 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"}
复制代码

说明一下:timemsglevel 这些参数是 logrus 自动加上的。

logrus 支持输出文件名和行号吗?

不支持,做者的回复是太耗性能。

不过网上也有人经过 Hook 的方式实现了,选择在生产环境使用的时候,记得作性能测试。

logrus 支持日志分割吗?

不支持,但有办法实现它。

一、能够利用 Linux logrotate,统一由运维进行处理。

二、能够利用 file-rotatelogs 实现。

须要导入包:

github.com/lestrrat-go/file-rotatelogs

github.com/rifflock/lfshook

奉上完整代码:

package middleware

import (
	"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_PATH
	logFileName := 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)

	// 设置 rotatelogs
	logWriter, 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",
	})

	// 新增 Hook
	logger.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()

		// 请求IP
		clientIP := c.ClientIP()

		// 日志格式
		logger.WithFields(logrus.Fields{
			"status_code"  : statusCode,
			"latency_time" : latencyTime,
			"client_ip"    : clientIP,
			"req_method"   : reqMethod,
			"req_uri"      : reqUri,
		}).Info()
	}
}

// 日志记录到 MongoDB
func LoggerToMongo() gin.HandlerFunc {
	return func(c *gin.Context) {
		
	}
}

// 日志记录到 ES
func LoggerToES() gin.HandlerFunc {
	return func(c *gin.Context) {

	}
}

// 日志记录到 MQ
func LoggerToMQ() gin.HandlerFunc {
	return func(c *gin.Context) {

	}
}
复制代码

这时会新生成一个文件 system.log.20190717.log,日志内容与上面的格式一致。

最后,logrus 可扩展的 Hook 不少,你们能够去网上查找。

有些读者建议,手机上看代码不方便,建议更新到 GitHub 上。

现已更新,地址以下:

github.com/xinliangnot…

推荐阅读

Gin 框架

基础篇

本文欢迎转发,转发请注明做者和出处,谢谢!

相关文章
相关标签/搜索