golang最高效低内存占用的JSON日志记录插件库onelog的使用

Golang最高效低内存占用的JSON日志记录插件库onelog的使用

Onelog是一个非常简单但非常高效的JSON日志记录库。它是目前最快的JSON日志记录器之一,同时也是内存分配最少的日志记录器之一。

安装

go get github.com/francoispqt/onelog

基本使用

import "github.com/francoispqt/onelog"

func main() {
    // 创建一个新的Logger
    // 第一个参数是io.Writer
    // 第二个参数是日志级别,是一个整数
    logger := onelog.New(
        os.Stdout, 
        onelog.ALL, // onelog.DEBUG|onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL的快捷方式
    )
    logger.Info("hello world !") // {"level":"info","message":"hello world"}
}

日志级别

日志级别是映射到字符串的整数。日志记录器将使用高效的位运算检查是否启用了级别,如果禁用,它会立即返回,这使得onelog在运行禁用日志记录时成为最快的,分配为0,操作时间小于1ns/op。

创建日志记录器时,必须使用|运算符设置不同的级别。

可用级别

  • onelog.DEBUG
  • onelog.INFO
  • onelog.WARN
  • onelog.ERROR
  • onelog.FATAL
logger := onelog.New(
    os.Stdout, 
    onelog.INFO|onelog.WARN,
)

动态设置级别示例

var logger *onelog.Logger

func init() {
    // 如果是调试模式,启用DEBUG级别
    if os.Getenv("DEBUG") != "" {
        logger = onelog.New(
            os.Stdout, 
            onelog.ALL, // onelog.DEBUG|onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL的快捷方式
        )
        return
    }
    logger = onelog.New(
        os.Stdout, 
        onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL,
    )
}

修改级别文本值

onelog.LevelText(onelog.INFO, "INFO")

钩子(Hook)

你可以定义一个钩子,它将在每条日志消息上运行。

logger := onelog.New(
    os.Stdout, 
    onelog.ALL,
)
logger.Hook(func(e onelog.Entry) {
    e.String("time", time.Now().Format(time.RFC3339))
})
logger.Info("hello world !") // {"level":"info","message":"hello world","time":"2018-05-06T02:21:01+08:00"}

上下文(Context)

上下文允许强制使用分组格式,其中来自所有日志记录方法(With、Info、Debug、InfoWith、InfoWithEntry等)的所有日志字段键值对(除了使用logger.Hook的值)将被包含在作为其键提供的给定上下文名称中。

logger := onelog.NewContext(
    os.Stdout, 
    onelog.INFO|onelog.WARN,
    "params"
)

logger.InfoWithFields("breaking news !", func(e onelog.Entry) {
    e.String("userID", "123455")
}) 

// {"level":"info","message":"breaking news !", "params":{"userID":"123456"}}

从父日志记录器继承上下文

parentLogger := onelog.New(
    os.Stdout, 
    onelog.INFO|onelog.WARN,
)

logger := parentLogger.WithContext("params")
logger.InfoWithFields("breaking news !", func(e onelog.Entry) {
    e.String("userID", "123455")
}) 

// {"level":"info","message":"breaking news !", "params":{"userID":"123456"}}

日志记录

无额外字段的日志记录

logger := onelog.New(
    os.Stdout, 
    onelog.ALL,
)
logger.Debug("i'm not sure what's going on") // {"level":"debug","message":"i'm not sure what's going on"}
logger.Info("breaking news !") // {"level":"info","message":"breaking news !"}
logger.Warn("beware !") // {"level":"warn","message":"beware !"}
logger.Error("my printer is on fire") // {"level":"error","message":"my printer is on fire"}
logger.Fatal("oh my...") // {"level":"fatal","message":"oh my..."}

带额外字段的日志记录

logger := onelog.New(
    os.Stdout, 
    onelog.ALL,
)

logger.DebugWithFields("i'm not sure what's going on", func(e onelog.Entry) {
    e.String("string", "foobar")
    e.Int("int", 12345)
    e.Int64("int64", 12345)
    e.Float("float64", 0.15)
    e.Bool("bool", true)
    e.Err("err", errors.New("someError"))
    e.ObjectFunc("user", func(e Entry) {
        e.String("name", "somename")
    })
}) 
// {"level":"debug","message":"i'm not sure what's going on","string":"foobar","int":12345,"int64":12345,"float64":0.15,"bool":true,"err":"someError","user":{"name":"somename"}}

链式语法

logger.InfoWith("foo bar").
    Int("testInt", 1).
    Int64("testInt64", 2).
    Float("testFloat", 1.15234).
    String("testString", "string").
    Bool("testBool", true).
    ObjectFunc("testObj", func(e Entry) {
        e.Int("testInt", 100)
    }).
    Object("testObj2", testObj). // 实现gojay.MarshalerJSONObject
    Array("testArr", testArr). // 实现gojay.MarshalerJSONArray
    Err("testErr", errors.New("my printer is on fire !")).
    Write() // 别忘了调用这个方法!

累积上下文

你可以创建一个带有一些累积上下文的日志记录器,这些上下文将包含在此日志记录器创建的所有日志中。

logger := onelog.New(
    os.Stdout, 
    onelog.ALL,
).With(func(e onelog.Entry) {
    e.String("userID", "123456")
})

logger.Info("user logged in") // {"level":"info","message":"user logged in","userID":"123456"}

logger.Debug("wtf?") // {"level":"debug","message":"wtf?","userID":"123456"}

