关于golang:slog-Go-实现的一个易于使用的易扩展可配置的日志库

7次阅读

共计 10657 个字符,预计需要花费 27 分钟才能阅读完成。

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 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).Template = slog.NamedTemplate

输入预览:

留神:slog.TextFormatter 应用模板字符串来格式化输入日志,因而新增字段输入须要同时调整模板。

应用 JSON 格局

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 – 能够对日志记录进行扩大解决。它在日志 RecordHandler 解决之前调用。

    • 你能够应用它对 Record 进行额定的操作,比方:新增字段,增加扩大信息等
  • Handler – 日志处理器,每条日志都会通过 Handler.Handle() 解决。

    • 在这里你能够将日志发送到 控制台,文件,近程服务器等等。
  • Formatter – 日志记录数据格式化解决。

    • 通常设置于 Handler 中,能够用于格式化日志记录,将记录转成文本,JSON 等,Handler 再将格式化后的数据写入到指定的中央。
    • Formatter 不是必须的。你能够不应用它, 间接在 Handler.Handle() 中对日志记录进行解决。

日志调度器繁难构造

          Processors
Logger --{Handlers --{ With Formatter

留神:肯定要记得将 Handler, Processor 增加注册到 logger 实例上,日志记录才会通过 Handler 解决。

Processor 定义

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 接口定义如下:

你能够自定义任何想要的 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 接口定义如下:

// 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"

自定义日志

自定义 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 main

import (
    "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 即可应用:

// 增加到默认 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 也能够通过传入 fns handler.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

依据配置疾速创立 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-bench
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-3740QM CPU @ 2.70GHz
BenchmarkZapNegative
BenchmarkZapNegative-4                  128133166               93.97 ns/op          192 B/op          1 allocs/op
BenchmarkZeroLogNegative
BenchmarkZeroLogNegative-4              909583207               13.41 ns/op            0 B/op          0 allocs/op
BenchmarkPhusLogNegative
BenchmarkPhusLogNegative-4              784099310               15.24 ns/op            0 B/op          0 allocs/op
BenchmarkLogrusNegative
BenchmarkLogrusNegative-4               289939296               41.60 ns/op           16 B/op          1 allocs/op
BenchmarkGookitSlogNegative
> BenchmarkGookitSlogNegative-4           29131203               417.4 ns/op           125 B/op          4 allocs/op
BenchmarkZapPositive
BenchmarkZapPositive-4                   9910075              1219 ns/op             192 B/op          1 allocs/op
BenchmarkZeroLogPositive
BenchmarkZeroLogPositive-4              13966810               871.0 ns/op             0 B/op          0 allocs/op
BenchmarkPhusLogPositive
BenchmarkPhusLogPositive-4              26743148               446.2 ns/op             0 B/op          0 allocs/op
BenchmarkLogrusPositive
BenchmarkLogrusPositive-4                2658482              4481 ns/op             608 B/op         17 allocs/op
BenchmarkGookitSlogPositive
> BenchmarkGookitSlogPositive-4            8349562              1441 ns/op             165 B/op          6 allocs/op
PASS
ok      command-line-arguments  146.669s
正文完
 0