Logrus libraries use
-
Introduction to the
Logrus is a structured logger for Go (Golang), and the API is fully compatible with standard library loggers. The log library in Golang is too simple to meet our current needs most of the time, mainly because it provides too simple interface functions. Logrus is designed to solve this problem. It is currently compatible with standard log libraries and supports json and Text text output.
-
The installation
go get github.com/sirupsen/logrus
-
Quick learning
-
// Create an instance log := *logrus.New() // Set it to json format log.SetFormatter(&logrus.JSONFormatter{ TimestampFormat: "The 2006-01-02 15:04:05",})// Set the log level log.SetLevel(logrus.InfoLevel) // Write to the log log.WithFields(logrus.Fields{ "name": "A ball.", }).Info("Here's Logrus quick use.") Copy the code
-
Log levels supported by Logrus
- Panic: Logs are recorded, and Panic occurs
- Fatal: Fatal errors that cause the program to crash, log, and exit
- Error: indicates Error logs
- Warn: warning logs
- Info: core flow logs
- Debug: Debug logs (Debug logs)
- Trace: Ultra-fine grained, which is not used under normal circumstances
Lavel: Panic < Fatal < Error < Warn < Info < Debug < Trace
-
-
The log information is output to a file
package tool import ( "bytes" "fmt" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/sirupsen/logrus" Type bodyLogWriter struct {gin.ResponseWriter body * bytes.buffer} // Define an instance var LoggerInfo logrus.Logger // Set the user uid, Var uid string func init() {setUid() Logger()} func Logger(){nowTime := time.now () LogFilePath := GetString(" logrus.log_file_path ") // If len(logFilePath) <= 0 {if dir, err := os.getwd (); if len(logFilePath) <= 0 { Err == nil {logFilePath = dir + "/logs/"}} // Create folder if err := os.mkdirall (logFilePath, os.modeperm); err ! // fileName logFileName := nowtime. Format("2006-01-02") + ".log" // log file address spliced fileName := Path. Join(logFilePath, logFileName) //fmt.Println(" fileName: "+fileName) if _, err := os.stat (fileName); err ! Println(" Check file: "+ err.error ()) _, err := os.create (fileName) if err! Println(err.error ())}} // OpenFile SRC, err := os.openfile (fileName, os.O_CREATE|os.O_RDWR|os.O_APPEND,os.ModeAppend|os.ModePerm) if err ! = nil { fmt.Println("write file log error", Err)} // instantiate loggerInfo = * logrus.new () // Set the output loggerInfo.out = SRC // // Logger. SetLevel(logrus.infolevel) //// Set log format json format // Logger. SetFormatter(& Logrus.jsonformatter {// TimestampFormat: "The 2006-01-02 15:04:05", //}) loggerInfo.AddHook(&LogrusHook{}) loggerInfo.SetFormatter(&logrus.JSONFormatter{ TimestampFormat: })} // Func LogErrorInfoToFile(fields logrus.fields) {loggerInfo.setlevel (logrus.infolevel) Loggerinfo.withfields (fields).info ()} // Write the binary to the buffer func (w bodyLogWriter) Write(b []byte) (int, Error) {w.body.write (b) return w.responsewriter.write (b)} // Write the string to the buffer func (w bodyLogWriter) WriteString(s string) (int, error) {w.b ody. WriteString (s) return w.R esponseWriter. WriteString (s)} / / get back to their body middleware func GinBodyLogMiddleware () gin.HandlerFunc { return func(c *gin.Context) { blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: ResponseStr := blw.body.string () // startTime := time.now () // endTime := Time.now () // Execution time latencyTime := endtime.sub (startTime) // Request Method reqMethod := c.equest.Method // Request route reqUri := C. equest.requesturi // Status code statusCode := C. whiter.status () // Request IP clientIP := c. clientIP () // Request parameter reqParams := C. equest.body // Request ua reqUa := C. equest.userAgent () var resultBody logrus.fields resultBody = make(map[string]interface{}) resultBody["response"] = responseStr resultBody["requestUri"] = reqUri resultBody["clientIp"] = clientIP resultBody["body"] = reqParams resultBody["userAgent"] = reqUa resultBody["requestMethod"] = reqMethod resultBody["startTime"] = startTime resultBody["endTime"] = endTime resultBody["latencyTime"] = latencyTime resultBody["statusCode"] = statusCode LogErrorInfoToFile(resultBody) setUid() } } func setUid() { uid = uuid.New().String() } func GetNewUid() string { return uid }Copy the code
Since I am using the GIN framework here, I use a middleware to log, which writes each returned structure to the log. However, there is still a problem with how to distinguish a log that has been requested to end. In this case, we need to use hook, see the next section.
-
hook
“Hook is the function of extending logs. It intercepts and modifies entries every time logs are written.
Based on this feature, we can think deeply about whether this is something we can manipulate to add the data we want.
To implement this idea we must first implement the hook interface, which is defined in Logrus.
This is the interface defined in Logruspackage logrus // A hook to be fired when logging on the logging levels returned from // `Levels()` on your implementation of the interface. Note that this is not // fired in a goroutine or a channel with workers, you should handle such // functionality yourself if your call is non-blocking and you don't wish for // the logging calls for levels returned from `Levels()` to block. type Hook interface { Levels() []Level Fire(*Entry) error } // Internal type for storing the hooks on a logger instance. type LevelHooks map[Level][]Hook // Add a hook to an instance of logger. This is called with // `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. func (hooks LevelHooks) Add(hook Hook) { for _, level := range hook.Levels() { hooks[level] = append(hooks[level], hook) } } // Fire all the hooks for the passed level. Used by `entry.log` to fire // appropriate hooks for a log entry. func (hooks LevelHooks) Fire(level Level, entry *Entry) error { for _, hook := range hooks[level] { iferr := hook.Fire(entry); err ! =nil { return err } } return nil } Copy the code
So we’re going to implement this interface, and we’re going to go crazy with our own methods
package tool import ( "github.com/sirupsen/logrus" ) type LogrusHook struct{}// Set all log levels to follow this hook func (hook *LogrusHook) Levels(a) []logrus.Level { return logrus.AllLevels } // Modify the data, or other operations func (hook *LogrusHook) Fire(entry *logrus.Entry) error { entry.Data["request_id"] = GetNewUid() return nil } Copy the code
Here I have modified the data in data and added a request ID to it. This ID is unique under the current request, so that we can ensure that the request is recorded uniformly and it is convenient for us to find the log.
-
Extend error logging to get the file and line number of the error message
func getErrorFileAndLine(err error) { // Get the file and line count of the upper runtime for skip := 1; true; skip++ { // Get the file and line count of the upper runtime _, file, line, ok := runtime.Caller(skip) if ok { var resultBody logrus.Fields resultBody = make(map[string]interface{}) resultBody["file_path"] = file resultBody["error_line"] = line resultBody["error_message"] = err.Error() LogErrorInfoToFile(resultBody) }else { break}}}Copy the code
As for the runtime.Caller method, it uses an infinite loop to obtain the records from the request to the error.
-
In general, this is still not deep enough. Logrus and hook, log cutting or runtime.Caller are all points that can be optimized.