前一篇文章介紹了 Go 標準庫中的日誌庫 log
。最後咱們也提到,log
庫只提供了三組接口,功能過於簡單了。 今天,咱們來介紹一個日誌庫中的「明星庫」——logrus
。本文編寫之時(2020.02.07),logrus 在 GitHub 上 star 數已達到 13.8k。 logrus
徹底兼容標準的log
庫,還支持文本、JSON 兩種日誌輸出格式。不少知名的開源項目都使用了這個庫,如大名鼎鼎的 docker。git
第三方庫須要先安裝:程序員
$ go get github.com/sirupsen/logrus
複製代碼
後使用:github
package main
import (
"github.com/sirupsen/logrus"
)
func main() {
logrus.SetLevel(logrus.TraceLevel)
logrus.Trace("trace msg")
logrus.Debug("debug msg")
logrus.Info("info msg")
logrus.Warn("warn msg")
logrus.Error("error msg")
logrus.Fatal("fatal msg")
logrus.Panic("panic msg")
}
複製代碼
logrus
的使用很是簡單,與標準庫log
相似。logrus
支持更多的日誌級別:golang
Panic
:記錄日誌,而後panic
。Fatal
:致命錯誤,出現錯誤時程序沒法正常運轉。輸出日誌後,程序退出;Error
:錯誤日誌,須要查看緣由;Warn
:警告信息,提醒程序員注意;Info
:關鍵操做,核心流程的日誌;Debug
:通常程序中輸出的調試信息;Trace
:很細粒度的信息,通常用不到;日誌級別從上向下依次增長,Trace
最大,Panic
最小。logrus
有一個日誌級別,高於這個級別的日誌不會輸出。 默認的級別爲InfoLevel
。因此爲了能看到Trace
和Debug
日誌,咱們在main
函數第一行設置日誌級別爲TraceLevel
。web
運行程序,輸出:redis
$ go run main.go
time="2020-02-07T21:22:42+08:00" level=trace msg="trace msg"
time="2020-02-07T21:22:42+08:00" level=debug msg="debug msg"
time="2020-02-07T21:22:42+08:00" level=info msg="info msg"
time="2020-02-07T21:22:42+08:00" level=info msg="warn msg"
time="2020-02-07T21:22:42+08:00" level=error msg="error msg"
time="2020-02-07T21:22:42+08:00" level=fatal msg="fatal msg"
exit status 1
複製代碼
因爲logrus.Fatal
會致使程序退出,下面的logrus.Panic
不會執行到。mongodb
另外,咱們觀察到輸出中有三個關鍵信息,time
、level
和msg
:docker
time
:輸出日誌的時間;level
:日誌級別;msg
:日誌信息。調用logrus.SetReportCaller(true)
設置在輸出日誌中添加文件名和方法信息:windows
package main
import (
"github.com/sirupsen/logrus"
)
func main() {
logrus.SetReportCaller(true)
logrus.Info("info msg")
}
複製代碼
輸出多了兩個字段file
爲調用logrus
相關方法的文件名,method
爲方法名:bash
$ go run main.go
time="2020-02-07T21:46:03+08:00" level=info msg="info msg" func=main.main file="D:/code/golang/src/github.com/darjun/go-daily-lib/logrus/caller/main.go:10"
複製代碼
有時候須要在輸出中添加一些字段,能夠經過調用logrus.WithField
和logrus.WithFields
實現。 logrus.WithFields
接受一個logrus.Fields
類型的參數,其底層實際上爲map[string]interface{}
:
// github.com/sirupsen/logrus/logrus.go
type Fields map[string]interface{}
複製代碼
下面程序在輸出中添加兩個字段name
和age
:
package main
import (
"github.com/sirupsen/logrus"
)
func main() {
logrus.WithFields(logrus.Fields{
"name": "dj",
"age": 18,
}).Info("info msg")
}
複製代碼
若是在一個函數中的全部日誌都須要添加某些字段,可使用WithFields
的返回值。例如在 Web 請求的處理器中,日誌都要加上user_id
和ip
字段:
package main
import (
"github.com/sirupsen/logrus"
)
func main() {
requestLogger := logrus.WithFields(logrus.Fields{
"user_id": 10010,
"ip": "192.168.32.15",
})
requestLogger.Info("info msg")
requestLogger.Error("error msg")
}
複製代碼
實際上,WithFields
返回一個logrus.Entry
類型的值,它將logrus.Logger
和設置的logrus.Fields
保存下來。 調用Entry
相關方法輸出日誌時,保存下來的logrus.Fields
也會隨之輸出。
默認狀況下,日誌輸出到io.Stderr
。能夠調用logrus.SetOutput
傳入一個io.Writer
參數。後續調用相關方法日誌將寫到io.Writer
中。 如今,咱們就能像上篇文章介紹log時同樣,能夠搞點事情了。傳入一個io.MultiWriter
, 同時將日誌寫到bytes.Buffer
、標準輸出和文件中:
package main
import (
"bytes"
"io"
"log"
"os"
"github.com/sirupsen/logrus"
)
func main() {
writer1 := &bytes.Buffer{}
writer2 := os.Stdout
writer3, err := os.OpenFile("log.txt", os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
log.Fatalf("create file log.txt failed: %v", err)
}
logrus.SetOutput(io.MultiWriter(writer1, writer2, writer3))
logrus.Info("info msg")
}
複製代碼
實際上,考慮到易用性,庫通常會使用默認值建立一個對象,包最外層的方法通常都是操做這個默認對象。
咱們以前好幾篇文章都提到過這點:
flag
標準庫中的CommandLine
對象;log
標準庫中的std
對象。這個技巧應用在不少庫的開發中,logrus
也是如此:
// github.com/sirupsen/logrus/exported.go
var (
std = New()
)
func StandardLogger() *Logger {
return std
}
func SetOutput(out io.Writer) {
std.SetOutput(out)
}
func SetFormatter(formatter Formatter) {
std.SetFormatter(formatter)
}
func SetReportCaller(include bool) {
std.SetReportCaller(include)
}
func SetLevel(level Level) {
std.SetLevel(level)
}
複製代碼
首先,使用默認配置定義一個Logger
對象std
,SetOutput/SetFormatter/SetReportCaller/SetLevel
這些方法都是調用std
對象的對應方法!
咱們固然也能夠建立本身的Logger
對象,使用方式與直接調用logrus
的方法相似:
package main
import "github.com/sirupsen/logrus"
func main() {
log := logrus.New()
log.SetLevel(logrus.InfoLevel)
log.SetFormatter(&logrus.JSONFormatter{})
log.Info("info msg")
}
複製代碼
logrus
支持兩種日誌格式,文本和 JSON,默認爲文本格式。能夠經過logrus.SetFormatter
設置日誌格式:
package main
import (
"github.com/sirupsen/logrus"
)
func main() {
logrus.SetLevel(logrus.TraceLevel)
logrus.SetFormatter(&logrus.JSONFormatter{})
logrus.Trace("trace msg")
logrus.Debug("debug msg")
logrus.Info("info msg")
logrus.Warn("warn msg")
logrus.Error("error msg")
logrus.Fatal("fatal msg")
logrus.Panic("panic msg")
}
複製代碼
程序輸出 JSON 格式的日誌:
$ go run main.go
{"level":"trace","msg":"trace msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"debug","msg":"debug msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"info","msg":"info msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"info","msg":"warn msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"error","msg":"error msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"fatal","msg":"fatal msg","time":"2020-02-07T21:40:04+08:00"}
exit status 1
複製代碼
除了內置的TextFormatter
和JSONFormatter
,還有很多第三方格式支持。咱們這裏介紹一個nested-logrus-formatter。
先安裝:
$ go get github.com/antonfisher/nested-logrus-formatter
複製代碼
後使用:
package main
import (
nested "github.com/antonfisher/nested-logrus-formatter"
"github.com/sirupsen/logrus"
)
func main() {
logrus.SetFormatter(&nested.Formatter{
HideKeys: true,
FieldsOrder: []string{"component", "category"},
})
logrus.Info("info msg")
}
複製代碼
程序輸出:
Feb 8 15:22:59.077 [INFO] info msg
複製代碼
nested
格式提供了多個字段用來定製行爲:
// github.com/antonfisher/nested-logrus-formatter/formatter.go
type Formatter struct {
FieldsOrder []string
TimestampFormat string
HideKeys bool
NoColors bool
NoFieldsColors bool
ShowFullLevel bool
TrimMessages bool
}
複製代碼
logrus
輸出日誌中字段是key=value
這樣的形式。使用nested
格式,咱們能夠經過設置HideKeys
爲true
隱藏鍵,只輸出值;logrus
是按鍵的字母序輸出字段,能夠設置FieldsOrder
定義輸出字段順序;TimestampFormat
設置日期格式。package main
import (
"time"
nested "github.com/antonfisher/nested-logrus-formatter"
"github.com/sirupsen/logrus"
)
func main() {
logrus.SetFormatter(&nested.Formatter{
// HideKeys: true,
TimestampFormat: time.RFC3339,
FieldsOrder: []string{"name", "age"},
})
logrus.WithFields(logrus.Fields{
"name": "dj",
"age": 18,
}).Info("info msg")
}
複製代碼
若是不隱藏鍵,程序輸出:
$ 2020-02-08T15:40:07+08:00 [INFO] [name:dj] [age:18] info msg
複製代碼
隱藏鍵,程序輸出:
$ 2020-02-08T15:41:58+08:00 [INFO] [dj] [18] info msg
複製代碼
注意到,咱們將時間格式設置成time.RFC3339
,即2006-01-02T15:04:05Z07:00
這種形式。
經過實現接口logrus.Formatter
能夠實現本身的格式。
// github.com/sirupsen/logrus/formatter.go
type Formatter interface {
Format(*Entry) ([]byte, error)
}
複製代碼
還能夠爲logrus
設置鉤子,每條日誌輸出前都會執行鉤子的特定方法。因此,咱們能夠添加輸出字段、根據級別將日誌輸出到不一樣的目的地。 logrus
也內置了一個syslog
的鉤子,將日誌輸出到syslog
中。這裏咱們實現一個鉤子,在輸出的日誌中增長一個app=awesome-web
字段。
鉤子須要實現logrus.Hook
接口:
// github.com/sirupsen/logrus/hooks.go
type Hook interface {
Levels() []Level
Fire(*Entry) error
}
複製代碼
Levels()
方法返回感興趣的日誌級別,輸出其餘日誌時不會觸發鉤子。Fire
是日誌輸出前調用的鉤子方法。
package main
import (
"github.com/sirupsen/logrus"
)
type AppHook struct {
AppName string
}
func (h *AppHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (h *AppHook) Fire(entry *logrus.Entry) error {
entry.Data["app"] = h.AppName
return nil
}
func main() {
h := &AppHook{AppName: "awesome-web"}
logrus.AddHook(h)
logrus.Info("info msg")
}
複製代碼
只須要在Fire
方法實現中,爲entry.Data
添加字段就會輸出到日誌中。
程序輸出:
$ time="2020-02-08T15:51:52+08:00" level=info msg="info msg" app=awesome-web
複製代碼
logrus
的第三方 Hook 不少,咱們可使用一些 Hook 將日誌發送到 redis/mongodb 等存儲中:
這裏咱們演示一個 redis,感興趣自行驗證其餘的。先安裝logrus-redis-hook
:
$ go get github.com/rogierlommers/logrus-redis-hook
複製代碼
而後編寫程序:
package main
import (
"io/ioutil"
logredis "github.com/rogierlommers/logrus-redis-hook"
"github.com/sirupsen/logrus"
)
func init() {
hookConfig := logredis.HookConfig{
Host: "localhost",
Key: "mykey",
Format: "v0",
App: "aweosome",
Hostname: "localhost",
TTL: 3600,
}
hook, err := logredis.NewHook(hookConfig)
if err == nil {
logrus.AddHook(hook)
} else {
logrus.Errorf("logredis error: %q", err)
}
}
func main() {
logrus.Info("just some info logging...")
logrus.WithFields(logrus.Fields{
"animal": "walrus",
"foo": "bar",
"this": "that",
}).Info("additional fields are being logged as well")
logrus.SetOutput(ioutil.Discard)
logrus.Info("This will only be sent to Redis")
}
複製代碼
爲了程序能正常工做,咱們還須要安裝redis
。
windows 上直接使用choco安裝 redis:
PS C:\Users\Administrator> choco install redis-64
Chocolatey v0.10.15
Installing the following packages:
redis-64
By installing you accept licenses for the packages.
Progress: Downloading redis-64 3.0.503... 100%
redis-64 v3.0.503 [Approved]
redis-64 package files install completed. Performing other installation steps.
ShimGen has successfully created a shim for redis-benchmark.exe
ShimGen has successfully created a shim for redis-check-aof.exe
ShimGen has successfully created a shim for redis-check-dump.exe
ShimGen has successfully created a shim for redis-cli.exe
ShimGen has successfully created a shim for redis-server.exe
The install of redis-64 was successful.
Software install location not explicitly set, could be in package or
default install location if installer.
Chocolatey installed 1/1 packages.
See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).
複製代碼
直接輸入redis-server
,啓動服務器:
運行程序後,咱們使用redis-cli
查看:
咱們看到mykey
是一個list
,每過來一條日誌,就在list
後新增一項。
本文介紹了logrus
的基本用法。logrus
的可擴展性很是棒,能夠引入第三方格式和 Hook 加強功能。在社區也比較受歡迎。
歡迎關注個人微信公衆號【GoUpUp】,共同窗習,一塊兒進步~
本文由博客一文多發平臺 OpenWrite 發佈!