316c6420a7520cfd9bd687126ed87d3c5ff00e9b
loggerx
基于 Go 原生 log 封装的日志库:支持按时间切分文件、同步/异步落盘、channel 分目录、Gin 中间件。
log := loggerx.NewLogger(ctx, loggerx.SetDir("./log"), loggerx.SetToConsole())
defer log.Close() // 退出前务必调用,否则最后一批日志(最多 32KB)不会落盘
log.Info(ctx, "hello")
log.Infof(ctx, "hello %s", "world")
用法
创建与选项
log := loggerx.NewLogger(ctx,
loggerx.SetDir("./log"), // 日志目录,默认 ./log
loggerx.SetToConsole(), // 同时输出到控制台
loggerx.SetDays(7), // 保留天数,默认 7;<=0 表示不删除
loggerx.SetTimeZone(time.FixedZone("CST", 8*3600)), // 时区,默认 time.Local
loggerx.SetFileSplit(loggerx.FileSplitTimeE), // 时间切割方式,默认按天
loggerx.SetSizeSplit(64<<20), // 单文件上限 64MB,超过则滚动归档
loggerx.SetCompress(true), // 归档是否压成 .gz,默认 true
loggerx.SetCompressLevel(gzip.BestCompression), // 压缩级别 1~9,默认 BestSpeed
loggerx.SetFlushInterval(200*time.Millisecond), // 定时刷盘,默认关闭
loggerx.SetEscapeHTML(false), // 是否转义 HTML,默认 true
loggerx.SetGID(false), // 是否记录 goroutine id,默认 true
loggerx.SetTraceField("trace_id"), // trace 字段名,默认 trace_id
loggerx.SetErrorToInfo(), // error 是否同时写入 info 日志
loggerx.SetExpandData("app", "order"), // 每条日志追加固定字段
loggerx.SetExtraDriver(f, hooks), // 额外落盘驱动(实现 io.Writer 即可)
loggerx.SetPrintFile(false), // 不写文件,只走驱动
)
定时刷盘:把崩溃丢失窗口压到最小
默认不刷盘时,日志攒在 32KB 内存缓冲里,进程被 kill -9 最多丢 32KB。
开启定时刷盘后,丢失量收敛为「一个间隔内产生的日志量」:
log := loggerx.NewLogger(ctx,
loggerx.SetDir("./log"),
loggerx.SetFlushInterval(200*time.Millisecond), // 每 200ms 刷一次
)
刷盘由后台协程完成,失败只记日志、不会中断写入;Close() 会等它退出。
按大小切割 + 自动压缩归档
log := loggerx.NewLogger(ctx,
loggerx.SetDir("./log"),
loggerx.SetSizeSplit(64<<20), // 每个文件最多 64MB
loggerx.SetCompress(true), // 滚动后压成 .gz(默认开)
)
滚动过程(不阻塞写入):
- 关掉当前句柄(关句柄会先清空缓冲,归档内容因此完整)
- 改名成
2026-09-13_info_3.log(先改名,任何时刻文件都存在,崩溃也不丢) - 后台协程压缩成
2026-09-13_info_3.log.gz,成功后删掉未压缩文件
细节保证:
- 序号全局递增,且跳过已存在的文件,不覆盖历史归档;跨进程重启也从目录里续号
- 单条日志超长(比上限还大)不会死循环,最坏就是该文件超限
- 归档失败(如权限问题)时继续写原文件,宁可文件大一点也不丢日志
Close()会等压缩协程收尾,返回后读归档不会读到半截.gz- 过期清理(
SetDays)同时作用于.log与.gz
文件切割
| 取值 | 目录/文件名形态 |
|---|---|
FileSplitNone |
info.log |
FileSplitTimeA |
2026/09/13/15_info.log |
FileSplitTimeB |
2026/09/13_info.log |
FileSplitTimeC |
2026/09-13_info.log |
FileSplitTimeD |
2026-09-13-15_info.log |
FileSplitTimeE |
2026-09-13_info.log(默认) |
channel 分目录
log.Channel("order").Info(ctx, "下单") // 落到 ./log/order/2026-09-13_info.log
log.Channel("pay").Error(ctx, "支付失败")
同步 / 异步
log.Info(ctx, "默认同步") // 写入内存缓冲(32KB 满才落盘)
log.WriteAsync().Info(ctx, "这条异步") // 交给后台协程消费
loggerx.SetWriteAsync() // 全局异步
异步是有界阻塞队列(容量 1000):队列满时写入方会等待,不会丢日志。
Close() 会等队列排空并把缓冲刷盘。
Gin 中间件
g := gin.Default()
log := loggerx.NewLogger(context.Background(), loggerx.SetToConsole())
defer log.Close()
g.Use(middleware.SetGinTraceIdByLogger(log)) // 读取/生成 trace_id
g.Use(middleware.SetGinParams(log)) // 记录请求与响应
与标准库 log 互通
NewLogger 会把全局 log 的输出接管到该实例,同时继承 io.Writer,可以直接传给任何需要 io.Writer 的地方:
log.SetOutput(loggerx.NewLogger(ctx, loggerx.SetDir("./log")))
落盘行为与保证
- 缓冲:每条日志先写进 32KB 内存缓冲,写满才
write一次系统调用。 - 完整性:以下四种方式都会把缓冲落盘 —— 缓冲写满、
Close()、MustSync()、SetFlushInterval定时器。 - 崩溃语义:进程被
kill -9、panic 未恢复、断电时,最多丢失最后 32KB 未落盘的日志; 开启SetFlushInterval后,丢失量收敛到「一个间隔内产生的日志量」。 - 关闭语义:
Close()之后该实例不再接受写入(返回loggerx: 日志已关闭); 会等异步队列排空、等压缩归档收尾;重复调用安全;不泄漏文件句柄。 - 写失败:写文件失败会重试一次(重开句柄);磁盘满等持续失败时该条日志会丢,
错误通过
io.Writer语义返回给调用方,建议对Write/MustSync/Close的返回值做检查。 - channel 隔离:
Channel("x")的日志落在<dir>/x/子目录,同步与异步模式都成立。
性能
本机(Windows / 16 核)实测,Go 1.26:
| 场景 | 单条耗时 | 分配 |
|---|---|---|
SetGID(false) + 只走驱动 |
3.4 µs | 14 |
| 默认(含 gid,写文件) | 10.8 µs | 15 |
| 并行写入(16 goroutine) | 19.6 µs | 17 |
单条 10.8 µs 中约 4.3 µs 花在采集 goroutine id 上。对延迟敏感、日志量大的场景建议
SetGID(false),可省掉约 40% 开销。开启 SetSizeSplit 后每条日志会多一次
Stat(约 0.6 µs)用于判断是否该滚动;不配置大小切割则没有这笔开销。
开发计划
- 自动清除过期的日志文件(
.log与.gz都清) - 支持日志文件压缩(滚动归档自动 gzip)
- 支持日志文件切割
- 支持日志文件归档到对象存储
- 支持多种文件分割类型
- 按照时间分割
- 按照文件大小分割(
SetSizeSplit) - 按照日志行数分割
- 支持日志级别过滤(
SetFormat("text")尚未生效) - 异步落盘(按实例隔离,不再是全局队列)
- 支持是否转义 HTML
- 支持定时刷盘(
SetFlushInterval)
Languages
Go
100%