3

In my Go app I moved error logging into a separate function

package logging

import "github.com/rs/zerolog"

func LogError(logger zerolog.Logger, err error) {
    logger.Error().Err(err).Msg("")
}

and want to create a small test to ensure the error log event was been raised. For reproduction purposes I won't show the test file but code from the playground https://go.dev/play/p/Jnyz51JemTh

package main

import (
    "errors"
    "fmt"

    "github.com/rs/zerolog"
)

func main() {
    logger := zerolog.Nop()
    testError := errors.New("test error")
    logHook := &logHook{}

    logger.Hook(logHook)

    LogError(logger, testError)

    amountOfLogEvents := len(logHook.logEvents)

    fmt.Printf("Expected amount of log events: 1\nActual amount of log events: %v", amountOfLogEvents)

    // TODO inspect logHook.logEvents[0]
}

func LogError(logger zerolog.Logger, err error) {
    logger.Error().Err(err).Msg("")
}

type logHook struct {
    logEvents []zerolog.Event
}

func (logHook *logHook) Run(logEvent *zerolog.Event, level zerolog.Level, message string) {
    logHook.logEvents = append(logHook.logEvents, *logEvent)
}

You should see the output

Expected amount of log events: 1

Actual amount of log events: 0

I thought I could create a custom hook to catch the event and inspect it. Unfortunately I'm struggling with two things

  • Inspecting the error, message, ... from a single zerolog.Event didn't work for me. I couldn't find any fields or functions for this
  • The test fails because amountOfLogEvents is empty, although I would expect it to have one event

Do you know what's wrong and how to fix it?

baitendbidz
  • 187
  • 3
  • 19

2 Answers2

2
  1. Nop cant apply the hooks,And the Logger is not a pointer
// Nop returns a disabled logger for which all operation are no-op.
func Nop() Logger {
    return New(nil).Level(Disabled)
}
// Hook returns a logger with the h Hook.
func (l Logger) Hook(h Hook) Logger {
    l.hooks = append(l.hooks, h)
    return l
}
  1. For the same reason, Your logHook should be
func (logHook *logHook) Run(logEvent *zerolog.Event, level zerolog.Level, message string) {
    logHook.logEvents = append(logHook.logEvents, *logEvent)
}

So we get this and can pass the test, see playground

logger := zerolog.New(os.Stdout)
logHook := &logHook{}
logger = logger.Hook(logHook)
Trock
  • 546
  • 2
  • 13
0

Another similar approach could be to use your own io.Writer to gather log messages as strings. It may be a bit easier to compare them later with string values expected by the test.

func TestMyTest(t *testing.T) {
    logs := &logSink{}
    logger := zerolog.New(logs)

    testedComponent := NewComponent(logger)
    testedComponent.DoSomething()

    assert.Contains(t, logs.Index(0), "some error logged here")
}

type logSink struct {
    logs []string
}

func (l *logSink) Write(p []byte) (n int, err error) {
    l.logs = append(l.logs, string(p))
    return len(p), nil
}

func (l *logSink) Index(i int) string {
    return l.logs[i]
}

After each logged event, the logSink.logs slice will contain a string for each entry like:

[
   {"time":"...","level":"info","message":"..."},
   {"time":"...","level":"debug","message":"..."},
]
luben
  • 2,512
  • 4
  • 30
  • 41