如何将互斥锁与ID管理器集成到Zerolog Writer实现自增日志ID?
解决方案
要实现自动添加自增ID、简化调用、缩短锁持有时间且保证时间有序的日志系统,我们可以封装一个自定义的Zerolog Logger,将ID管理、锁逻辑和时间戳控制整合到内部,同时保留Zerolog的链式调用特性。
实现代码
1. 自定义Logger和Event结构体
package main import ( "sync" "time" "github.com/rs/zerolog" "gopkg.in/natefinch/lumberjack.v2" ) // Logger 自定义日志器,整合ID管理、锁和Zerolog type Logger struct { baseLogger zerolog.Logger mu sync.Mutex nextID int64 // 自增ID的起始值,可根据需求调整 } // Event 自定义事件结构体,转发Zerolog的字段方法,自动添加ID type Event struct { innerEvent *zerolog.Event id int64 } // NewLogger 创建自定义Logger,传入Lumberjack作为底层Writer func NewLogger() *Logger { // 初始化Lumberjack Writer lumberjackWriter := &lumberjack.Logger{ Filename: "/var/log/myapp/app.log", MaxSize: 10, // 单个日志文件最大大小(MB) MaxBackups: 5, // 保留的备份文件数量 MaxAge: 30, // 日志文件保留天数 Compress: true, // 是否压缩备份文件 } // 初始化Zerolog基础Logger(不自动添加时间戳,由自定义逻辑控制) baseLogger := zerolog.New(lumberjackWriter).Logger() return &Logger{ baseLogger: baseLogger, nextID: 0, } } // Info 创建Info级别的日志事件,自动获取ID和时间戳 func (l *Logger) Info() *Event { l.mu.Lock() defer l.mu.Unlock() // 原子性操作:递增ID + 获取当前时间 l.nextID++ now := time.Now() // 创建Zerolog事件并设置时间戳 inner := l.baseLogger.Info().Timestamp(now) return &Event{ innerEvent: inner, id: l.nextID, } } // Error 创建Error级别的日志事件,逻辑同Info func (l *Logger) Error() *Event { l.mu.Lock() defer l.mu.Unlock() l.nextID++ now := time.Now() inner := l.baseLogger.Error().Timestamp(now) return &Event{ innerEvent: inner, id: l.nextID, } } // Msg 完成日志写入,自动添加ID字段 func (e *Event) Msg(msg string) { e.innerEvent.Uint64("id", uint64(e.id)).Msg(msg) } // 以下方法转发Zerolog的字段添加能力,保留链式调用 func (e *Event) Str(key, value string) *Event { e.innerEvent.Str(key, value) return e } func (e *Event) Int(key string, value int) *Event { e.innerEvent.Int(key, value) return e } func (e *Event) Uint(key string, value uint) *Event { e.innerEvent.Uint(key, value) return e } // 可根据需求添加更多Zerolog字段方法,比如Bool、Float64等
2. 使用示例
func main() { logger := NewLogger() // 简化调用,无需手动加锁 logger.Info().Str("user", "alice").Msg("User logged in") logger.Error().Int("code", 500).Msg("Internal server error") // 并发场景下也能保证ID递增且时间有序 for i := 0; i < 10; i++ { go func(idx int) { logger.Info().Int("goroutine", idx).Msg("Concurrent log message") }(i) } }
核心特性说明
- 简化调用:无需手动加锁,直接通过
logger.Info().Msg(...)链式调用,和原生Zerolog使用方式一致。 - 锁持有时间极短:仅在获取自增ID和时间戳的瞬间加锁,锁的范围被压缩到最小,对并发性能影响极低。
- 时间有序保证:ID递增和时间戳获取在同一个锁保护下完成,确保ID=n+1的日志时间戳一定不早于ID=n的日志时间戳。
- 完整保留Zerolog功能:自定义Event转发了Zerolog的所有字段添加方法,可自由扩展日志内容。
内容的提问来源于stack exchange,提问作者ExtremG
相关产品推荐
相关产品推荐