logger.ErrorWithFields("Oops", func(e onelog.Entry) {
    e.String("error_code", "ROFL")
}) // {"level":"error","message":"oops","userID":"123456","error_code":"ROFL"}

修改全局配置

修改级别文本值

onelog.LevelText(onelog.INFO, "INFO")

修改消息键

onelog.MsgKey("msg")

修改级别键

onelog.LevelKey("lvl")

注意:这些更改是全局的(影响日志记录器的所有实例)。此外,这些函数应该在运行时只调用一次,以避免任何数据竞争问题。

性能基准

Onelog在性能方面表现优异,特别是在禁用日志记录时具有零分配和极低的延迟。以下是部分基准测试结果:

禁用日志记录

ns/op bytes/op allocs/op
Zap 8.73 0 0
zerolog 2.45 0 0
logrus 12.1 16 1
onelog 0.74 0 0

带字段的禁用日志记录

ns/op bytes/op allocs/op
Zap 208 768 5
zerolog 68.7 128 4
logrus 721 1493 12
onelog 1.31 0 0
onelog-chain 68.2 0 0

记录基本消息

ns/op bytes/op allocs/op
Zap 205 0 0
zerolog 135 0 0
logrus 1256 1554 24
onelog 84.8 0 0

Onelog是一个高效、低内存占用的JSON日志记录解决方案,特别适合性能敏感的应用场景。


更多关于golang最高效低内存占用的JSON日志记录插件库onelog的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang最高效低内存占用的JSON日志记录插件库onelog的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


onelog - Go语言高效低内存占用的JSON日志库

onelog是一个专为Go语言设计的高性能、低内存占用的JSON日志库,特别适合需要高频日志记录的场景。下面我将详细介绍它的特点和使用方法。

onelog的核心优势

  1. 极低的内存分配:通过对象复用和缓冲区管理,大幅减少GC压力
  2. 高性能:比标准库log和许多流行日志库快2-5倍
  3. 结构化日志:自动输出为JSON格式,便于日志分析系统处理
  4. 简洁API:提供链式调用,代码可读性高

基本使用示例

package main

import (
	"github.com/francoispqt/onelog"
)

func main() {
	// 创建日志器,输出到标准输出
	logger := onelog.New(os.Stdout, onelog.ALL)
	
	// 基本日志记录
	logger.Info("服务启动") // 输出: {"level":"info","message":"服务启动"}
	
	// 带上下文的日志
	logger.WithFields(
		func(e onelog.Entry) {
			e.String("user", "john_doe")
			e.Int("attempt", 3)
			e.Bool("success", true)
		},
	).Info("用户登录")
	// 输出: {"level":"info","message":"用户登录","user":"john_doe","attempt":3,"success":true}
	
	// 错误日志
	logger.WithFields(
		func(e onelog.Entry) {
			e.String("error", err.Error())
			e.String("file", "main.go")
			e.Int("line", 42)
		},
	).Error("处理请求失败")
}

高级配置

// 自定义配置日志器
logger := onelog.New(
	os.Stdout, 
	onelog.INFO|onelog.WARN|onelog.ERROR, // 只记录INFO及以上级别
	onelog.WithLevelFieldName("severity"), // 自定义级别字段名
	onelog.WithTimestamp(),                // 添加时间戳
	onelog.WithTimeFormat(time.RFC3339),   // 自定义时间格式
)

// 使用钩子添加全局字段
logger.WithHook(func(e onelog.Entry) {
	e.String("service", "payment-gateway")
	e.String("version", "1.0.0")
})

// 现在所有日志都会包含service和version字段
logger.Info("处理支付请求")

性能优化技巧

  1. 重用Entry对象:避免频繁创建日志条目对象
  2. 预分配缓冲区:对于已知大小的日志,可以预分配缓冲区
  3. 异步记录:使用缓冲通道实现异步日志记录
// 异步日志记录示例
type asyncLogger struct {
	logger *onelog.Logger
	ch     chan func()
}

func NewAsyncLogger(w io.Writer, level uint8) *asyncLogger {
	al := &asyncLogger{
		logger: onelog.New(w, level),
		ch:     make(chan func(), 1000), // 缓冲1000条日志
	}
	go al.process()
	return al
}

func (a *asyncLogger) process() {
	for f := range a.ch {
		f()
	}
}

func (a *asyncLogger) Info(msg string) {
	a.ch <- func() {
		a.logger.Info(msg)
	}
}

// 使用方式
asyncLog := NewAsyncLogger(os.Stdout, onelog.ALL)
asyncLog.Info("异步日志记录")

与标准库对比

// 标准库日志
func BenchmarkStdLog(b *testing.B) {
	for i := 0; i < b.N; i++ {
		log.Printf("processing item %d", i)
	}
}

// onelog日志
func BenchmarkOnelog(b *testing.B) {
	logger := onelog.New(io.Discard, onelog.ALL)
	for i := 0; i < b.N; i++ {
		logger.Info("processing item").Int("item", i).Write()
	}
}

测试结果通常显示onelog比标准库快2-3倍,且内存分配次数少90%以上。

最佳实践

  1. 生产环境:建议使用INFO及以上级别,避免DEBUG日志影响性能
  2. 敏感信息:避免在日志中记录密码、密钥等敏感信息
  3. 字段命名:保持字段名一致性,便于日志分析
  4. 错误处理:总是记录错误发生的上下文信息

onelog通过其精心设计的数据结构和API,在保证功能完整性的同时,实现了极高的性能表现,是Go语言项目中记录结构化日志的优秀选择。

回到顶部