Golang中ZeroLog运行时错误的解决方法
Golang中ZeroLog运行时错误的解决方法
我有一个使用并发goroutine的项目,每个进程都会记录日志信息。我随机遇到了以下异常,可能是log.Msgf或日志事件引起的:
panic: runtime error: index out of range [-1]
goroutine 6190 [running]: github.com/rs/zerolog/internal/json.Encoder.AppendKey(...) /vendor/github.com/rs/zerolog/internal/json/base.go:15 github.com/rs/zerolog.(*Event).Str(0xc003e10ba0?, {0x11a06c2?, 0xc000470ee0?}, {0x0?, 0x1e?}) /vendor/github.com/rs/zerolog/event.go:247 +0x13d
更多关于Golang中ZeroLog运行时错误的解决方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
肖恩——这个问题有最新进展吗? 我已经多次审查了代码,没有发现日志事件在 goroutine 之间被重用的情况。
更多关于Golang中ZeroLog运行时错误的解决方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢 Dean 和 Jeff。 因此,唯一的解决方案是在每个 goroutine 中初始化 logger。
你上面的代码格式有点难以阅读。如果你能在 Go Playground 创建一个可复现的示例,你可以尝试在你试图使用的库的 GitHub 仓库中提交一个问题。
这不会有帮助,因为 ZeroLog 的 JSON 格式化器是全局的。如果你必须以 JSON 格式记录日志,那么你需要使用互斥锁来保护你的日志调用。
我查看了ZeroLog的源代码。它只有一个全局的JSON格式化器,并且没有对该格式化器的访问进行同步。多个goroutine调用需要JSON格式化器的ZeroLog函数,可能会导致JSON格式化器的状态损坏。
func main() {
fmt.Println("hello world")
}
你能展示一下你的代码吗?我在一个issue里找到了类似的错误信息:panic: runtime error: index out of range [-1] seen when logging with many go coroutines running · Issue #422 · rs/zerolog · GitHub
以下是代码的简短版本
main.go。 log.Info().Msgf(“%v”, clientName) – 用于写入有关初始化程序/客户端的消息 … API 请求启动了几个 goroutine – 每个 goroutine 包含以下内容
- log.Debug() 或 log.Msgf()
- defer func(start time.Time) { var e *zerolog.Event switch { case err != nil: e = log.Error() default: e = log.WithLevel(v.Config.Level()) } e. Str(“message”, message). e.AnErr(constants.Err, err).Send() }(time.Now())
这个panic是由于ZeroLog在并发写入时内部状态不一致导致的。当多个goroutine同时写入同一个日志事件或共享的日志对象时,可能会出现索引越界错误。
问题通常发生在以下情况:
- 多个goroutine共享同一个
zerolog.Event实例 - 在调用
Msg()或Msgf()之后继续使用事件对象 - 日志配置(如钩子、采样器)存在竞态条件
解决方案:
// 错误示例 - 共享事件对象
func processConcurrently() {
event := log.Info() // 创建事件
for i := 0; i < 10; i++ {
go func(idx int) {
event.Str("goroutine", fmt.Sprintf("%d", idx)).Msg("processing")
}(i)
}
}
// 正确示例1 - 每个goroutine创建独立事件
func processConcurrently() {
for i := 0; i < 10; i++ {
go func(idx int) {
log.Info().Str("goroutine", fmt.Sprintf("%d", idx)).Msg("processing")
}(i)
}
}
// 正确示例2 - 使用带上下文日志器
func processConcurrently() {
logger := log.With().Str("module", "processor").Logger()
for i := 0; i < 10; i++ {
go func(idx int) {
logger.Info().Str("goroutine", fmt.Sprintf("%d", idx)).Msg("processing")
}(i)
}
}
// 正确示例3 - 确保事件使用后不再访问
func processConcurrently() {
for i := 0; i < 10; i++ {
go func(idx int) {
event := log.Info()
event.Str("goroutine", fmt.Sprintf("%d", idx))
event.Msg("processing")
// 不要在此后继续使用event对象
}(i)
}
}
如果问题仍然存在,可以添加全局恢复机制:
func safeLog(fn func()) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Log panic recovered: %v\n", r)
}
}()
fn()
}
// 使用方式
go func() {
safeLog(func() {
log.Info().Str("key", "value").Msg("message")
})
}()
检查代码中是否存在以下模式:
- 重用已调用
Msg()的事件对象 - 在多个goroutine中操作同一个事件对象
- 使用全局日志配置时的并发修改

