Обзор
Сначала синхронизируйте обзор проекта:
Последняя опубликованная статья посвящена планированию каталога проекта и проверке параметров, в которой для проверки параметров используется версия validator.v8, которая была обновлена до версии validator.v9, а последний код можно просмотреть на github.
В этой статье мы поделимся: Промежуточное ПО для маршрутизации — ведение журнала.
Журнал - это особенно важная вещь, которая нам удобна для устранения проблемы.В этой статье мы реализуем лог в текстовый файл.
Вот что я запланировал и параметры, которые нужно записать:
- request 请求数据
- request_time
- request_method
- request_uri
- request_proto
- request_ua
- request_referer
- request_post_data
- request_client_ip
- response 返回数据
- response_time
- response_code
- response_msg
- response_data
- cost_time 花费时间
Фреймворк Gin поставляется с промежуточным программным обеспечением Logger.Мы знаем, соответствует ли промежуточное программное обеспечение Logger, которое поставляется с фреймворком, нашим потребностям?
gin.Logger()
Давайте сначала воспользуемся gin.Logger(), чтобы увидеть эффект.
Добавьте код в метод SetupRouter route.go:
engine.Use(gin.Logger())
После запуска запроса несколько раз, вывод журнала находится в командной строке:
[GIN] 2019/08/30 - 21:24:16 | 200 | 178.072µs | ::1 | GET /ping
[GIN] 2019/08/30 - 21:24:27 | 200 | 367.997µs | ::1 | POST /product
[GIN] 2019/08/30 - 21:24:28 | 200 | 2.521592ms | ::1 | POST /product
Сначала решим первую задачу, как вывести лог в текст?
Добавьте код в метод SetupRouter route.go:
f, _ := os.Create(config.AppAccessLogName)
gin.DefaultWriter = io.MultiWriter(f)
engine.Use(gin.Logger())
После запуска запроса несколько раз вывод лога в файл:
[GIN] 2019/08/30 - 21:36:07 | 200 | 369.023µs | ::1 | GET /ping
[GIN] 2019/08/30 - 21:36:08 | 200 | 27.585µs | ::1 | GET /ping
[GIN] 2019/08/30 - 21:36:10 | 200 | 14.302µs | ::1 | POST /product
Хотя запись в файл прошла успешно, записанные параметры не те, что нам нужны.
Как это сделать?
Нам нужно настроить промежуточное программное обеспечение журнала для записи в соответствии с нужными нам параметрами.
Пользовательский регистратор()
middleware/logger/logger.go
package logger
import (
"bytes"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"go-gin-api/app/config"
"go-gin-api/app/util"
"log"
"os"
)
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func (w bodyLogWriter) WriteString(s string) (int, error) {
w.body.WriteString(s)
return w.ResponseWriter.WriteString(s)
}
func SetUp() gin.HandlerFunc {
return func(c *gin.Context) {
bodyLogWriter := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = bodyLogWriter
//开始时间
startTime := util.GetCurrentMilliTime()
//处理请求
c.Next()
responseBody := bodyLogWriter.body.String()
var responseCode int
var responseMsg string
var responseData interface{}
if responseBody != "" {
response := util.Response{}
err := json.Unmarshal([]byte(responseBody), &response)
if err == nil {
responseCode = response.Code
responseMsg = response.Message
responseData = response.Data
}
}
//结束时间
endTime := util.GetCurrentMilliTime()
if c.Request.Method == "POST" {
c.Request.ParseForm()
}
//日志格式
accessLogMap := make(map[string]interface{})
accessLogMap["request_time"] = startTime
accessLogMap["request_method"] = c.Request.Method
accessLogMap["request_uri"] = c.Request.RequestURI
accessLogMap["request_proto"] = c.Request.Proto
accessLogMap["request_ua"] = c.Request.UserAgent()
accessLogMap["request_referer"] = c.Request.Referer()
accessLogMap["request_post_data"] = c.Request.PostForm.Encode()
accessLogMap["request_client_ip"] = c.ClientIP()
accessLogMap["response_time"] = endTime
accessLogMap["response_code"] = responseCode
accessLogMap["response_msg"] = responseMsg
accessLogMap["response_data"] = responseData
accessLogMap["cost_time"] = fmt.Sprintf("%vms", endTime - startTime)
accessLogJson, _ := util.JsonEncode(accessLogMap)
if f, err := os.OpenFile(config.AppAccessLogName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666); err != nil {
log.Println(err)
} else {
f.WriteString(accessLogJson + "\n")
}
}
}
После запуска запроса несколько раз вывод лога в файл:
{"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}
{"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}
{"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}
OK, все нужные нам параметры записаны!
Задайте несколько вопросов:
1. Существуют ли инструменты ведения журнала с открытым исходным кодом?
Конечно есть,из которых логрус самый используемый.Этот инструмент мощный.Я им тоже поделился.Вы можете прочитать оригинал статьи."Сбор логов с логрусом".
2. Зачем входить в текст?
Потому что лог-платформа может использовать ELK.
Используйте Logstash для сбора текстовых файлов, используйте механизм Elasticsearch для анализа поиска и, наконец, отобразите их на платформе Kibana.
3. При большом количестве запросов не будет ли проблем с записью в файл?
Может быть, этот кусок может использовать асинхронность, мы можем использовать go's chan, смотрите код конкретной реализации, я не буду его выкладывать.