원문 : https://programmer.help/blogs/gin-framework-logging-with-logrus.html
Gin framework - logging with Logrus
Summary
The previous article shared the routing configuration of the Gin framework, and this article shared logging.
After checking a lot of data, github.com/sirupsen/logrus is the most widely used log record in Go.
Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger.
By default, the log of the Gin framework will only be output in the console. We use Logrus to encapsulate a middleware to record the log in the file.
This article is about learning and using Logrus.
Log format
For example, we agree that the log format is Text, including the following fields:
Request time, log level, status code, execution time, request IP, request mode and request route.
Next, we use Logrus to implement it.
Logrus use
Install in dep mode.
In Gopkg.toml file, add:
[[constraint]] name = "github.com/sirupsen/logrus" version = "1.4.2"
Import in project:
import "github.com/sirupsen/logrus"
On the project command line:
dep ensure
At this point, you will see the sirupsen directory in the vendor/github.com /.
We are ready to use it. Before we start, let's plan to set this function as a middleware, such as logger.go.
Logs can be recorded in a File, defining a LoggerToFile method.
Logs can be recorded in MongoDB and a LoggerToMongo method is defined.
Logs can be recorded in ES, defining a LoggerToES method.
Logs can be recorded in MQ, defining a LoggerToMQ method.
...
This time, let's first implement logging to file and LoggerToFile method. Others can be implemented according to our own needs.
The logger middleware, created, can be used for migration in other projects.
No more nonsense, just look at the code.
package middleware import ( "fmt" "ginDemo/config" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "os" "path" "time" ) // Log to file func LoggerToFile() gin.HandlerFunc { logFilePath := config.Log_FILE_PATH logFileName := config.LOG_FILE_NAME //log file fileName := path.Join(logFilePath, logFileName) //write file src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend) if err != nil { fmt.Println("err", err) } //instantiation logger := logrus.New() //Set output logger.Out = src //Set log level logger.SetLevel(logrus.DebugLevel) //Format log logger.SetFormatter(&logrus.TextFormatter{}) return func(c *gin.Context) { // start time startTime := time.Now() // Processing request c.Next() // End time endTime := time.Now() // execution time latencyTime := endTime.Sub(startTime) // Request mode reqMethod := c.Request.Method // Request routing reqUri := c.Request.RequestURI // Status code statusCode := c.Writer.Status() // Request IP clientIP := c.ClientIP() // Log format logger.Infof("| %3d | %13v | %15s | %s | %s |", statusCode, latencyTime, clientIP, reqMethod, reqUri, ) } } // Log to MongoDB func LoggerToMongo() gin.HandlerFunc { return func(c *gin.Context) { } } // Log to ES func LoggerToES() gin.HandlerFunc { return func(c *gin.Context) { } } // Logging to MQ func LoggerToMQ() gin.HandlerFunc { return func(c *gin.Context) { } }
Log middleware is written, how to call it?
Just add in main.go:
engine := gin.Default() //Add after this line engine.Use(middleware.LoggerToFile())
Do this and look at the log:
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 |"
This time="2019-07-17T22:10:45+08:00", this time format is not what we want, what should we do?
Time needs to be formatted. Modify logger.SetFormatter
//Format log logger.SetFormatter(&logrus.TextFormatter{ TimestampFormat:"2006-01-02 15:04:05", })
Run it, and then look at the log:
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 |"
Time has become normal.
I don't like text format, like JSON format, what to do?
//Format log logger.SetFormatter(&logrus.JSONFormatter{ TimestampFormat:"2006-01-02 15:04:05", })
Run it, and then look at the log:
{"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"}
There are too many msg information, which is inconvenient to read. What should I do?
// Log format logger.WithFields(logrus.Fields{ "status_code" : statusCode, "latency_time" : latencyTime, "client_ip" : clientIP, "req_method" : reqMethod, "req_uri" : reqUri, }).Info()
Run it, and then look at the log:
{"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"}
Note: time, msg and level are automatically added by logrus.
Does logrus support output file names and line numbers?
No, the author's reply is too performance consuming.
However, there are also people on the Internet who have realized it through the way of Hook. When they choose to use it in the production environment, they should remember to do performance tests.
Does logrus support log segmentation?
No, but there is a way to achieve it.
1. Linux logrotate can be used for unified processing by operation and maintenance.
2. It can be realized by file rotatelogs.
Need to import package:
github.com/lestrrat-go/file-rotatelogs github.com/rifflock/lfshook
Complete code:
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" ) // Log to file func LoggerToFile() gin.HandlerFunc { logFilePath := config.Log_FILE_PATH logFileName := config.LOG_FILE_NAME // log file fileName := path.Join(logFilePath, logFileName) // write file src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend) if err != nil { fmt.Println("err", err) } // instantiation logger := logrus.New() // Set output logger.Out = src // Set log level logger.SetLevel(logrus.DebugLevel) // Set rotatelogs logWriter, err := rotatelogs.New( // Split file name fileName + ".%Y%m%d.log", // Generate soft chain, point to the latest log file rotatelogs.WithLinkName(fileName), // Set maximum save time (7 days) rotatelogs.WithMaxAge(7*24*time.Hour), // Set log cutting interval (1 day) 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", }) // Add Hook logger.AddHook(lfHook) return func(c *gin.Context) { // start time startTime := time.Now() // Processing request c.Next() // End time endTime := time.Now() // execution time latencyTime := endTime.Sub(startTime) // Request mode reqMethod := c.Request.Method // Request routing reqUri := c.Request.RequestURI // Status code statusCode := c.Writer.Status() // Request IP clientIP := c.ClientIP() // Log format logger.WithFields(logrus.Fields{ "status_code" : statusCode, "latency_time" : latencyTime, "client_ip" : clientIP, "req_method" : reqMethod, "req_uri" : reqUri, }).Info() } } // Log to MongoDB func LoggerToMongo() gin.HandlerFunc { return func(c *gin.Context) { } } // Log to ES func LoggerToES() gin.HandlerFunc { return func(c *gin.Context) { } } // Logging to MQ func LoggerToMQ() gin.HandlerFunc { return func(c *gin.Context) { } }
A new file system.log.20190717.log will be generated. The log content is consistent with the above format.
Finally, there are many extensible hooks in logrus, which you can search online.
Some readers suggest that it's not convenient to read the code on the mobile phone, so it's recommended to update it to GitHub.
Now updated to the following address:
https://github.com/xinliangnote/Go
'Programming' 카테고리의 다른 글
유용한 사이트 모음 (0) | 2021.07.23 |
---|---|
[GO] go-file-rotatelogs : 로그파일 로테이션 기법 (0) | 2021.07.23 |
[go] gin : ShouldBind함수로 QueryString의 array 값 처리하기 (0) | 2021.07.19 |
[GO] struct tag 관련 글 (0) | 2021.07.19 |
[GO] Windows, macOS 및 Linux용 Go 프로그램을 교차 컴파일하는 방법 (0) | 2021.07.17 |
최근댓글