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

8 回复

肖恩——这个问题有最新进展吗? 我已经多次审查了代码,没有发现日志事件在 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")
}

以下是代码的简短版本

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同时写入同一个日志事件或共享的日志对象时,可能会出现索引越界错误。

问题通常发生在以下情况:

  1. 多个goroutine共享同一个zerolog.Event实例
  2. 在调用Msg()Msgf()之后继续使用事件对象
  3. 日志配置(如钩子、采样器)存在竞态条件

解决方案:

// 错误示例 - 共享事件对象
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中操作同一个事件对象
  • 使用全局日志配置时的并发修改
回到顶部