📑 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
缓冲写入buffer
缓冲日志写入gzip
压缩日志文件BackupNum
BackupTime
NEW:
v0.3.0
废弃原来实现的纷乱的各种handler,统一抽象为FlushCloseHandler
SyncCloseHandler
WriteCloserHandler
IOWriterHandler
几个支持不同类型writer的处理器。让构建自定义 Handler 更加简单,内置的handlers也基本上由它们组成。
English instructions please see ./README
go get github.com/gookit/slog
slog
使用非常简单,无需任何配置即可使用。
package main
import (
"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 main
import (
"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).SetTemplate(slog.NamedTemplate)
输出预览:
注意:
slog.TextFormatter
使用模板字符串来格式化输出日志,因此新增字段输出需要同时调整模板。
slog 也内置了 JSON 格式的 Formatter
。若不特别指定,默认都是使用 TextFormatter
格式化日志记录。
package main
import (
"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()
中对日志记录进行处理。日志调度器简易结构:
Processors
Logger --{
Handlers --{ With Formatter
注意:一定要记得将
Handler
,Processor
添加注册到 logger 实例上,日志记录才会经过Handler
处理。
Processor
接口定义如下:
// Processor interface definition
type Processor interface {
// Process record
Process(record *Record)
}
// ProcessorFunc definition
type ProcessorFunc func(record *Record)
// Process record
func (fn ProcessorFunc) Process(record *Record) {
fn(record)
}
你可以使用它在日志
Record
到达Handler
处理之前,对Record进行额外的操作,比如:新增字段,添加扩展信息等
添加 processor 到 logger:
slog.AddProcessor(mypkg.AddHostname())
// or
l := 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
,只需要实现slog.Handler
接口即可。
// Handler interface definition
type 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 interface
type Formatter interface {
Format(record *Record) ([]byte, error)
}
函数包装类型:
// FormatterFunc wrapper definition
type FormatterFunc func(r *Record) ([]byte, error)
// Format a log record
func (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"
更改模板:
myTemplate := "[{{datetime}}] [{{level}}] {{message}}"
f := slog.NewTextFormatter()
f.SetTemplate(myTemplate)
自定义 Processor 和 自定义 Formatter 都比较简单,实现一个对应方法即可。
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 main
import (
"github.com/gookit/slog"
"github.com/gookit/slog/handler"
)
func main() {
l := slog.NewWithHandlers(handler.NewConsoleHandler(slog.AllLevels))
l.Info("message")
}
你只需要实现 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即可使用:
// 添加到默认 logger
slog.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
// 发送日志到系统的syslog
func NewSysLogHandler(priority syslog.Priority, tag string) (*SysLogHandler, error)
// 一个简单的handler实现,输出日志到给定的 io.Writer
func 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 mypkg
import (
"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.log
time-rotate-file.log.20201229_155753
time-rotate-file.log.20201229_155754
按大小进行切割的文件名示例, 格式 filename.log.yMD_000N
. 例如:
size-rotate-file.log
size-rotate-file.log.122915_00001
size-rotate-file.log.122915_00002
启用gzip压缩旧的日志文件:
h1 := handler.MustRotateFile("/tmp/error.log", handler.EveryHour,
handler.WithLogLevels(slog.DangerLevels),
handler.WithCompress(true),
)
size-rotate-file.log.122915_00001.gz
size-rotate-file.log.122915_00002.gz
// Config struct
type Config struct {
// Logfile for write logs
Logfile string `json:"logfile" yaml:"logfile"`
// Levels for log record
Levels []slog.Level `json:"levels" yaml:"levels"`
// UseJSON for format logs
UseJSON bool `json:"use_json" yaml:"use_json"`
// BuffMode buffer缓冲模式. allow: line, bite
BuffMode string `json:"buff_mode" yaml:"buff_mode"`
// BuffSize 启用缓冲区,单位是字节。设置 0 以禁用缓冲
BuffSize int `json:"buff_size" yaml:"buff_size"`
// RotateTime 用于按时间切割文件,单位是秒。
RotateTime rotatefile.RotateTime `json:"rotate_time" yaml:"rotate_time"`
// MaxSize 用于按大小旋转文件,单位是字节。
MaxSize uint64 `json:"max_size" yaml:"max_size"`
// Compress 确定是否应使用 gzip 压缩旋转的日志文件。
// The default is not to perform compression.
Compress bool `json:"compress" yaml:"compress"`
// BackupNum 保留旧文件的最大数量。
// 0 不限制,默认为 20。
BackupNum uint `json:"backup_num" yaml:"backup_num"`
// BackupTime 保留旧文件的最长时间。单位是小时
// 0 不进行清理,默认为一周。
BackupTime uint `json:"backup_time" yaml:"backup_time"`
// RenameFunc build filename for rotate file
RenameFunc func(filepath string, rotateNum uint) string
}
Examples:
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)
使用 handler.Builder
可以方便快速的创建Handler实例。
testFile := "testdata/info.log"
h := handler.NewBuilder().
WithLogfile(testFile).
WithLogLevels(slog.NormalLevels).
WithBuffSize(1024*8).
WithBuffMode(handler.BuffModeBite).
WithRotateTime(rotatefile.Every30Min).
WithCompress(true).
Build()
l := slog.NewWithHandlers(h)
bufwrite
包:
bufwrite.BufIOWriter
通过包装go的 bufio.Writer
额外实现了 Sync(), Close()
方法,方便使用bufwrite.LineWriter
参考go的 bufio.Writer
实现, 可以支持按行刷出缓冲,对于写日志文件更有用rotatefile
包:
rotatefile.Writer
实现对日志文件按大小和指定时间进行自动切割,同时也支持自动清理日志文件
handler/rotate_file
即是通过使用它对日志文件进行切割处理当然,rotatefile.Writer
也可以用在其他日志包上,例如:log
、glog
等等。
例如,在 golang log
上使用 rotatefile:
package main
import (
"log"
"github.com/gookit/slog/rotatefile"
)
func main() {
logFile := "testdata/another_logger.log"
writer, err := rotatefile.NewConfig(logFile).Create()
if err != nil {
panic(err)
}
log.SetOutput(writer)
log.Println("log message")
}
运行单元测试
go test -v ./...
make test-bench
record ad 2022.11.08
% make test-bench
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-3740QM CPU @ 2.70GHz
BenchmarkZapNegative
BenchmarkZapNegative-4 123297997 110.4 ns/op 192 B/op 1 allocs/op
BenchmarkZeroLogNegative
BenchmarkZeroLogNegative-4 891508806 13.36 ns/op 0 B/op 0 allocs/op
BenchmarkPhusLogNegative
BenchmarkPhusLogNegative-4 811990076 14.74 ns/op 0 B/op 0 allocs/op
BenchmarkLogrusNegative
BenchmarkLogrusNegative-4 242633541 49.40 ns/op 16 B/op 1 allocs/op
BenchmarkGookitSlogNegative
>>> BenchmarkGookitSlogNegative-4 29102253 422.8 ns/op 125 B/op 4 allocs/op
BenchmarkZapPositive
BenchmarkZapPositive-4 9772791 1194 ns/op 192 B/op 1 allocs/op
BenchmarkZeroLogPositive
BenchmarkZeroLogPositive-4 13944360 856.8 ns/op 0 B/op 0 allocs/op
BenchmarkPhusLogPositive
BenchmarkPhusLogPositive-4 27839614 431.2 ns/op 0 B/op 0 allocs/op
BenchmarkLogrusPositive
BenchmarkLogrusPositive-4 2621076 4583 ns/op 608 B/op 17 allocs/op
BenchmarkGookitSlogPositive
>>> BenchmarkGookitSlogPositive-4 8908768 1359 ns/op 149 B/op 5 allocs/op
PASS
ok command-line-arguments 149.379s
实现参考了以下项目,非常感谢它们
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。