我们日常使用 go 提供的 log
标准库可能过于简单了.因此有了很多基于标准库二次开发的第三方日志库.logrus
就是其中一个.logrus
完全兼容标准的log
库,还支持文本、JSON 两种日志输出格式.很多知名的开源项目都使用了这个库,如 docker
, etcd
等.
概述 logrus
的使用非常简单,与标准库log类似.但 logrus
支持更多的日志级别: Trace
,Debug
,Info
,Warn
,Error
,Fatal
,Panic
.默认为Info
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ( "github.com/sirupsen/logrus" ) func main () { logrus.Debug("debug msg" ) logrus.Info("info msg" ) logrus.Warn("warn msg" ) logrus.Error("error msg" ) }
可以看到输出内容很简陋.因此我们可以对 logrus
进行定制.
常用函数 logrus
提供了众多函数对日志的输出格式进行定制.如:
日志的输出级别
日志使用文本格式还是 JSON 格式
日志带有时间戳
日志输出到指定文件中或直接输出到日志处理工具中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func New () *Logger func StandardLogger () *Logger func SetFormatter (formatter Formatter) func SetLevel (level Level) func SetOutput (out io.Writer) func SetReportCaller (include bool ) func WithField (key string , value interface {}) *Entry func WithFields (fields Fields) *Entry
以上大多都是直接调用包函数来完成相关配置的.其实在 logrus
包中有一个默认的 logrus.Logger
对象.所有其他的包函数都是调用其相关方法实现的.其定义方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 func New () *Logger { return &Logger{ Out: os.Stderr, Formatter: new (TextFormatter), Hooks: make (LevelHooks), Level: InfoLevel, ExitFunc: os.Exit, ReportCaller: false , } }
日志格式 logrus
提供了 JSONFormatter
和 TextFormatter
来分别实现日志 JSON 和 文本 格式的输出.它们都实现了 Formatter
接口.因此,后续我们也可以自己编写对应的 Formatter
对象
JSONFormatter
和 TextFormatter
中都定义了日志输出的各种参数.可以通过配置这些成员变量来配置日志的格式.如下:
1 2 3 4 5 6 TextFormatter{ FullTimestamp: true , TimestampFormat: "2006-01-02 15:04:05" , }
日志输出 同时,对于输出位置来说,我们可以使用 SetOutput
函数进行修改,将日志输出到文件中.如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import ( "bytes" "io" "log" "os" "github.com/sirupsen/logrus" ) func main () { stdout := os.Stdout logfile, 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(stdout, logfile)) logrus.Info("info msg" ) }
添加字段 有时,我们会添加一字段到日志中
1 2 3 4 5 6 7 8 9 10 11 import ( "github.com/sirupsen/logrus" ) func main () { logrus.WithFields(logrus.Fields{ "name" : "dj" , "age" : 18 , }).Info("info msg" ) }
除了 logrus
包中定义的两种 Formatter
外,我们可以自定义 Formatter
.只需要实现如下接口即可.
1 2 3 type Formatter interface { Format(*Entry) ([]byte , error) }
如下是文档 中提供的一种自定义的 Formatter
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type MyJSONFormatter struct {} func (f *MyJSONFormatter) Format (entry *logrus.Entry) ([]byte , error) { serialized, err := json.Marshal(entry.Data) if err != nil { return nil , fmt.Errorf("Failed to marshal fields to JSON, %w" , err) } return append (serialized, '\n' ), nil } func main () { logrus.SetFormatter(new (MyJSONFormatter)) logrus.WithFields(logrus.Fields{ "name" : "MyJSONFormatter" , "msg" : "message" , }).Info("my json format" ) }
从上面的示例可以看到, Format()
接口方法中包含 Entry
对象.其定义如下
1 2 3 4 5 6 7 8 9 10 11 type Entry struct { Logger *Logger Data Fields Time time.Time Level Level Caller *runtime.Frame Message string Buffer *bytes.Buffer Context context.Context }
hooks 有这么一种需求,对不同的级别的日志,我希望能够输出到不同的位置.该怎么实现呢? logrus
提供了对日志级别的 hooks
机制.它可以对不同级别日志添加不同的 hooks
.从而实现不同的需求.
logrus
提供了两种 hooks. writer
和 SyslogHook
.可以实现不同的功能
writer 参见 github 的一个示例,将不同级别的日志写入到不同的日志文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package mainimport ( log "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/writer" "io/ioutil" "os" ) func main () { log.SetOutput(ioutil.Discard) log.SetFormatter(&log.TextFormatter{ TimestampFormat: "2006-01-02 15:03:04" , FullTimestamp: true , }) errlog, err := os.OpenFile("err.log" , os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644 ) if err != nil { log.Error(err) } defer errlog.Close() infolog, err := os.OpenFile("info.log" , os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644 ) if err != nil { log.Error(err) } defer infolog.Close() log.AddHook(&writer.Hook{ Writer: errlog, LogLevels: []log.Level{ log.PanicLevel, log.FatalLevel, log.ErrorLevel, log.WarnLevel, }, }) log.AddHook(&writer.Hook{ Writer: infolog, LogLevels: []log.Level{ log.InfoLevel, log.DebugLevel, }, }) log.Info("This will go to info.log" ) log.Warn("This will go to err.log" ) }
syslogHook 参见 github 将日志输出到 syslog
第三方日志处理工具中.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import ( "log/syslog" "github.com/sirupsen/logrus" lSyslog "github.com/sirupsen/logrus/hooks/syslog" ) func main () { log := logrus.New() hook, err := lSyslog.NewSyslogHook("udp" , "localhost:514" , syslog.LOG_INFO, "" ) if err == nil { log.Hooks.Add(hook) } }
其它类似的 hooks
还有
参考: