Logging

All the logging in claircore is done with zerolog via context.Context values. Loggers are extracted from the Contexts via the zerolog.Ctx function, then a child logger is created via the With method and associated with a new Context via the (*zerolog.Logger).WithContext method.

This allows for claircore's logging to be used consistently throughout all the packages without having unintended prints to stderr.

How to Log

Getting a logger

In a function, first obtain a logger:

	log := zerolog.Ctx(ctx).With().

then add key-value pairs of any relevant context:

		Str("component", "Example.Logger").

then create the new logger:

		Logger()

then add the logger back to the Context so that child functions will have the annotations on the logger they extract from the Context.

	ctx = log.WithContext(ctx)

The log object shouldn't be stored in a struct and should stay function local.

Logging style

Constant Messages

Zerolog emits lines when the Msg or Msgf methods are called. Project style is to not use Msgf. Any variable data should be set as key-value pairs on the Event object.

For example, don't do this:

	log.Info().Msgf("done at: %v", time.Now())

Do this instead:

	log.Info().
		Time("time", time.Now()).
		Msgf("done")

Grammar

When noting when noting the change during a chunk of work, make sure that the log messages scan as visually similar. Usually, this means formatting messages into "${process} ${event}". For example:

frob start
frob initialized
frob ready
frob success
frob done

Is much easier to scan than:

starting to frob
initialized frobber
ready for frobbing
did frob
done with frobing

Don't log and return

When handling an error, code should only log it if it does not propagate it. The code that ultimately handles the error is responsible for deciding what to do with it. Logging and returning ends up with the same message repeated multiple times in the logs.

Levels

Claircore attempts to have consistent leveled logging. The rules for figuring out what level to use is:

  • Panic

    There's some occurrence that means the process won't work correctly.

  • Fatal

    Unused, because it prevents defers from running.

  • Error

    Something unexpected occurred and the process can continue, but a human needs to be notified. An error will be returned for this request.

  • Warn

    Something unexpected occurred and the process can continue. An error will be returned for this request.

  • Info

    Some information that may be useful to an operator. Examples include a timer-based process starting and ending, a user request starting and ending, or a summary of work done.

  • Debug

    Some information that may be useful to a developer. Examples include entering and exiting functions, stepping through a function, or specific file paths used while work is being done.