diff --git a/promslog/slog.go b/promslog/slog.go index f8f77165..d84a1460 100644 --- a/promslog/slog.go +++ b/promslog/slog.go @@ -282,8 +282,8 @@ func New(config *Config) *slog.Logger { return slog.New(slog.NewTextHandler(config.Writer, logHandlerOpts)) } -// NewNopLogger is a convenience function to return an slog.Logger that writes -// to io.Discard. +// NewNopLogger is a convenience function to return an slog.Logger that +// discards all log records. func NewNopLogger() *slog.Logger { - return New(&Config{Writer: io.Discard}) + return slog.New(slog.DiscardHandler) } diff --git a/promslog/slog_test.go b/promslog/slog_test.go index 05cbd61f..dd5f6b24 100644 --- a/promslog/slog_test.go +++ b/promslog/slog_test.go @@ -254,3 +254,35 @@ func TestReservedKeys(t *testing.T) { }) } } + +type expensiveValuer struct { + called *bool +} + +func (v expensiveValuer) LogValue() slog.Value { + *v.called = true + return slog.StringValue("expensive") +} + +func TestNewNopLogger(t *testing.T) { + logger := NewNopLogger() + + for _, lvl := range []slog.Level{slog.LevelDebug, slog.LevelInfo, slog.LevelWarn, slog.LevelError} { + require.Falsef(t, logger.Enabled(context.Background(), lvl), "nop logger must be disabled for level %s", lvl) + } + + // Arguments must never be evaluated, so that using a nop logger in a hot + // path stays free. + called := false + logger.Info("test", "expensive", expensiveValuer{called: &called}) + require.Falsef(t, called, "nop logger must not evaluate log arguments") +} + +func BenchmarkNopLogger(b *testing.B) { + logger := NewNopLogger() + value := struct{ Field string }{Field: "value"} + + for b.Loop() { + logger.Info("test", "key", value) + } +}