hincky的主页 hincky的主页
  • 学习笔记

    • Vue笔记
    • Vuepress
    • nginx
  • 语言类

    • java
    • go
    • python
    • 设计模式
  • 框架类

    • Spring
    • Spring Security
    • Mybatis
  • 容器技术

    • docker
    • k8s
    • helm
    • prometheus
    • grafana
    • jenkins
  • 命令集合

    • linux命令
    • docker命令
    • git命令
    • vim命令
    • k8s命令
  • 数据库

    • sql
    • mysql
  • 协议

    • 网络模型
    • http/1.1
    • WebSocket
    • http/2
    • TLS/SSL
    • tcp
    • IP
    • tcpdump抓包命令
    • wireshark抓包工具
  • 通用

    • Git
  • 技术分享

    • git push/pull总是超时怎么办
    • idea debug技巧
    • postman使用
    • 问题总结
    • idea使用技巧
  • Oauth2

    • Oauth2原理
  • 项目列表

    • redis项目
    • 微服务项目
  • 分类
  • 标签
  • 归档
  • 随笔
GitHub (opens new window)

Hincky

当有趣的人,做想做的事
  • 学习笔记

    • Vue笔记
    • Vuepress
    • nginx
  • 语言类

    • java
    • go
    • python
    • 设计模式
  • 框架类

    • Spring
    • Spring Security
    • Mybatis
  • 容器技术

    • docker
    • k8s
    • helm
    • prometheus
    • grafana
    • jenkins
  • 命令集合

    • linux命令
    • docker命令
    • git命令
    • vim命令
    • k8s命令
  • 数据库

    • sql
    • mysql
  • 协议

    • 网络模型
    • http/1.1
    • WebSocket
    • http/2
    • TLS/SSL
    • tcp
    • IP
    • tcpdump抓包命令
    • wireshark抓包工具
  • 通用

    • Git
  • 技术分享

    • git push/pull总是超时怎么办
    • idea debug技巧
    • postman使用
    • 问题总结
    • idea使用技巧
  • Oauth2

    • Oauth2原理
  • 项目列表

    • redis项目
    • 微服务项目
  • 分类
  • 标签
  • 归档
  • 随笔
GitHub (opens new window)
  • java

  • python

  • Spring

  • SpringMVC

  • SpringSecurity

  • Mybatis

  • 设计模式

  • Go

    • 基础语法

    • Gin框架

      • 框架安装
        • 概述
        • Gin 安装
      • 路由配置
        • 概述
        • 路由配置
        • 源码
      • 日志记录
        • 概述
        • 日志格式
        • Logrus 使用
        • 源码
      • 数据绑定和验证
        • 概述
        • 源码
      • 自定义错误处理
        • 概述
        • 错误处理
        • 自定义错误处理
        • panic 和 recover
        • 源码
      • 统一定义 API 错误码
        • 改之前
        • 改之后
        • errno 包源码
        • 错误码规则
          • 错误码为 5 位数
    • Go gRPC

    • go-gin-api [文档]

    • go基本介绍
      • 安装go
      • hello world
      • 变量类型
      • 变量和声明
        • 定义和赋值分开
        • 定义和赋值合并
        • 最简单的写法
        • 最简写法结合函数
        • 多个变量同时赋值
        • 变量总结
      • if
      • 导入包
      • 运行go代码
      • 函数声明
        • 返回值个数
        • 参数接收
        • 两个参数都接收
        • 只接收其中一个参数
    • Go结构体
      • 定义结构体
      • 声明和初始化
        • 结构体字段
        • 结构体初始化
        • 函数参数传递方式
        • 镜像复制
        • 指针
      • 结构体上的函数
      • 构造器
        • new
    • Go相关项目推荐
      • 收录golang的一些好的开源项目
  • 后端
  • Go
  • Gin框架
hincky
2022-11-21
目录

日志记录

# 概述

上篇文章分享了 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"
1
2
3

在项目中导入:

import "github.com/sirupsen/logrus"
1

在项目命令行执行:

dep ensure
1

这时,在 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) {

	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

日志中间件写好了,怎么调用呢?

只需在 main.go 中新增:

engine := gin.Default() //在这行后新增
engine.Use(middleware.LoggerToFile())
1
2

运行一下,看看日志:

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 |"
1
2

这个 time="2019-07-17T22:10:45+08:00" ,这个时间格式不是咱们想要的,怎么办?

时间需要格式化一下,修改 logger.SetFormatter

//设置日志格式
logger.SetFormatter(&logrus.TextFormatter{
	TimestampFormat:"2006-01-02 15:04:05",
})
1
2
3
4

执行以下,再看日志:

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 |"
1
2

时间变得正常了。

我不喜欢文本格式,喜欢 JSON 格式,怎么办?

//设置日志格式
logger.SetFormatter(&logrus.JSONFormatter{
	TimestampFormat:"2006-01-02 15:04:05",
})
1
2
3
4

执行以下,再看日志:

{"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"}
1
2

msg 信息太多,不方便看,怎么办?

// 日志格式
logger.WithFields(logrus.Fields{
	"status_code"  : statusCode,
	"latency_time" : latencyTime,
	"client_ip"    : clientIP,
	"req_method"   : reqMethod,
	"req_uri"      : reqUri,
}).Info()
1
2
3
4
5
6
7
8

执行以下,再看日志:

{"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"}
1
2

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

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

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

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

logrus 支持日志分割吗?

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

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

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) {

	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

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

最后,logrus 可扩展的 Hook 很多,大家可以去网上查找。

# 源码

下载源码后,请先执行 dep ensure 下载依赖包!

查看源码 (opens new window)

编辑 (opens new window)
路由配置
数据绑定和验证

← 路由配置 数据绑定和验证→

最近更新
01
人生前期重要的能力
05-17
02
防火墙命令
04-11
03
docker-compose部署mysql主从集群
03-22
更多文章>
Theme by Vdoing | Copyright © 2022-2023 Hincky | MIT License | 粤ICP备2022120427号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式