Go语言 github.com/rs/zerolog的延期评估字段

nnvyjq4y  于 2023-10-14  发布在  Go
关注(0)|答案(2)|浏览(126)

简介

zerolog字段

我在我的golang项目中使用github.com/rs/zerolog
我知道我可以通过使用类似这样的东西向输出添加字段:

  1. package main
  2. import (
  3. "os"
  4. "github.com/rs/zerolog"
  5. )
  6. func main() {
  7. logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
  8. logger.Int("myIntField", 42)
  9. logger.Info("a regular log output") // this log entry will also contain the integer field `myIntField`
  10. }

但是我想要的是在运行时计算logger.Info("a regular log output")行的值,字段myIntField的值。

设置

我有一个生产者/消费者设置(例如,参见https://goplay.tools/snippet/hkoMAwqKcwj),其中包含go例程,我有两个整数,它们是对仍在运行的消费者和生产者go例程的数量进行原子计数。当消费者和生产者被拆除时,我想在运行时显示这些数字。
下面是使用log而不是zerolog时的代码:

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "sync"
  7. "sync/atomic"
  8. )
  9. func main() {
  10. numProducers := int32(3)
  11. numConsumers := int32(3)
  12. producersRunning := numProducers
  13. consumersRunning := numConsumers
  14. var wg sync.WaitGroup
  15. l := log.New(os.Stderr, "", 0)
  16. // producers
  17. for i := int32(0); i < numProducers; i++ {
  18. idx := i
  19. wg.Add(1)
  20. go (func() {
  21. // producer tear down
  22. defer func() {
  23. atomic.AddInt32(&producersRunning, -1)
  24. l.Printf("producer-%3d . producersRunning: %3d\n", idx, producersRunning)
  25. wg.Done()
  26. }()
  27. // this is where the actual producer works is happening
  28. })()
  29. }
  30. // consumers
  31. for i := int32(0); i < numConsumers; i++ {
  32. idx := i
  33. wg.Add(1)
  34. go (func() {
  35. // consumer tear down
  36. defer func() {
  37. atomic.AddInt32(&consumersRunning, -1)
  38. l.Printf("consumer-%3d . consumersRunning: %3d\n", idx, consumersRunning)
  39. wg.Done()
  40. }()
  41. // this is where the actual consumer works is happening
  42. })()
  43. }
  44. fmt.Println("waiting")
  45. wg.Wait()
  46. }

它输出如下内容:

  1. waiting
  2. producer- 1 . producersRunning: 2
  3. producer- 0 . producersRunning: 1
  4. consumer- 1 . consumersRunning: 2
  5. producer- 2 . producersRunning: 0
  6. consumer- 2 . consumersRunning: 1
  7. consumer- 0 . consumersRunning: 0

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

使用zerolog,您可以创建记录器并将其传递给每个go-rountine:

  1. logger := zerolog.New(os.Stderr)
  2. go myConsumer(logger.With().Str("is", "consumer").Logger())
  3. go myProducer(logger.With().Str("is", "producer").Logger())

然后,您可以通过查看每个日志行中的is字段,轻松地在日志中找出消息是来自消费者还是生产者。
但是,如果我想总是在每个日志行中打印当前活动的消费者/生产者的数量,该怎么办?你可能会被诱惑去做这样的事情:

  1. go myConsumer(logger.With().Str("is", "consumer").Int("consumersRunning", consumersRunning).Logger())
  2. go myProducer(logger.With().Str("is", "producer").Int("producersRunning", producersRunning).Logger())

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

摘要

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

  1. func (e *Event) DeferredInt(key string, i func()int) *Event

如果它存在的话,可能会起作用。
有没有其他方法可以达到同样的效果?

可能的解决方法

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

  1. logFunc := func() zerolog.Logger {
  2. return logger.With().Int("runningConcumers", runningConsumers).Logger()
  3. }

然后可以使用logFunc().Msg("hello")创建日志条目。这推迟了runningConsumers的评估,但也为每个日志条目创建了一个日志记录器,感觉有点过分。
到现在为止,我希望我没有混淆你。

7lrncoxx

7lrncoxx1#

你可以添加一个钩子。为每个日志记录事件评估挂钩
https://go.dev/play/p/Q7doafJGaeE

  1. package main
  2. import (
  3. "os"
  4. "github.com/rs/zerolog"
  5. )
  6. type IntHook struct {
  7. Count int
  8. }
  9. func (h *IntHook) Run(e *zerolog.Event, l zerolog.Level, msg string) {
  10. e.Int("count", h.Count)
  11. h.Count++
  12. }
  13. func main() {
  14. var intHook IntHook
  15. log := zerolog.New(os.Stdout).Hook(&intHook)
  16. log.Info().Msg("hello world")
  17. log.Info().Msg("hello world one more time")
  18. }

输出

  1. {"level":"info","count":0,"message":"hello world"}
  2. {"level":"info","count":1,"message":"hello world one more time"}

在调用Hook.Run之间需要指针来保存Count
也许你的HookFunc更好。它是一个无状态函数,每个事件都会调用它。下面是一个函数钩子的例子,它为每个消息调用PRNG:https://go.dev/play/p/xu6aXpUmE0v

  1. package main
  2. import (
  3. "math/rand"
  4. "os"
  5. "github.com/rs/zerolog"
  6. )
  7. func RandomHook(e *zerolog.Event, l zerolog.Level, msg string) {
  8. e.Int("random", rand.Intn(100))
  9. }
  10. func main() {
  11. var randomHook zerolog.HookFunc = RandomHook
  12. log := zerolog.New(os.Stdout).Hook(randomHook)
  13. log.Info().Msg("hello world")
  14. log.Info().Msg("hello world one more time")
  15. }

输出

  1. {"level":"info","random":81,"message":"hello world"}
  2. {"level":"info","random":87,"message":"hello world one more time"}
展开查看全部
jucafojl

jucafojl2#

您可以使用zerolog Hook来实现这一点。钩子是带有Run方法的接口,该方法在事件数据写入给定的io.Writer(在您的示例中为os.Stderr)之前被调用。
下面是一些示例代码:

  1. type counter struct {
  2. name string
  3. value int32
  4. }
  5. func (c *counter) inc() { atomic.AddInt32(&c.value, 1) }
  6. func (c *counter) dec() { atomic.AddInt32(&c.value, -1) }
  7. func (c *counter) get() { atomic.LoadInt32(&c.value) }
  8. func (c *counter) Run(e *zerolog.Event, _ zerolog.Level, _ string) {
  9. e.Int32(c.name, c.get())
  10. }
  11. int main() {
  12. numConsumers, numProducers := 3, 3
  13. consumersRunning := &counter{
  14. name: "consumersRunning",
  15. value: int32(numConsumers),
  16. }
  17. producersRunning := &counter{
  18. name: "producersRunning",
  19. value: int32(numProducers),
  20. }
  21. logger := zerolog.New(os.Stderr)
  22. consumerLogger := logger.With().Str("is", "consumer").Logger().Hook(consumersRunning)
  23. producerLogger := logger.With().Str("is", "producer").Logger().Hook(producersRunning)
  24. // your other code
  25. }

您将使用计数器的incdec方法来修改正在运行的消费者/生产者的数量。

展开查看全部

相关问题