golang零依赖高性能JSON日志记录插件库zkits-logger的使用
Golang零依赖高性能JSON日志记录插件库zkits-logger的使用
关于zkits-logger
zkits-logger是一个零依赖的标准JSON日志库,支持结构化JSON日志,并与标准库兼容。它具有以下特点:
- 不依赖任何第三方库
- 提供可控的调用者和堆栈信息报告
- 提供7种日志级别支持
- 兼容标准库logger *log.Logger
- 支持链式调用和添加多个日志扩展数据
- 提供日志钩子支持
- 内置多种日志格式化器,支持自定义日志格式化器
- 支持按级别将日志输出到不同的写入器
- 支持输出拦截器,轻松劫持和控制日志输出
- 提供支持按大小轮转的日志文件写入器
- 可以控制每条日志的格式和输出写入器
安装
go get -u -v github.com/edoger/zkits-logger
快速开始
import "github.com/edoger/zkits-logger"
// 创建一个指定名称的logger实例
// 允许空名称
log := logger.New("app")
// {"level":"info","message":"Hello.","name":"app","time":"2020-02-20T20:20:20+08:00"}
log.Info("Hello.")
// {"fields":{"field":1},"level":"info","message":"Hello.","name":"app","time":"2020-02-20T20:20:20+08:00"}
log.WithField("field", 1).Info("Hello.")
设计理念
零依赖
我们认为日志记录器应该足够轻量级,因为我们的应用程序已经足够复杂,它们通常有大量的第三方依赖,所以不要给应用程序添加额外的依赖。
无锁设计
为了日志记录器的性能,我们整体采用了无锁设计。因此,我们约定日志记录器的共享数据是只读的,并要求应用程序在初始化日志记录器后不要修改其内部共享数据(格式化器、日志写入器、钩子等)。
推荐做法:
package log
import "github.com/edoger/zkits-logger"
var defaultLogger = logger.New("name")
// 初始化日志记录器
func Initialize(/* 你的配置 */) {
// defaultLogger.SetLevel(Level)
// defaultLogger.SetOutput(Writer)
// defaultLogger.SetFormatter(Formatter)
// ... ...
}
// GetLogger 获取默认日志记录器实例
func GetLogger() logger.Log {
// 屏蔽Logger的设置方法
return defaultLogger.AsLog()
}
链式调用和日志树
链式调用更符合我们编写源代码时的思维方式(从左到右,连续)。我们设计了许多Log.With*
方法来扩展日志的附加字段,通过这些方法可以记录更多的字段信息。
// 创建带有额外属性的Log实例
// 所有SubLog都继承BaseLog的属性(同名属性会被覆盖),
// 并且它们彼此独立,不再与BaseLog关联
SubLog1 := BaseLog.WithField("key", value)
SubLog2 := BaseLog.WithFields(map[string]interface{}{/* 多个字段 */})
SubLog3 := BaseLog.WithContext(ctx)
// 也支持字段键值对
SubLog4 := BaseLog.WithFieldPairs("key1", value1, "key2", value2, /* 更多... */)
/* 更多... */
// 为子模块添加日志记录器,子模块记录的所有日志都有我们添加的额外属性
SubModule.WithLogger(Log.WithField("module", "ModuleName"))
最佳实践:
// 好
Log.WithFields(map[string]interface{}{"field1": var1, "field2": var2})
// 不好
Log.WithField("field1", var1).WithField("field2", var2)
快速定位错误
日志的目的是观察我们应用程序的运行状态。当应用程序出现问题时,日志可以帮助我们快速找到并解决问题的根本原因。
记录错误
// 记录错误时告诉我们文件名和行号
Log.WithCaller().WithError(err).Error("Task failed!") // 将错误记录到单独的字段
Log.WithCaller().Errorf("Task failed: %s.", err)
// 太麻烦?代码太冗长?那我们就全局设置!
// 现在会自动为所有符合级别的日志添加调用者信息
Logger.EnableLevelCaller(ErrorLevel)
Logger.EnableLevelsCaller([]Level{ErrorLevel, FatalLevel, PanicLevel})
崩溃
// 告诉我们崩溃的调用堆栈信息
Log.WithStack().Error("Application crash!")
// 设置Logger.SetExitFunc(nil)禁用自动调用os.Exit
Log.WithStack().Fatal("Application crash!")
// 设置Logger.SetPanicFunc(nil)禁用自动调用panic
Log.WithStack().Panic("Application crash!")
日志钩子
日志钩子允许我们在日志记录之前做一些额外的事情。日志钩子旨在统一处理我们感兴趣的日志事件(按级别)。日志钩子应该足够轻量级,例如:统计日志数量、触发警报等。
// 为我们的应用程序定义钩子
type AppHook struct {}
func (*AppHook) Levels() []Level {
return []Level{ErrorLevel, FatalLevel, PanicLevel}
}
func (*AppHook) Fire(s Summary) error {
// 做一些事情!
return nil
}
// 注册应用程序日志钩子
Logger.AddHook(new(AppHook))
不想定义钩子结构体?
Logger.AddHookFunc([]Level{ErrorLevel}, func(s Summary) error {
// 做一些事情!
return nil
})
有多个钩子需要注册?
// 创建一个钩子集合
bag := NewHookBag()
// 添加我们的钩子
bag.Add(Hook1)
bag.Add(Hook2)
/* ... 更多 ... */
bag.Add(HookN)
// 一次性注册它们
Logger.AddHook(bag)
日志格式化器
日志格式化器用于将日志对象格式化为预期的字符串数据。日志格式化器应尽可能高效,这是日志记录器中最消耗CPU的组件。
JSON格式化器
// 默认的格式化器,如果你不改变它,就是这样
f := DefaultJSONFormatter()
// 创建一个并配置自定义字段名
// 如果full参数为true,它将始终确保所有字段都存在于顶级json对象中
f, err := NewJSONFormatter(keys map[string]string, full bool)
f := MustNewJSONFormatter(keys map[string]string, full bool)
文本格式化器
f := DefaultTextFormatter()
f := DefaultQuoteTextFormatter() // 转义不可见字符
// format参数用于控制日志的格式,它有许多控制参数
// quote参数用于转义日志中的不可见字符
f, err := NewTextFormatter(format string, quote bool)
f := MustNewTextFormatter(format string, quote bool)
控制台格式化器
// 控制台格式化器与文本格式化器非常相似。唯一的区别是
// 我们为不同的日志级别输出不同的控制台颜色,这在
// 从控制台输出日志时非常有用
f := NewConsoleFormatter()
输出拦截器
输出拦截器可以绕过日志记录器绑定的输出写入器,并将日志输出到我们的自定义系统(这些系统通常是动态的或不能包装为io.Writer
)。
// 拦截器参数中的io.Writer是日志记录器绑定的io.Writer实例
Logger.SetOutputInterceptor(func(s Summary, w io.Writer) (int, error) {
// 做一些事情!
return s.Size(), nil
})
格式输出
FormatOutput
允许自定义每条日志的格式和写入器。
// 拦截器参数中的io.Writer是日志记录器绑定的io.Writer实例
Logger.SetFormatOutput(FormatOutputFunc(func(e Entity, b *bytes.Buffer) (io.Writer, error) {
// 1. 格式化日志并写入缓冲区
// 2. 确定并返回需要写入日志的写入器
return os.Stdout, nil
})
日志上下文和追踪
日志上下文是为了支持追踪技术。默认情况下,我们不会对日志绑定的上下文对象做任何操作。如果你需要使用链路追踪技术,你应该定义一个自定义的日志格式化器来处理追踪数据。
// 为日志绑定上下文
Log.WithContext(ctx)
// 注册一个支持解析追踪数据的格式化器
Logger.SetFormatter(FormatterFunc(func(e Entity, b *bytes.Buffer) error {
ctx := e.Context()
// 1. 从Context解析追踪数据
// 2. 格式化日志Entity并将数据写入Buffer
return nil
}))
日志桥接
日志桥接是这里的一个概念。应用程序通常有许多第三方库和组件。为了适应这些库和组件的日志记录器,我们在不引入依赖的情况下构建了一些日志桥接。它们负责将我们的日志记录器转换为这些库和组件的日志记录器。
标准库日志记录器
import log // 标准日志包
// 将我们的日志记录器转换为标准库日志记录器实例
g := Logger.AsStandardLogger() // 返回 *log.Logger 实例
// 你也可以自定义默认日志级别
log.New(NewLevelWriter(DebugLevel, Logger.AsLog()))
许可证
Apache-2.0
更多关于golang零依赖高性能JSON日志记录插件库zkits-logger的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang零依赖高性能JSON日志记录插件库zkits-logger的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
zkits-logger: 零依赖高性能JSON日志记录库
zkits-logger 是一个专为Go语言设计的轻量级、零依赖、高性能的JSON日志记录库,特别适合需要结构化日志输出的现代应用场景。
主要特性
- 零依赖:不依赖任何第三方库
- 高性能:优化的日志记录性能
- 结构化JSON输出:便于日志分析系统处理
- 灵活的日志级别:支持多种日志级别
- 上下文支持:可附加额外字段信息
基本用法
安装
go get github.com/edoger/zkits-logger
简单示例
package main
import (
"github.com/edoger/zkits-logger"
)
func main() {
// 创建一个默认logger
log := logger.NewLogger("app")
// 记录不同级别的日志
log.Debug("This is a debug message")
log.Info("This is an info message")
log.Warn("This is a warning message")
log.Error("This is an error message")
// 带额外字段的日志
log.WithFields(logger.Fields{
"user_id": 123,
"action": "login",
}).Info("User logged in")
}
高级配置
自定义输出
package main
import (
"os"
"github.com/edoger/zkits-logger"
)
func main() {
// 创建自定义配置的logger
log := logger.NewLogger("myapp").
WithOutput(os.Stderr). // 设置输出到stderr
WithLevel(logger.DebugLevel) // 设置日志级别为Debug
log.Debug("Debug message will be shown")
}
日志级别
zkits-logger 支持以下日志级别:
DebugLevel
InfoLevel
WarnLevel
ErrorLevel
FatalLevel
PanicLevel
上下文日志
package main
import (
"github.com/edoger/zkits-logger"
)
func main() {
log := logger.NewLogger("app")
// 创建一个带有固定上下文的logger
contextLog := log.WithFields(logger.Fields{
"service": "payment",
"version": "1.0.0",
})
// 所有使用contextLog记录的日志都会自动包含上述字段
contextLog.Info("Payment processed")
// 可以继续添加临时字段
contextLog.WithField("amount", 99.99).Info("Charge completed")
}
性能优化技巧
- 避免在热路径上创建字段:预先创建带字段的logger
- 合理设置日志级别:生产环境通常使用Info或更高
- 复用logger实例:避免频繁创建新logger
package main
import (
"github.com/edoger/zkits-logger"
)
func main() {
log := logger.NewLogger("app")
serviceLog := log.WithField("service", "api")
// 在循环中使用预配置的logger
for i := 0; i < 100; i++ {
serviceLog.WithField("iteration", i).Debug("Processing")
}
}
实际应用示例
package main
import (
"net/http"
"time"
"github.com/edoger/zkits-logger"
)
var log = logger.NewLogger("webapp").WithField("version", "1.0.0")
func main() {
http.HandleFunc("/", handleRequest)
http.ListenAndServe(":8080", nil)
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 为每个请求创建特定的logger
reqLog := log.WithFields(logger.Fields{
"method": r.Method,
"path": r.URL.Path,
"ip": r.RemoteAddr,
})
defer func() {
// 记录请求处理时间和状态
reqLog.WithField("duration", time.Since(start).String()).
Info("Request completed")
}()
reqLog.Info("Request started")
// 处理请求...
w.Write([]byte("Hello, World!"))
}
总结
zkits-logger 是一个简单而强大的日志记录解决方案,特别适合需要轻量级、高性能JSON日志输出的Go应用程序。它的零依赖特性使得它非常容易集成到各种项目中,而结构化的日志输出则便于后续的日志分析和处理。
通过合理使用上下文和预配置logger,你可以在保持代码简洁的同时,获得丰富的日志信息,这对于调试和监控生产环境中的应用程序非常有帮助。