克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

slog

GitHub go.mod Go version GoDoc Go Report Card Unit-Tests GitHub tag (latest SemVer) Coverage Status

📑 Go 实现的一个易于使用的,易扩展、可配置的日志库

控制台日志效果:

console-log-all-level

功能特色

  • 简单,无需配置,开箱即用
  • 支持常用的日志级别处理
    • 如: 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 文件夹

输出日志到文件

  • 支持启用 buffer 缓冲日志写入
  • 支持按时间、按大小自动分割文件
  • 支持配置通过 gzip 压缩日志文件
  • 支持清理旧日志文件 配置: BackupNum BackupTime

NEW: v0.3.0 废弃原来实现的纷乱的各种handler,统一抽象为 FlushCloseHandler SyncCloseHandler WriteCloserHandler IOWriterHandler 几个支持不同类型writer的处理器。让构建自定义 Handler 更加简单,内置的handlers也基本上由它们组成。

English

English instructions please see ./README

GoDoc

安装

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 使用模板字符串来格式化输出日志,因此新增字段输出需要同时调整模板。

使用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"

更改模板:

myTemplate := "[{{datetime}}] [{{level}}] {{message}}"

f := slog.NewTextFormatter()
f.SetTemplate(myTemplate)

自定义日志

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

启用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

根据配置快速创建Handler实例

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

使用Builder快速创建Handler实例

使用 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

当然,rotatefile.Writer 也可以用在其他日志包上,例如:logglog 等等。

例如,在 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

Gookit packages

  • gookit/ini Go config management, use INI files
  • gookit/rux Simple and fast request router for golang HTTP
  • gookit/gcli Build CLI application, tool library, running CLI commands
  • gookit/slog Lightweight, extensible, configurable logging library written in Go
  • gookit/color A command-line color library with true color support, universal API methods and Windows support
  • gookit/event Lightweight event manager and dispatcher implements by Go
  • gookit/cache Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached.
  • gookit/config Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags
  • gookit/filter Provide filtering, sanitizing, and conversion of golang data
  • gookit/validate Use for data validation and filtering. support Map, Struct, Form data
  • gookit/goutil Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more
  • More, please see https://github.com/gookit

Acknowledgment

实现参考了以下项目,非常感谢它们

LICENSE

MIT

The MIT License (MIT) Copyright (c) 2016 inhere Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

📑 Lightweight, configurable, extensible logging library written in Go. Support multi level, multi outputs and built-in multi file logger, buffers, clean, rotate-file handling.一个易于使用的,轻量级、可配置、可扩展的日志库。 展开 收起
Go 等 2 种语言
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化