概述

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

查了很多资料,Go 的日志记录用的最多的还是 github.com/sirupsen/logrus。

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

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

这篇文章就是学习和使用 Logrus 。

日志格式

比如,我们约定日志格式为 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"}

说明一下:time、 msg、 level 这些参数是 logrus 自动加上的。

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

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

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

logrus 支持日志分割吗?

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

1、可以利用 Linuxlogrotate,统一由运维进行处理。

2、可以利用 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 上。

现已更新,地址如下:

https://github.com/xinliangnote/Go

Gin框架 - 使用 Logrus 进行日志记录的更多相关文章

  1. Gin 框架 - 使用 logrus 进行日志记录

    目录 概述 日志格式 Logrus 使用 推荐阅读 概述 上篇文章分享了 Gin 框架的路由配置,这篇文章分享日志记录. 查了很多资料,Go 的日志记录用的最多的还是 github.com/sirup ...

  2. [系列] Gin框架 - 数据绑定和验证

    目录 概述 推荐阅读 概述 上篇文章分享了 Gin 框架使用 Logrus 进行日志记录,这篇文章分享 Gin 框架的数据绑定与验证. 有读者咨询我一个问题,如何让框架的运行日志不输出控制台? 解决方 ...

  3. Gin框架 - 数据绑定和验证

    概述 上篇文章分享了 Gin 框架使用 Logrus 进行日志记录,这篇文章分享 Gin 框架的数据绑定与验证. 有读者咨询我一个问题,如何让框架的运行日志不输出控制台? 解决方案: engine : ...

  4. Gin框架介绍与使用

    Gin // 初识 Gin框架 //下载(可能会下载不全.缺什么get什么即可) //go get -u -v github.com/gin-gonic/gin package main import ...

  5. 使用zap接收gin框架默认的日志并配置日志归档

    目录 使用zap接收gin框架默认的日志并配置日志归档 gin默认的中间件 基于zap的中间件 在gin项目中使用zap 使用zap接收gin框架默认的日志并配置日志归档 本文介绍了在基于gin框架开 ...

  6. Gin 框架 - 安装和路由配置

    目录 概述 Gin 安装 路由配置 推荐阅读 概述 看下 Gin 框架的官方介绍: Gin 是一个用 Go (Golang) 编写的 web 框架. 它是一个类似于 martini 但拥有更好性能的 ...

  7. Gin框架 - 自定义错误处理

    目录 概述 错误处理 自定义错误处理 panic 和 recover 推荐阅读 概述 很多读者在后台向我要 Gin 框架实战系列的 Demo 源码,在这里再说明一下,源码我都更新到 GitHub 上, ...

  8. Hibernate使用Log4j日志记录(使用properties文件)

    我们知道,Log4j和Logback框架可用于支持日志记录hibernate,使用log4j有两种执行日志记录的方法: 通过log4j.xml文件(或) 通过log4j.properties文件 在这 ...

  9. 基于.NetCore3.1系列 —— 日志记录之初识Serilog

    一.前言 对内置日志系统的整体实现进行了介绍之后,可以通过使用内置记录器来实现日志的输出路径.而在实际项目开发中,使用第三方日志框架(如: Log4Net.NLog.Loggr.Serilog.Sen ...

随机推荐

  1. Express中app.use()用法 详解

    app.use(path,callback)中的callback既可以是router对象又可以是函数 app.get(path,callback)中的callback只能是函数 当一个路由有好多个子路 ...

  2. tomcat9上传文件失败错误

    项目上线正常运行一段时间后,有一天突然所有的附件上传都出现了错误,查找项目本身的日志系统也一致没有跟踪到错误.经过几番折腾,在tomcat9-stdout.log日志中发现如下异常: ERROR or ...

  3. 聊一聊 bootstrap 的轮播图插件

    今天做工作的时候,轻车熟路的做完,又用到了bootstrap的轮播图,觉得有必要安利一下这个插件,如果你需要的轮播图.功能不需要太炫酷,那么bootstrap的插件是你的首要选择. 使用方式 引入js ...

  4. 【Ajax】Ajax入门总结

    目录 Ajax( Asynchronous JavaScript and XML ) 向服务器发送请求 服务器回应请求 本文内容总结自 w3cschool: https://www.w3school. ...

  5. layui 使用随记

    layui confir使用 不显示右上角关闭按钮 针对提示框内按钮指定操作 layer.confirm("这里填写提示信息", {closeBtn:0,icon: 0, titl ...

  6. go-面向对象编程(下)

    面向对象编程思想-抽象 抽象的介绍 我们在前面去定义一个结构体时候,实际上就是把一类事物的共有的 属性( 字段)和 行为( 方法)提取 出来,形成一个 物理模型(结构体).这种研究问题的方法称为抽象 ...

  7. go开发注意事项和dos的一些操作

    不需要加分号 写法 go编译器一行一行编译,所以多条语句不能写在同一行,否则会报错 go语言定义的变量或者import的包如果没有使用到,代码不能通过编译 func main() { ... } 只能 ...

  8. 帝国CMS标签【操作类型】说明详解

    看标签的参数时候,一般最后一个参数是操作类型说明,可是后面写的是:"操作类型说明 具体看操作类型说明", 这个操作类型说明在什么地方看啊 操作类型 说明 操作类型 说明 0 各栏目 ...

  9. DataGridView中实现点击单元格Cell动态添加自定义控件

    场景 鼠标点击DataGridView的某个单元格时,此单元格添加一个自定义的控件,这里以 添加下拉框为例 效果 注: 博客主页: https://blog.csdn.net/badao_liuman ...

  10. HTML5新标签与特性---多媒体

    多媒体标签 embed:标签定义嵌入的内容 audio:播放音频 video:播放视频 多媒体 embed(会使用) embed可以用来插入各种多媒体,格式可以是 Midi.Wav.AIFF.AU.M ...