介绍一个golang 日志框架logrus
文档介绍
logrus文档
官方案例介绍了如何配置std打印
package main
import (
"os"
log "github.com/sirupsen/logrus"
)
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
// Output to stdout instead of the default stderr
// Can be any io.Writer, see below for File example
log.SetOutput(os.Stdout)
// Only log the warning severity or above.
log.SetLevel(log.WarnLevel)
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(log.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(log.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
// A common pattern is to re-use fields between logging statements by re-using
// the logrus.Entry returned from WithFields()
contextLogger := log.WithFields(log.Fields{
"common": "this is a common field",
"other": "I also should be logged always",
})
contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")
}
仔细观察,如果要输出到文件,只需要在logrus.SetOutput
设置上对应的*os.File
即可
文件日志记录一般都是要切割的,不然文件太大不方便查看
采用的库
https://github.com/lestrrat-go/file-rotatelogs
log.go
package logs
import (
"fmt"
"github.com/sirupsen/logrus"
"log"
)
type Log struct {
*logrus.Entry
LogWriter
}
func (l *Log) Flush() {
l.LogWriter.Flush()
}
type LogConf struct {
Level logrus.Level
AdapterName string
Hook logrus.Hook
}
func InitLog(conf LogConf) *Log {
adapterName := "std"
if conf.AdapterName != "" {
adapterName = conf.AdapterName
}
writer, ok := writerAdapter[adapterName]
if !ok {
adapterName = "std"
writer, _ = writerAdapter[adapterName]
}
fmt.Println("adapterName:" + adapterName)
log :=&Log{
logrus.NewEntry(logrus.New()),
writer(), // 初始化writer
}
// Log as JSON instead of the default ASCII formatter.
log.Logger.SetFormatter(&logrus.JSONFormatter{})
// Output to stdout instead of the default stderr
// Can be any io.Writer, see below for File example
log.Logger.SetOutput(log.LogWriter)
// Only log the warning severity or above.
if conf.Level != 0 {
log.Logger.SetLevel(conf.Level)
} else {
log.Logger.SetLevel(logrus.InfoLevel)
}
if conf.Hook != nil {
log.Logger.AddHook(conf.Hook)
}
// 设置日志打印位置
log.Logger.SetReportCaller(true)
return log
}
type TestHook struct {
}
func (hook *TestHook) Levels() []logrus.Level {
return []logrus.Level{logrus.InfoLevel}
}
func (hook *TestHook) Fire(entry *logrus.Entry) error {
log.Print("hook: %+v", entry)
return nil
}
file_rotate.go
package logs
import (
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"log"
"time"
)
type fileRotateWriter struct {
*rotatelogs.RotateLogs
}
func (frw *fileRotateWriter) Flush() {
frw.Close()
}
func newFileRotateWriter() LogWriter {
writer, err := getRotateLogs()
if err != nil {
log.Fatal(err)
}
return &fileRotateWriter{
writer,
}
}
func getRotateLogs() (*rotatelogs.RotateLogs, error) {
path := LOGPATH
logf, err := rotatelogs.New(
path + ".%Y%m%d%H%M", // 指定文件格式
//rotatelogs.WithLinkName(path), // 将最新文件软链到path,windows环境不支持
rotatelogs.WithMaxAge(time.Second*1800), // 日志最长保存时长
rotatelogs.WithRotationTime(time.Second*60), // 日志切分时间间隔
)
return logf, err
}
func init() {
RegisterInitWriterFunc("file-rotate", newFileRotateWriter)
}
main.go
package main
import (
"logrus-practice/logs"
"github.com/sirupsen/logrus"
)
func main() {
conf := logs.LogConf{
Level: logrus.InfoLevel,
AdapterName: "file-rotate",
Hook: &logs.TestHook{},
}
log := logs.InitLog(conf)
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(logrus.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(logrus.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
// A common pattern is to re-use fields between logging statements by re-using
// the logrus.Entry returned from WithFields()
contextLogger := log.WithFields(logrus.Fields{
"common": "this is a common field",
"other": "I also should be logged always",
})
contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")
}
框架支持,在不同level的日志打印中进行hook。我们可以根据传入的*logrus.Entry
里用户自定义的field进行不同的业务处理
上面代码已经含有hook的使用
type TestHook struct {
}
func (hook *TestHook) Levels() []logrus.Level {
return []logrus.Level{logrus.InfoLevel}
}
func (hook *TestHook) Fire(entry *logrus.Entry) error {
// 可以根据data字段里的field 去做操作
log.Print("hook: %+v", entry)
return nil
}
生成环境中,很多公司都会采用ELK那一套,把日志转发录入到ES中,在kibana按条件搜索