Skip to content

Commit 52c68da

Browse files
committed
Add Doc : [go-gin-api] 路由中间件 - 日志记录
1 parent 158cbcd commit 52c68da

File tree

2 files changed

+206
-0
lines changed

2 files changed

+206
-0
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
## 概述
2+
3+
首先同步下项目概况:
4+
5+
![](https://github.com/xinliangnote/Go/blob/master/03-go-gin-api%20%5B文档%5D/images/3_api_1.png)
6+
7+
上篇文章分享了,规划项目目录和参数验证,其中参数验证使用的是 validator.v8 版本,现已更新到 validator.v9 版本,最新代码查看 github 即可。
8+
9+
这篇文章咱们分享:路由中间件 - 日志记录。
10+
11+
日志是特别重要的一个东西,方便我们对问题进行排查,这篇文章我们实现将日志记录到文本文件中。
12+
13+
这是我规划的,需要记录的参数:
14+
15+
```
16+
- request 请求数据
17+
- request_time
18+
- request_method
19+
- request_uri
20+
- request_proto
21+
- request_ua
22+
- request_referer
23+
- request_post_data
24+
- request_client_ip
25+
26+
- response 返回数据
27+
- response_time
28+
- response_code
29+
- response_msg
30+
- response_data
31+
32+
- cost_time 花费时间
33+
```
34+
Gin 框架中自带 Logger 中间件,我们了解下框架中自带的 Logger 中间件是否满足我们的需求?
35+
36+
## gin.Logger()
37+
38+
我们先使用 gin.Logger() 看看效果。
39+
40+
在 route.go SetupRouter 方法中增加代码:
41+
42+
```
43+
engine.Use(gin.Logger())
44+
```
45+
46+
运行后多请求几次,日志输出在命令行中:
47+
48+
```
49+
[GIN] 2019/08/30 - 21:24:16 | 200 | 178.072µs | ::1 | GET /ping
50+
[GIN] 2019/08/30 - 21:24:27 | 200 | 367.997µs | ::1 | POST /product
51+
[GIN] 2019/08/30 - 21:24:28 | 200 | 2.521592ms | ::1 | POST /product
52+
```
53+
54+
先解决第一个问题,怎么将日志输出到文本中?
55+
56+
在 route.go SetupRouter 方法中增加代码:
57+
58+
```
59+
f, _ := os.Create(config.AppAccessLogName)
60+
gin.DefaultWriter = io.MultiWriter(f)
61+
engine.Use(gin.Logger())
62+
```
63+
64+
运行后多请求几次,日志输出在文件中:
65+
66+
```
67+
[GIN] 2019/08/30 - 21:36:07 | 200 | 369.023µs | ::1 | GET /ping
68+
[GIN] 2019/08/30 - 21:36:08 | 200 | 27.585µs | ::1 | GET /ping
69+
[GIN] 2019/08/30 - 21:36:10 | 200 | 14.302µs | ::1 | POST /product
70+
```
71+
72+
虽然记录到文件成功了,但是记录的参数不是我们想要的样子。
73+
74+
怎么办呢?
75+
76+
我们需要自定义一个日志中间件,按照我们需要的参数进行记录。
77+
78+
## 自定义 Logger()
79+
80+
**middleware/logger/logger.go**
81+
82+
```
83+
package logger
84+
85+
import (
86+
"bytes"
87+
"encoding/json"
88+
"fmt"
89+
"github.com/gin-gonic/gin"
90+
"go-gin-api/app/config"
91+
"go-gin-api/app/util"
92+
"log"
93+
"os"
94+
)
95+
96+
type bodyLogWriter struct {
97+
gin.ResponseWriter
98+
body *bytes.Buffer
99+
}
100+
func (w bodyLogWriter) Write(b []byte) (int, error) {
101+
w.body.Write(b)
102+
return w.ResponseWriter.Write(b)
103+
}
104+
func (w bodyLogWriter) WriteString(s string) (int, error) {
105+
w.body.WriteString(s)
106+
return w.ResponseWriter.WriteString(s)
107+
}
108+
109+
func SetUp() gin.HandlerFunc {
110+
return func(c *gin.Context) {
111+
bodyLogWriter := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
112+
c.Writer = bodyLogWriter
113+
114+
//开始时间
115+
startTime := util.GetCurrentMilliTime()
116+
117+
//处理请求
118+
c.Next()
119+
120+
responseBody := bodyLogWriter.body.String()
121+
122+
var responseCode int
123+
var responseMsg string
124+
var responseData interface{}
125+
126+
if responseBody != "" {
127+
response := util.Response{}
128+
err := json.Unmarshal([]byte(responseBody), &response)
129+
if err == nil {
130+
responseCode = response.Code
131+
responseMsg = response.Message
132+
responseData = response.Data
133+
}
134+
}
135+
136+
//结束时间
137+
endTime := util.GetCurrentMilliTime()
138+
139+
if c.Request.Method == "POST" {
140+
c.Request.ParseForm()
141+
}
142+
143+
//日志格式
144+
accessLogMap := make(map[string]interface{})
145+
146+
accessLogMap["request_time"] = startTime
147+
accessLogMap["request_method"] = c.Request.Method
148+
accessLogMap["request_uri"] = c.Request.RequestURI
149+
accessLogMap["request_proto"] = c.Request.Proto
150+
accessLogMap["request_ua"] = c.Request.UserAgent()
151+
accessLogMap["request_referer"] = c.Request.Referer()
152+
accessLogMap["request_post_data"] = c.Request.PostForm.Encode()
153+
accessLogMap["request_client_ip"] = c.ClientIP()
154+
155+
accessLogMap["response_time"] = endTime
156+
accessLogMap["response_code"] = responseCode
157+
accessLogMap["response_msg"] = responseMsg
158+
accessLogMap["response_data"] = responseData
159+
160+
accessLogMap["cost_time"] = fmt.Sprintf("%vms", endTime - startTime)
161+
162+
accessLogJson, _ := util.JsonEncode(accessLogMap)
163+
164+
if f, err := os.OpenFile(config.AppAccessLogName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666); err != nil {
165+
log.Println(err)
166+
} else {
167+
f.WriteString(accessLogJson + "\n")
168+
}
169+
}
170+
}
171+
```
172+
173+
运行后多请求几次,日志输出在文件中:
174+
175+
```
176+
{"cost_time":"0ms","request_client_ip":"::1","request_method":"GET","request_post_data":"","request_proto":"HTTP/1.1","request_referer":"","request_time":1567172568233,"request_ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36","request_uri":"/ping","response_code":1,"response_data":null,"response_msg":"pong","response_time":1567172568233}
177+
{"cost_time":"0ms","request_client_ip":"::1","request_method":"GET","request_post_data":"","request_proto":"HTTP/1.1","request_referer":"","request_time":1567172569158,"request_ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36","request_uri":"/ping","response_code":1,"response_data":null,"response_msg":"pong","response_time":1567172569158}
178+
{"cost_time":"0ms","request_client_ip":"::1","request_method":"POST","request_post_data":"name=admin","request_proto":"HTTP/1.1","request_referer":"","request_time":1567172629565,"request_ua":"PostmanRuntime/7.6.0","request_uri":"/product","response_code":-1,"response_data":null,"response_msg":"Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'NameValid' tag","response_time":1567172629565}
179+
```
180+
181+
OK,咱们想要的所有参数全都记录了!
182+
183+
抛出几个问题吧:
184+
185+
1、有没有开源的日志记录工具?
186+
187+
当然有,其中 logrus 是用的最多的,这个工具功能强大,原来我也分享过,可以看下原来的文章[《使用 logrus 进行日志收集》](https://mp.weixin.qq.com/s/gBWEHe20Lv_2wBSlM2WeVA)
188+
189+
2、为什么将日志记录到文本中?
190+
191+
因为,日志平台可以使用的是 ELK。
192+
193+
使用 Logstash 进行收集文本文件,使用 Elasticsearch 引擎进行搜索分析,最终在 Kibana 平台展示出来。
194+
195+
3、当大量请求过来时,写入文件会不会出问题?
196+
197+
可能会,这块可以使用异步,咱们可以用下 go 的 chan,具体实现看代码吧,我就不贴了。
198+
199+
## 源码地址
200+
201+
https://github.com/xinliangnote/go-gin-api
202+
203+
## go-gin-api 系列文章
204+
205+
- [1. 使用 go modules 初始化项目](https://mp.weixin.qq.com/s/1XNTEgZ0XGZZdxFOfR5f_A)
206+
- [2. 规划项目目录和参数验证](https://mp.weixin.qq.com/s/11AuXptWGmL5QfiJArNLnA)
74.8 KB
Loading

0 commit comments

Comments
 (0)