golang標準庫的日誌框架很是簡單,僅僅提供了print,panic和fatal三個函數。對於更精細的日誌級別、日誌文件分割,以及日誌分發等方面,並無提供支持。在golang的世界,流行的日誌框架包括logrus、zap、zerolog、seelog等。github
1. 日誌級別: logrus有7個日誌級別,依次是Trace << Debug << Info << Warning << Error << Fatal << Panic
golang
// 只輸出不低於當前級別是日誌數據 logrus.SetLevel(logrus.DebugLevel)
2. 日誌格式: logrus內置了JSONFormatter
和TextFormatter
兩種格式,也能夠經過Formatter
接口定義日誌格式redis
// 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. 輸出文件:api
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
定義了全部日誌打印的方法app
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
接口。curl
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") }
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-hookfunc 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
輸出,會執行os.Exit(1)
。logrus提供RegisterExitHandler
方法,能夠在系統異常時調用一些資源釋放api等,讓應用正確地關閉。func main() { logrus.RegisterExitHandler(func() { fmt.Println("發生了fatal異常,執行關閉文件等工做") }) logrus.Warnln("warn測試") logrus.Fatalln("fatal測試") logrus.Infoln("info測試") //不會執行 }
logger.SetNoLock()
來關閉之。參考