logging.TestLogger records into memory and does not touch the active backend, so it suits tests of code that receives a logging.Logger as a dependency.
func TestService_Run(t *testing.T) {
tl := logging.NewTestLogger()
svc := NewService(tl) // accepts logging.Logger
svc.Run()
recs := tl.ByLevel(logging.LevelError)
require.Len(t, recs, 1)
require.Equal(t, "run failed", recs[0].Message)
require.Equal(t, "boom", recs[0].Fields["error"])
}Record has Level, Message, Logger (the name of a named logger), Caller and Fields. Field values are stored as zap keeps them: integers are int64, unsigned integers uint64, floats float64; the error from WithError is stored under the key error as text. Static fields and redacted values (***) appear in Fields as they would in a real record.
Methods: Records(), ByLevel(level), Match(func(Record) bool), Reset(). Everything recorded can be read from other goroutines.
NewTestLogger takes the same Options as Init: WithLevel restricts the recorded levels (by default everything from trace up is recorded), WithContextExtractors makes WithContext work, WithConfig brings CustomLevels, Fields and Redact, and WithHook hooks are called. Format, output target and levelsEndpoint are ignored.
Named and context loggers that write into the same records are made through tl.Backend():
db := tl.Backend().NewNamedLogger("db")TestLogger also implements Panicer and Namer.
Fatal does not exit the process: the record is saved, then panic is called with the message text. Panic records and panics the same way. Check with require.Panics.
If the code under test calls logging.NewLogger() itself or uses logging.Default, install the test backend as the active one:
tl := logging.UseTestLogger(t)After the test, the previous backend and configuration are restored automatically (t can be anything with Cleanup, such as *testing.T). The active backend is global, so such tests must not run in parallel (t.Parallel()). Loggers created before the call stay on the previous backend, while logging.Default follows the new one.