rs / zerolog

Zero Allocation JSON Logger
MIT License
10.41k stars 567 forks source link

Code example for 'Thread-safe, lock-free, non-blocking writer' in Readme.md seems out of date #468

Open knmiecc opened 2 years ago

knmiecc commented 2 years ago

I tried copying the code example stated here and noticed that in the (to my understanding) latest version of go-diode there doesn't seem to be a NewWriter function anymore. In general I get the impression that the usage of that diodes package has fundamentally changed, which means a new code example bit should be written for that section, or the section be edited to make people aware of the change for the time being.

I have not yet understood the way diode works now, so I can't make a suggestion yet on how to fix it that readme section.

pscheid92 commented 1 year ago

I stumbled over that section in the README, too, and it is fortunately not out of date; just confusing. To understand it better, check the Context for Proposal 3 section below 😇

Proposals

  1. Add import "github.com/rs/zerolog/diode" to the snippet.
  2. Add some code snippets that reliably show some output.
  3. Add a high-level explanation of how we use go-diode and what that means for users.

Context for Proposal 1

[...] noticed that in the (to my understanding) latest version of go-diode there doesn't seem to be a NewWriter function anymore.

One doesn't find a NewWriter function in go-diode because there is none. We provide it through the github.com/rs/zerolog/diode module.

As a hint, we could use an executable snippet with the import statements:

package main

import (
    "os"
    "time"
    "fmt"

    "github.com/rs/zerolog"
    "github.com/rs/zerolog/diode"
)

func main() {
    wr := diode.NewWriter(os.Stdout, 1000, 10*time.Millisecond, func(missed int) {
        fmt.Printf("Logger Dropped %d messages", missed)
    })

    log := zerolog.New(wr)
    log.Print("test")
}

Context for Proposal 2

The given example code doesn't produce output in most cases. As mentioned in the heading, using a diode makes the code non-blocking. Thus, we don't wait at the log.Print("test"). Our program exists before the diode can write the log output. To a learner, it looks like something is wrong with the snippet.

We could add a sleep statement to give the diode some time.

package main

import (
    "os"
    "time"
    "fmt"

    "github.com/rs/zerolog"
    "github.com/rs/zerolog/diode"
)

func main() {
    size := 1
    pollInterval := 1 * time.Second

    wr := diode.NewWriter(os.Stdout, size, pollInterval, func(missed int) {
        fmt.Printf("Logger Dropped %d messages\n", missed)
    })

    log := zerolog.New(wr)
    log.Print("test")

    // put main to sleep to give the diode time for async output
    time.Sleep(1 * time.Second)
}

// OUTPUT:
// {"level":"debug","message":"test"}

Context for Proposal 3

From the README, it doesn't get clear what to expect from a diode. For instance, we don't mention that a diode can drop log messages if we produce too many.

We could rewrite the example to educate the reader:

For example, we create a diode with a buffer for one log message. We check this buffer every second. Afterwards, we write three log messages. The diode checks the buffer after one second and calls the alerter with missed = 2 (since we lost two log messages). Then it writes out the remaining third log message.

package main

import (
    "fmt"
    "os"
    "time"

    "github.com/rs/zerolog"
    "github.com/rs/zerolog/diode"
)

func alerter(missed int) {
    fmt.Printf("Logger dropped %d messages\n", missed)
}

func main() {
    size := 1
    pollInterval := 1 * time.Second

    wr := diode.NewWriter(os.Stdout, size, pollInterval, alerter)
    log := zerolog.New(wr)

    log.Print("test 1")
    log.Print("test 2")
    log.Print("test 3")

    // put main to sleep to give the diode time for async output
    time.Sleep(2 * time.Second)
}

// OUTPUT:
// Logger dropped 2 messages
// {"level":"debug","message":"test 3"}