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
更多关于golang最高效低内存占用的JSON日志记录插件库onelog的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
onelog - Go语言高效低内存占用的JSON日志库
onelog是一个专为Go语言设计的高性能、低内存占用的JSON日志库,特别适合需要高频日志记录的场景。下面我将详细介绍它的特点和使用方法。
onelog的核心优势
- 极低的内存分配:通过对象复用和缓冲区管理,大幅减少GC压力
- 高性能:比标准库log和许多流行日志库快2-5倍
- 结构化日志:自动输出为JSON格式,便于日志分析系统处理
- 简洁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("处理支付请求")
性能优化技巧
- 重用Entry对象:避免频繁创建日志条目对象
- 预分配缓冲区:对于已知大小的日志,可以预分配缓冲区
- 异步记录:使用缓冲通道实现异步日志记录
// 异步日志记录示例
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%以上。
最佳实践
- 生产环境:建议使用INFO及以上级别,避免DEBUG日志影响性能
- 敏感信息:避免在日志中记录密码、密钥等敏感信息
- 字段命名:保持字段名一致性,便于日志分析
- 错误处理:总是记录错误发生的上下文信息
onelog通过其精心设计的数据结构和API,在保证功能完整性的同时,实现了极高的性能表现,是Go语言项目中记录结构化日志的优秀选择。