[toc] logrus介紹 golang標準庫的日誌框架非常簡單,僅僅提供了print,panic和fatal三個函數。對於更精細的日誌級別、日誌文件分割,以及日誌分發等方面,並沒有提供支持。在golang的世界,流行的日誌框架包括logrus、zap、zerolog、seelog等。 logru ...
目錄
logrus介紹
golang標準庫的日誌框架非常簡單,僅僅提供了print,panic和fatal三個函數。對於更精細的日誌級別、日誌文件分割,以及日誌分發等方面,並沒有提供支持。在golang的世界,流行的日誌框架包括logrus、zap、zerolog、seelog等。
logrus配置
1. 日誌級別: logrus有7個日誌級別,依次是Trace << Debug << Info << Warning << Error << Fatal << Panic
// 只輸出不低於當前級別是日誌數據
logrus.SetLevel(logrus.DebugLevel)
2. 日誌格式: logrus內置了JSONFormatter
和TextFormatter
兩種格式,也可以通過Formatter
介面定義日誌格式
// TextFormatter格式
logrus.SetFormatter(&logrus.TextFormatter{
ForceColors: true,
EnvironmentOverrideColors: true,
TimestampFormat: "2006-01-02 15:04:05", //時間格式
// FullTimestamp:true,
// DisableLevelTruncation:true,
})
// JSONFormatter格式
logrus.SetFormatter(&logrus.JSONFormatter{
PrettyPrint: false, //格式化
TimestampFormat: "2006-01-02 15:04:05", //時間格式
})
3. 輸出文件:
logfile, _ := os.OpenFile("./app.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
logrus.SetOutput(logfile) //預設為os.stderr
4. 日誌定位: 定位行號(如:func=main.main file="./xxx.go:38"
)
logrus.SetReportCaller(true)
示例:
func init() {
logrus.SetLevel(logrus.DebugLevel)
logrus.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
})
logfile, _ := os.OpenFile("./app.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
logrus.SetOutput(logfile) //預設為os.stderr
}
//方式一:logrus函數(最終調用的是logrus.StandardLogger預設實例方法)
func main() {
logrus.Infoln("測試數據")
}
日誌列印
FieldLogger介面: FieldLogger
定義了所有日誌列印的方法
type FieldLogger interface {
WithField(key string, value interface{}) *Entry
WithFields(fields Fields) *Entry
WithError(err error) *Entry
Debugf(format string, args ...interface{})
Infof(format string, args ...interface{})
Printf(format string, args ...interface{})
Warnf(format string, args ...interface{})
Warningf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
Panicf(format string, args ...interface{})
Debug(args ...interface{})
Info(args ...interface{})
Print(args ...interface{})
Warn(args ...interface{})
Warning(args ...interface{})
Error(args ...interface{})
Fatal(args ...interface{})
Panic(args ...interface{})
Debugln(args ...interface{})
Infoln(args ...interface{})
Println(args ...interface{})
Warnln(args ...interface{})
Warningln(args ...interface{})
Errorln(args ...interface{})
Fatalln(args ...interface{})
Panicln(args ...interface{})
}
日誌列印1: 預設實例 (函數)
,即通過logrus包提供的函數(覆蓋了FieldLogger
介面的所有方法),直接列印日誌。但其實logrus包函數是調用了logrus.Loger
預設實例。
// 直接調用包函數
func main() {
logrus.Infoln("...")
logrus.Errorln("...")
// ...
}
日誌列印2:Logger實例(對象)
,它實現了FieldLogger
介面。
func main() {
//var loger = logrus.StandardLogger()
var loger = logrus.New()
loger.Formatter = &logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"}
loger.Infoln("可以創建任意數量的logrus實例")
}
日誌列印3:Entry示例(對象)
,它也實現了FieldLogger
介面,是最終是日誌列印入口。
- 這裡用到了
Field
機制,logrus鼓勵通過Field
機制進行精細化的、結構化的日誌記錄,而不是通過冗長的消息來記錄日誌。
func main() {
logrus.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})
// Entry實例
entry := logrus.WithFields(logrus.Fields{
"global": "全局欄位",
})
entry.WithFields(logrus.Fields{"module": "用戶模塊"}).
Info("測試ok")
}
HOOK機制
- hook即鉤子,攔截器。它為logrus提供了強大的功能擴展,如將日誌分發到任意地方,如本地文件系統、
logstash
、es
等,或者切割日誌、定義日誌內容和格式等。hook介面原型如下:
type Hook interface {
Levels() []Level //日誌級別
Fire(*Entry) error //列印入口(Entry對象)
}
Hook - 日誌切割:
import (
"github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
"time"
)
// 說明:按時間切割日誌文件(2秒創建一個日誌文件)
func main() {
hook := NewLfsHook("app_hook", time.Second*2, 5)
logrus.AddHook(hook)
logrus.Infoln("測試開始")
log := logrus.WithFields(logrus.Fields{"module": "用戶模塊"})
for i := 0; i < 10; i++ {
log.Infoln("成功", i)
time.Sleep(time.Second)
}
}
// 日誌鉤子(日誌攔截,並重定向)
func NewLfsHook(logName string, rotationTime time.Duration, leastDay uint) logrus.Hook {
writer, err := rotatelogs.New(
// 日誌文件
logName+".%Y%m%d%H%M%S",
// 日誌周期(預設每86400秒/一天旋轉一次)
rotatelogs.WithRotationTime(rotationTime),
// 清除歷史 (WithMaxAge和WithRotationCount只能選其一)
//rotatelogs.WithMaxAge(time.Hour*24*7), //預設每7天清除下日誌文件
rotatelogs.WithRotationCount(leastDay), //只保留最近的N個日誌文件
)
if err != nil {
panic(err)
}
// 可設置按不同level創建不同的文件名
lfsHook := lfshook.NewHook(lfshook.WriterMap{
logrus.DebugLevel: writer,
logrus.InfoLevel: writer,
logrus.WarnLevel: writer,
logrus.ErrorLevel: writer,
logrus.FatalLevel: writer,
logrus.PanicLevel: writer,
}, &logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})
return lfsHook
}
Hook - Redis重定向: 即將日誌輸出到redis
import (
logredis "github.com/rogierlommers/logrus-redis-hook"
"io/ioutil"
"github.com/sirupsen/logrus"
)
func init() {
hookConfig := logredis.HookConfig{
Host: "localhost",
Key: "test",
Format: "v1",
App: "my_app_name",
Port: 6379,
Hostname: "my_app_hostname",
DB: 0, // optional
TTL: 3600,
}
hook, err := logredis.NewHook(hookConfig)
if err == nil {
logrus.AddHook(hook)
} else {
logrus.Errorf("logredis error: %q", err)
}
}
func main() {
logrus.WithFields(logrus.Fields{
"module": "user"}).
Info("user login")
// If you want to disable writing to stdout, use setOutput
logrus.SetOutput(ioutil.Discard)
logrus.Info("log to Redis")
}
// 測試:
// 1.啟動redis服務: redis-server
// 2.監控redis數據: redis-cli monitor
其他Hook:
MongoDb
:https://github.com/weekface/mgorusRedis
:https://github.com/rogierlommers/logrus-redis-hookInfluxDb
:https://github.com/abramovic/logrus_influxdbLogstash
:https://github.com/bshuster-repo/logrus-logstash-hook
Gin日誌
- 將gin框架的日誌定向到logrus日誌文件
func init() {
// 輸出格式
logrus.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})
// 輸出路徑
logfile, _ := os.OpenFile("./app.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
logrus.SetOutput(logfile)
// Gin日誌重定向
gin.DisableConsoleColor() //不需要顏色
gin.DefaultWriter = io.MultiWriter(os.Stdout, logfile) //os.Stdout
}
//測試:curl 0.0.0.0:8080/index
func main() {
log := logrus.WithFields(logrus.Fields{
"module": "用戶模塊",
})
r := gin.Default()
r.GET("/index", func(c *gin.Context) {
log.Warnln("gin日誌數據")
c.String(200, "ok")
})
_ = r.Run()
}
Fatal處理
- logrus的
Fatal
輸出,會執行os.Exit(1)
。logrus提供RegisterExitHandler
方法,可以在系統異常時調用一些資源釋放api等,讓應用正確地關閉。
func main() {
logrus.RegisterExitHandler(func() {
fmt.Println("發生了fatal異常,執行關閉文件等工作")
})
logrus.Warnln("warn測試")
logrus.Fatalln("fatal測試")
logrus.Infoln("info測試") //不會執行
}
線程安全
- 預設情況下,logrus的api都是線程安全的,其內部通過互斥鎖來保護併發寫。互斥鎖在調用hooks或者寫日誌的時候執行。如果不需要鎖,可以調用
logger.SetNoLock()
來關閉之。
可以關閉logrus互斥鎖的情形包括: - 沒有設置hook,或者所有的hook都是線程安全的實現。
- 寫日誌到logger.Out已經是線程安全的了。例如,logger.Out已經被鎖保護,或者寫文件時,文件是以O_APPEND方式打開的,並且每次寫操作都小於4k。
參考