github.com/rs/zerolog 字段的延迟评估

Kon*_*ine 3 evaluation logging go zerolog defer-keyword

介绍

零日志字段

github.com/rs/zerolog在我的 golang 项目中使用。

我知道我可以使用如下方式将字段添加到输出中:

package main

import (
    "os"

    "github.com/rs/zerolog"
)

func main() {
    logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
    logger.Int("myIntField", 42)
    logger.Info("a regular log output") // this log entry will also contain the integer field `myIntField`
}
Run Code Online (Sandbox Code Playgroud)

但我想要的是在行运行时评估字段logger.Info("a regular log output")的值myIntField是什么。

那个设定

我有一个带有 go-routines 的生产者/消费者设置(例如,请参阅https://goplay.tools/snippet/hkoMAwqKcwj),并且我有两个整数,它们以原子方式计算仍在运行的消费者和生产者 go-routines 的数量。在拆除消费者和生产者后,我想在运行时显示这些数字。

这是使用 log 而不是 Zerolog 时的代码:

package main

import (
    "fmt"
    "log"
    "os"
    "sync"
    "sync/atomic"
)

func main() {
    numProducers := int32(3)
    numConsumers := int32(3)

    producersRunning := numProducers
    consumersRunning := numConsumers

    var wg sync.WaitGroup

    l := log.New(os.Stderr, "", 0)

    // producers
    for i := int32(0); i < numProducers; i++ {
        idx := i
        wg.Add(1)
        go (func() {
            // producer tear down
            defer func() {
                atomic.AddInt32(&producersRunning, -1)
                l.Printf("producer-%3d . producersRunning: %3d\n", idx, producersRunning)
                wg.Done()
            }()

            // this is where the actual producer works is happening
        })()
    }

    // consumers
    for i := int32(0); i < numConsumers; i++ {
        idx := i
        wg.Add(1)
        go (func() {
            // consumer tear down
            defer func() {
                atomic.AddInt32(&consumersRunning, -1)
                l.Printf("consumer-%3d . consumersRunning: %3d\n", idx, consumersRunning)
                wg.Done()
            }()

            // this is where the actual consumer works is happening
        })()
    }

    fmt.Println("waiting")
    wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)

它输出类似这样的内容:

waiting
producer-  1 . producersRunning:   2
producer-  0 . producersRunning:   1
consumer-  1 . consumersRunning:   2
producer-  2 . producersRunning:   0
consumer-  2 . consumersRunning:   1
consumer-  0 . consumersRunning:   0
Run Code Online (Sandbox Code Playgroud)

每个消费者/生产者一个记录器

使用 Zerolog,您可以创建记录器并将它们传递给每个 go-routine:

logger := zerolog.New(os.Stderr)

go myConsumer(logger.With().Str("is", "consumer").Logger())
go myProducer(logger.With().Str("is", "producer").Logger())
Run Code Online (Sandbox Code Playgroud)

is然后,只需查看每个日志行中的字段,您就可以轻松地在日志中找出消息是来自消费者还是生产者。

但是,如果我想始终在每个日志行中打印当前活跃的消费者/生产者的数量怎么办?您可能会想做这样的事情:

go myConsumer(logger.With().Str("is", "consumer").Int("consumersRunning", consumersRunning).Logger())
go myProducer(logger.With().Str("is", "producer").Int("producersRunning", producersRunning).Logger())
Run Code Online (Sandbox Code Playgroud)

但当然,这只会打印创建 go 例程时的瞬时consumersRunning值。producersRunning相反,我希望日志输出能够反映日志输出时的值。

概括

我希望我的问题很清楚。我不确定这是否违反零的概念,但像这样的函数

func (e *Event) DeferredInt(key string, i func()int) *Event
Run Code Online (Sandbox Code Playgroud)

如果它存在的话,可能会起作用。

还有其他方法可以达到同样的效果吗?

潜在的解决方法

我的意思是一种方法可能是logger用这样的函数调用替换变量:

logFunc := func() zerolog.Logger {
  return logger.With().Int("runningConcumers", runningConsumers).Logger()
}
Run Code Online (Sandbox Code Playgroud)

然后可以使用 创建日志条目logFunc().Msg("hello")。这推迟了评估runningConsumers,但也为每个日志条目创建了一个记录器,这感觉有点矫枉过正。

到目前为止,我希望我没有让你感到困惑。

Pak*_*ula 5

您可以添加一个钩子。针对每个日志记录事件评估 Hook

https://go.dev/play/p/Q7doafJGaeE

package main

import (
    "os"

    "github.com/rs/zerolog"
)

type IntHook struct {
    Count int
}

func (h *IntHook) Run(e *zerolog.Event, l zerolog.Level, msg string) {
    e.Int("count", h.Count)
    h.Count++
}

func main() {
    var intHook IntHook
    log := zerolog.New(os.Stdout).Hook(&intHook)

    log.Info().Msg("hello world")
    log.Info().Msg("hello world one more time")
}
Run Code Online (Sandbox Code Playgroud)

输出是

{"level":"info","count":0,"message":"hello world"}
{"level":"info","count":1,"message":"hello world one more time"}
Run Code Online (Sandbox Code Playgroud)

需要Count在调用之间保存指针Hook.Run

可能对你来说aHookFunc更好。它是为每个事件调用的无状态函数。以下是为每条消息调用 PRNG 的函数挂钩示例:https://go.dev/play/p/xu6aXpUmE0v

package main

import (
    "math/rand"
    "os"

    "github.com/rs/zerolog"
)

func RandomHook(e *zerolog.Event, l zerolog.Level, msg string) {
    e.Int("random", rand.Intn(100))
}

func main() {
    var randomHook zerolog.HookFunc = RandomHook
    log := zerolog.New(os.Stdout).Hook(randomHook)

    log.Info().Msg("hello world")
    log.Info().Msg("hello world one more time")
}
Run Code Online (Sandbox Code Playgroud)

输出

{"level":"info","random":81,"message":"hello world"}
{"level":"info","random":87,"message":"hello world one more time"}
Run Code Online (Sandbox Code Playgroud)