日志是程式中必不可少的一個環節, 由于Go語言内置的日志庫功能比較簡潔, 我們在實際開發中通常會選擇使用第三方的日志庫來進行開發。本文介紹了logrus這個日志庫的基本使用。
1、logrus介紹
Logrus是Go(golang)的結構化logger, 與标準庫logger完全API相容。
它有以下特點:
完全相容标準日志庫, 擁有七種日志級别:Trace, Debug, Info, Warning, Error, Fataland Panic。
可擴充的Hook機制, 允許使用者通過Hook的方式将日志分發到任意地方, 如本地檔案系統, logstash, elasticsearch或者mq等, 或者通過Hook定義日志内容和格式等
可選的日志輸出格式, 内置了兩種日志格式JSONFormater和TextFormatter, 還可以自定義日志格式
Field機制, 通過Filed機制進行結構化的日志記錄
線程安全
github位址:
https://github.com/Sirupsen/logrus
Star: 16.9k
2、安裝
go get github.com/sirupsen/logrus
3、基本示例
使用Logrus最簡單的方法是簡單的包級導出日志程式:
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{"animal": "dog"}).Info("一條舔狗出現了。")
}
4、進階示例
對于更進階的用法, 例如在同一應用程式記錄到多個位置, 你還可以建立logrus Logger的執行個體:
package main
import (
"os"
"github.com/sirupsen/logrus"
)
// 建立一個新的logger執行個體。可以建立任意多個。
var log = logrus.New()
func main() {
// 設定日志輸出為os.Stdout
log.Out = os.Stdout
// 可以設定像檔案等任意`io.Writer`類型作為日志輸出
file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) // 如果不确定檔案是否已經存在, 要使用 os.O_CREATE 參數, 但是 該檔案所屬的目錄一定要确儲存在
if err == nil {
log.Out = file
} else {
log.Info("Failed to log to file, using default stderr")
}
log.WithFields(logrus.Fields{
"animal": "dog",
"size": 10,
}).Info("一群舔狗出現了。") //這裡将寫入到logrus.log中
}
5、日志級别
Logrus有七個日志級别:Trace, Debug, Info, Warning, Error, Fataland Panic。
package main
import (
"github.com/sirupsen/logrus"
)
// 建立一個新的logger執行個體。可以建立任意多個。
var log = logrus.New()
func main() {
log.Trace("Something very low level.")
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
log.Error("Something failed but I'm not quitting.")
// 記完日志後會調用os.Exit(1)
log.Fatal("Bye.")
// 記完日志後會調用 panic()
log.Panic("I'm bailing.")
}
如何設定日志級别
你可以在Logger上設定日志記錄級别, 然後它隻會記錄具有該級别或以上級别任何内容的條目:
// 會記錄info及以上級别 (warn, error, fatal, panic)
log.SetLevel(log.InfoLevel)
如果你的程式支援debug或環境變量模式, 設定log.Level = logrus.DebugLevel會很有幫助。
6、字段
Logrus鼓勵通過日志字段進行謹慎的結構化日志記錄, 而不是冗長的、不可解析的錯誤消息。
例如, 差別于使用log.Fatalf("Failed to send event %s to topic %s with key %d"), 你應該使用如下方式記錄更容易發現的内容:
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
輸出結果:
time="2021-01-09T09:44:03+08:00" level=info msg="A walrus appears" animal=walrus
package main
import (
"github.com/sirupsen/logrus"
)
// 建立一個新的logger執行個體。可以建立任意多個。
var log = logrus.New()
func main() {
event := "時間"
topic := "主題"
key := "密鑰"
log.WithFields(logrus.Fields{
"event": event,
"topic": topic,
"key": key,
}).Fatal("Failed to send event")
}
輸出結果:
time="2021-01-09T09:47:23+08:00" level=fatal msg="Failed to send event" event="時間" key="密鑰" topic="主題"
WithFields的調用是可選的。
7、預設字段
通常, 将一些字段始終附加到應用程式的全部或部分的日志語句中會很有幫助。例如, 你可能希望始終在請求的上下文中記錄request_id和user_ip。
差別于在每一行日志中寫上log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip}), 你可以向下面的示例代碼一樣建立一個logrus.Entry去傳遞這些字段。
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
requestLogger.Info("something happened on that request") # will log request_id and user_ip
requestLogger.Warn("something not great happened")
8、日志條目
除了使用WithField或WithFields添加的字段外, 一些字段會自動添加到所有日志記錄事中:
time:記錄日志時的時間戳
msg:記錄的日志資訊
level:記錄的日志級别
9、格式化
logrus内置以下兩種日志格式化程式:
logrus.TextFormatter
logrus.JSONFormatter
執行個體: 綜合案例
package main
import (
"os"
sssss
"github.com/sirupsen/logrus"
)
func main() {
customFormatter := new(logrus.TextFormatter)
customFormatter.FullTimestamp = true // 顯示完整時間
customFormatter.TimestampFormat = "2006-01-02 15:04:05" // 時間格式
customFormatter.DisableTimestamp = false // 禁止顯示時間
customFormatter.DisableColors = true // 禁止顔色顯示
logrus.SetFormatter(customFormatter)
logrus.SetOutput(os.Stdout)
logrus.SetLevel(logrus.DebugLevel)
logrus.Debug("Debug日志")
logrus.Info("Info日志")
logrus.Warn("Warn日志")
logrus.Error("Error日志")
logrus.Fatal("Fatal日志") //log之後會調用os.Exit(1)
logrus.Panic("Panic日志") //log之後會panic()
}
輸出格式:
time="2021-01-07 22:48:38" level=debug msg="Debug日志"
time="2021-01-07 22:48:38" level=info msg="Info日志"
time="2021-01-07 22:48:38" level=warning msg="Warn日志"
time="2021-01-07 22:48:38" level=error msg="Error日志"
time="2021-01-07 22:48:38" level=fatal msg="Fatal日志"
10、記錄函數名
如果你希望将調用的函數名添加為字段, 請通過以下方式設定:
log.SetReportCaller(true)
這會将調用者添加為"method", 如下所示:
{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by",
"time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
注意:開啟這個模式會增加性能開銷。
11、Hooks
你可以添加日志級别的鈎子(Hook)。例如, 向異常跟蹤服務發送Error、Fatal和Panic、資訊到StatsD或同時将日志發送到多個位置, 例如syslog。
Logrus配有内置鈎子。在init中添加這些内置鈎子或你自定義的鈎子:
12、線程安全
預設的logger在并發寫的時候是被mutex保護的,比如當同時調用hook和寫log時mutex就會被請求,有另外一種情況,檔案是以appending mode打開的, 此時的并發操作就是安全的,可以用logger.SetNoLock()來關閉它。
gin架構使用logrus
package main
import (
"os"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
var log = logrus.New()
func init() {
log.Formatter = &logrus.JSONFormatter{}
f, _ := os.Create("./gin.log")
log.Out = f // 重點: 日志輸入到檔案中
gin.SetMode(gin.ReleaseMode) // 設定gin架構模式 線上模式 不會在終端列印多餘的日志資訊
gin.DefaultWriter = log.Out
log.Level = logrus.InfoLevel
}
func main() {
// 建立一個預設的路由引擎
r := gin.Default()
// GET:請求方式;/hello:請求的路徑
// 當用戶端以GET方法請求/hello路徑時,會執行後面的匿名函數
r.GET("/hello", func(c *gin.Context) {
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Warn("A group of walrus emerges from the ocean")
// c.JSON:傳回JSON格式的資料
c.JSON(200, gin.H{
"message": "Hello world!",
})
})
// 啟動HTTP服務,預設在0.0.0.0:8080啟動服務
r.Run()
}