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

1 回复

更多关于golang零依赖高性能JSON日志记录插件库zkits-logger的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


zkits-logger: 零依赖高性能JSON日志记录库

zkits-logger 是一个专为Go语言设计的轻量级、零依赖、高性能的JSON日志记录库,特别适合需要结构化日志输出的现代应用场景。

主要特性

  1. 零依赖:不依赖任何第三方库
  2. 高性能:优化的日志记录性能
  3. 结构化JSON输出:便于日志分析系统处理
  4. 灵活的日志级别:支持多种日志级别
  5. 上下文支持:可附加额外字段信息

基本用法

安装

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")
}

性能优化技巧

  1. 避免在热路径上创建字段:预先创建带字段的logger
  2. 合理设置日志级别:生产环境通常使用Info或更高
  3. 复用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,你可以在保持代码简洁的同时,获得丰富的日志信息,这对于调试和监控生产环境中的应用程序非常有帮助。

回到顶部