slog - Go 实现的一个易于应用的,易扩大、可配置的日志库
控制台日志成果:
性能特色
- 简略,无需配置,开箱即用
反对罕用的日志级别解决
- 如:
trace
debug
info
notice
warn
error
fatal
panic
- 如:
- 能够任意扩大本人须要的
Handler
Formatter
- 反对同时增加多个
Handler
日志解决,输入日志到不同的中央 反对自定义构建
Handler
处理器- 内置的
handler.Config
handler.Builder
,能够方便快捷的构建想要的日志处理器
- 内置的
反对自定义
Formatter
格式化解决- 内置了
json
text
两个日志记录格式化Formatter
- 内置了
曾经内置了罕用的日志处理器
console
输入日志到控制台,反对色调输入writer
输入日志到指定的io.Writer
file
输入日志到指定文件,可选启用buffer
缓冲写入simple
输入日志到指定文件,无缓冲间接写入文件rotate_file
输入日志到指定文件,并且同时反对按工夫、按大小宰割文件,默认启用buffer
缓冲写入- 更多内置实现请查看 ./handler 文件夹
代码仓库
- Github
- Gitee
装置
go get github.com/gookit/slog
疾速开始
slog
应用非常简单,无需任何配置即可应用。
package mainimport ( "github.com/gookit/slog")func main() { slog.Info("info log message") slog.Warn("warning log message") slog.Infof("info log %s", "message") slog.Debugf("debug %s", "message")}
输入预览:
[2020/07/16 12:19:33] [application] [INFO] [main.go:7] info log message [2020/07/16 12:19:33] [application] [WARNING] [main.go:8] warning log message [2020/07/16 12:19:33] [application] [INFO] [main.go:9] info log message [2020/07/16 12:19:33] [application] [DEBUG] [main.go:10] debug message
启用控制台色彩
您能够在输入控制台日志时启用色彩输入,将会依据不同级别打印不同色调。
package mainimport ( "github.com/gookit/slog")func main() { slog.Configure(func(logger *slog.SugaredLogger) { f := logger.Formatter.(*slog.TextFormatter) f.EnableColor = true }) slog.Trace("this is a simple log message") slog.Debug("this is a simple log message") slog.Info("this is a simple log message") slog.Notice("this is a simple log message") slog.Warn("this is a simple log message") slog.Error("this is a simple log message") slog.Fatal("this is a simple log message")}
输入预览:
更改日志输入款式
下面是更改了默认logger的 Formatter
设置。
你也能够创立本人的logger,并追加 ConsoleHandler
来反对打印日志到控制台:
h := handler.NewConsoleHandler(slog.AllLevels)l := slog.NewWithHandlers()l.Trace("this is a simple log message")l.Debug("this is a simple log message")
更改默认的logger日志输入款式:
h.GetFormatter().(*slog.TextFormatter).Template = slog.NamedTemplate
输入预览:
留神: slog.TextFormatter
应用模板字符串来格式化输入日志,因而新增字段输入须要同时调整模板。
应用JSON格局
slog 也内置了 JSON 格局的 Formatter
。若不特地指定,默认都是应用 TextFormatter
格式化日志记录。
package mainimport ( "github.com/gookit/slog")func main() { // use JSON formatter slog.SetFormatter(slog.NewJSONFormatter()) slog.Info("info log message") slog.Warn("warning log message") slog.WithData(slog.M{ "key0": 134, "key1": "abc", }).Infof("info log %s", "message") r := slog.WithFields(slog.M{ "category": "service", "IP": "127.0.0.1", }) r.Infof("info %s", "message") r.Debugf("debug %s", "message")}
输入预览:
{"channel":"application","data":{},"datetime":"2020/07/16 13:23:33","extra":{},"level":"INFO","message":"info log message"}{"channel":"application","data":{},"datetime":"2020/07/16 13:23:33","extra":{},"level":"WARNING","message":"warning log message"}{"channel":"application","data":{"key0":134,"key1":"abc"},"datetime":"2020/07/16 13:23:33","extra":{},"level":"INFO","message":"info log message"}{"IP":"127.0.0.1","category":"service","channel":"application","datetime":"2020/07/16 13:23:33","extra":{},"level":"INFO","message":"info message"}{"IP":"127.0.0.1","category":"service","channel":"application","datetime":"2020/07/16 13:23:33","extra":{},"level":"DEBUG","message":"debug message"}
架构阐明
Logger
- 日志调度器. 一个logger能够注册多个Handler
,Processor
Record
- 日志记录,每条日志就是一个Record
实例。Processor
- 能够对日志记录进行扩大解决。它在日志Record
被Handler
解决之前调用。- 你能够应用它对
Record
进行额定的操作,比方:新增字段,增加扩大信息等
- 你能够应用它对
Handler
- 日志处理器,每条日志都会通过Handler.Handle()
解决。- 在这里你能够将日志发送到 控制台,文件,近程服务器等等。
Formatter
- 日志记录数据格式化解决。- 通常设置于
Handler
中,能够用于格式化日志记录,将记录转成文本,JSON等,Handler
再将格式化后的数据写入到指定的中央。 Formatter
不是必须的。你能够不应用它,间接在Handler.Handle()
中对日志记录进行解决。
- 通常设置于
日志调度器繁难构造:
ProcessorsLogger --{ Handlers --{ With Formatter
留神:肯定要记得将Handler
,Processor
增加注册到 logger 实例上,日志记录才会通过Handler
解决。
Processor 定义
Processor
接口定义如下:
// Processor interface definitiontype Processor interface { // Process record Process(record *Record)}// ProcessorFunc definitiontype ProcessorFunc func(record *Record)// Process recordfunc (fn ProcessorFunc) Process(record *Record) { fn(record)}
你能够应用它在日志Record
达到Handler
解决之前,对Record进行额定的操作,比方:新增字段,增加扩大信息等
增加 processor 到 logger:
slog.AddProcessor(mypkg.AddHostname())// orl := slog.New()l.AddProcessor(mypkg.AddHostname())
这里应用内置的processor slog.AddHostname
作为示例,它能够在每条日志记录上增加新字段 hostname
。
slog.AddProcessor(slog.AddHostname())slog.Info("message")
输入成果,蕴含新增字段 "hostname":"InhereMac"
:
{"channel":"application","level":"INFO","datetime":"2020/07/17 12:01:35","hostname":"InhereMac","data":{},"extra":{},"message":"message"}
Handler 定义
Handler
接口定义如下:
你能够自定义任何想要的Handler
,只须要实现slog.Handler
接口即可。
// Handler interface definitiontype Handler interface { io.Closer Flush() error // IsHandling Checks whether the given record will be handled by this handler. IsHandling(level Level) bool // Handle a log record. // all records may be passed to this method, and the handler should discard // those that it does not want to handle. Handle(*Record) error}
Formatter 定义
Formatter
接口定义如下:
// Formatter interfacetype Formatter interface { Format(record *Record) ([]byte, error)}
函数包装类型:
// FormatterFunc wrapper definitiontype FormatterFunc func(r *Record) ([]byte, error)// Format a log recordfunc (fn FormatterFunc) Format(r *Record) ([]byte, error) { return fn(r)}
JSON格式化Formatter
type JSONFormatter struct { // Fields exported log fields. Fields []string // Aliases for output fields. you can change export field name. // item: `"field" : "output name"` // eg: {"message": "msg"} export field will display "msg" Aliases StringMap // PrettyPrint will indent all json logs PrettyPrint bool // TimeFormat the time format layout. default is time.RFC3339 TimeFormat string}
Text格式化formatter
默认模板:
const DefaultTemplate = "[{{datetime}}] [{{channel}}] [{{level}}] [{{caller}}] {{message}} {{data}} {{extra}}\n"const NamedTemplate = "{{datetime}} channel={{channel}} level={{level}} [file={{caller}}] message={{message}} data={{data}}\n"
自定义日志
自定义 Processor 和 自定义 Formatter 都比较简单,实现一个对应办法即可。
创立自定义Logger实例
slog.Info, slog.Warn
等办法,应用的默认logger,并且默认输入日志到控制台。
你能够创立一个全新的 slog.Logger
实例:
形式1:
l := slog.New()// add handlers ...h1 := handler.NewConsoleHandler(slog.AllLevels)l.AddHandlers(h1)
形式2:
l := slog.NewWithName("myLogger")// add handlers ...h1 := handler.NewConsoleHandler(slog.AllLevels)l.AddHandlers(h1)
形式3:
package mainimport ( "github.com/gookit/slog" "github.com/gookit/slog/handler")func main() { l := slog.NewWithHandlers(handler.NewConsoleHandler(slog.AllLevels)) l.Info("message")}
创立自定义 Handler
你只须要实现 slog.Handler
接口即可创立自定义 Handler
。你能够通过 slog内置的handler.LevelsWithFormatter
handler.LevelWithFormatter
等片段疾速的组装本人的 Handler。
示例:
应用了handler.LevelsWithFormatter
, 只还须要实现Close, Flush, Handle
办法即可
type MyHandler struct { handler.LevelsWithFormatter Output io.Writer}func (h *MyHandler) Handle(r *slog.Record) error { // you can write log message to file or send to remote.}func (h *MyHandler) Flush() error {}func (h *MyHandler) Close() error {}
将 Handler
增加到 logger即可应用:
// 增加到默认 loggerslog.AddHander(&MyHandler{})// 或者增加到自定义 logger:l := slog.New()l.AddHander(&MyHandler{})
应用内置处理器
./handler 包曾经内置了罕用的日志 Handler,基本上能够满足绝大部分场景。
// 输入日志到控制台func NewConsoleHandler(levels []slog.Level) *ConsoleHandler// 发送日志到email邮箱func NewEmailHandler(from EmailOption, toAddresses []string) *EmailHandler// 发送日志到零碎的syslogfunc NewSysLogHandler(priority syslog.Priority, tag string) (*SysLogHandler, error)// 一个简略的handler实现,输入日志到给定的 io.Writerfunc NewSimpleHandler(out io.Writer, level slog.Level) *SimpleHandler
输入日志到文件:
// 输入日志到指定文件,默认不带缓冲func NewFileHandler(logfile string, fns ...ConfigFn) (h *SyncCloseHandler, err error)// 输入日志到指定文件且格局为JSON,默认不带缓冲func JSONFileHandler(logfile string, fns ...ConfigFn) (*SyncCloseHandler, error)// 带缓冲的输入日志到指定文件func NewBuffFileHandler(logfile string, buffSize int, fns ...ConfigFn) (*SyncCloseHandler, error)
TIP:NewFileHandler
JSONFileHandler
也能够通过传入 fnshandler.WithBuffSize(buffSize)
启用写入缓冲
输入日志到文件并主动切割:
// 依据文件大小进行主动切割func NewSizeRotateFile(logfile string, maxSize int, fns ...ConfigFn) (*SyncCloseHandler, error)// 依据工夫进行主动切割func NewTimeRotateFile(logfile string, rt rotatefile.RotateTime, fns ...ConfigFn) (*SyncCloseHandler, error)// 同时反对配置依据大小和工夫进行切割, 默认设置文件大小是 20M,默认主动宰割工夫是 1小时(EveryHour)。func NewRotateFileHandler(logfile string, rt rotatefile.RotateTime, fns ...ConfigFn) (*SyncCloseHandler, error)
TIP: 通过传入fns ...ConfigFn
能够设置更多选项,比方 日志文件保留工夫, 日志写入缓冲大小等。 具体设置请看handler.Config
构造体
输入日志到文件
输入日志到指定文件,默认不启用 buffer
缓冲写入。 也能够通过传入参数启用缓冲。
package mypkgimport ( "github.com/gookit/slog" "github.com/gookit/slog/handler")func main() { defer slog.MustFlush() // DangerLevels 蕴含: slog.PanicLevel, slog.ErrorLevel, slog.WarnLevel h1 := handler.MustFileHandler("/tmp/error.log", handler.WithLogLevels(slog.DangerLevels)) // NormalLevels 蕴含: slog.InfoLevel, slog.NoticeLevel, slog.DebugLevel, slog.TraceLevel h2 := handler.MustFileHandler("/tmp/info.log", handler.WithLogLevels(slog.NormalLevels)) slog.PushHandler(h1) slog.PushHandler(h2) // add logs slog.Info("info message text") slog.Error("error message text")}
提醒: 如果启用了写入缓冲buffer
,肯定要在程序完结时调用logger.Flush()
刷出缓冲区的内容到文件。
带主动切割的日志处理器
slog/handler
也内置了输入日志到指定文件,并且同时反对按工夫、按大小宰割文件,默认启用 buffer
缓冲写入
func Example_rotateFileHandler() { h1 := handler.MustRotateFile("/tmp/error.log", handler.EveryHour, handler.WithLogLevels(slog.DangerLevels)) h2 := handler.MustRotateFile("/tmp/info.log", handler.EveryHour, handler.WithLogLevels(slog.NormalLevels)) slog.PushHandler(h1) slog.PushHandler(h2) // add logs slog.Info("info message") slog.Error("error message")}
按工夫切割文件示例:
time-rotate-file.logtime-rotate-file.log.20201229_155753time-rotate-file.log.20201229_155754
按大小进行切割的文件名示例, 格局 filename.log.yMD_000N
. 例如:
size-rotate-file.logsize-rotate-file.log.122915_00001size-rotate-file.log.122915_00002
依据配置疾速创立Handler实例
testFile := "testdata/error.log" h := handler.NewEmptyConfig(). With( handler.WithLogfile(testFile), handler.WithBuffSize(1024*8), handler.WithLogLevels(slog.DangerLevels), handler.WithBuffMode(handler.BuffModeBite), ). CreateHandler() l := slog.NewWithHandlers(h)
应用Builder疾速创立Handler实例
testFile := "testdata/info.log" h := handler.NewBuilder(). With( handler.WithLogfile(testFile), handler.WithBuffSize(1024*8), handler.WithLogLevels(slog.NormalLevels), handler.WithBuffMode(handler.BuffModeBite), ). Build() l := slog.NewWithHandlers(h)
扩大工具包
bufwrite
包:
bufwrite.BufIOWriter
通过包装go的bufio.Writer
额定实现了Sync(), Close()
办法,方便使用bufwrite.LineWriter
参考go的bufio.Writer
实现, 能够反对按行刷出缓冲,对于写日志文件更有用
rotatefile
包:
rotatefile.Writer
实现对日志文件按大小和指定工夫进行主动切割,同时也反对主动清理日志文件handler/rotate_file
即是通过应用它对日志文件进行切割解决
测试以及性能
单元测试
运行单元测试
go test -v ./...
性能压测
make test-bench
record ad 2022.04.27
% make test-benchgoos: darwingoarch: amd64cpu: Intel(R) Core(TM) i7-3740QM CPU @ 2.70GHzBenchmarkZapNegativeBenchmarkZapNegative-4 128133166 93.97 ns/op 192 B/op 1 allocs/opBenchmarkZeroLogNegativeBenchmarkZeroLogNegative-4 909583207 13.41 ns/op 0 B/op 0 allocs/opBenchmarkPhusLogNegativeBenchmarkPhusLogNegative-4 784099310 15.24 ns/op 0 B/op 0 allocs/opBenchmarkLogrusNegativeBenchmarkLogrusNegative-4 289939296 41.60 ns/op 16 B/op 1 allocs/opBenchmarkGookitSlogNegative> BenchmarkGookitSlogNegative-4 29131203 417.4 ns/op 125 B/op 4 allocs/opBenchmarkZapPositiveBenchmarkZapPositive-4 9910075 1219 ns/op 192 B/op 1 allocs/opBenchmarkZeroLogPositiveBenchmarkZeroLogPositive-4 13966810 871.0 ns/op 0 B/op 0 allocs/opBenchmarkPhusLogPositiveBenchmarkPhusLogPositive-4 26743148 446.2 ns/op 0 B/op 0 allocs/opBenchmarkLogrusPositiveBenchmarkLogrusPositive-4 2658482 4481 ns/op 608 B/op 17 allocs/opBenchmarkGookitSlogPositive> BenchmarkGookitSlogPositive-4 8349562 1441 ns/op 165 B/op 6 allocs/opPASSok command-line-arguments 146.669s