Skip to content

log/slog: add DiscardHandler #70296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/next/62005.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pkg log/slog, var DiscardHandler Handler #62005
1 change: 1 addition & 0 deletions doc/next/6-stdlib/99-minor/log/slog/62005.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The new [DiscardHandler] is a handler that is never enabled and always discards its output.
23 changes: 23 additions & 0 deletions src/log/slog/example_discard_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package slog_test

import (
"log/slog"
"log/slog/internal/slogtest"
"os"
)

func ExampleDiscardHandler() {
// A slog.TextHandler can output log messages.
logger1 := slog.New(slog.NewTextHandler(
os.Stdout,
&slog.HandlerOptions{ReplaceAttr: slogtest.RemoveTime},
))
logger1.Info("message 1")

// A slog.DiscardHandler will discard all messages.
logger2 := slog.New(slog.DiscardHandler)
logger2.Info("message 2")

// Output:
// level=INFO msg="message 1"
}
11 changes: 11 additions & 0 deletions src/log/slog/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,3 +602,14 @@ func appendRFC3339Millis(b []byte, t time.Time) []byte {
b = append(b[:n+prefixLen], b[n+prefixLen+1:]...) // drop the 4th digit
return b
}

// DiscardHandler discards all log output.
// DiscardHandler.Enabled returns false for all Levels.
var DiscardHandler Handler = discardHandler{}

type discardHandler struct{}

func (dh discardHandler) Enabled(context.Context, Level) bool { return false }
func (dh discardHandler) Handle(context.Context, Record) error { return nil }
func (dh discardHandler) WithAttrs(attrs []Attr) Handler { return dh }
func (dh discardHandler) WithGroup(name string) Handler { return dh }
21 changes: 21 additions & 0 deletions src/log/slog/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"context"
"encoding/json"
"io"
"os"
"path/filepath"
"slices"
"strconv"
Expand Down Expand Up @@ -711,3 +712,23 @@ func BenchmarkWriteTime(b *testing.B) {
buf = appendRFC3339Millis(buf[:0], tm)
}
}

func TestDiscardHandler(t *testing.T) {
ctx := context.Background()
stdout, stderr := os.Stdout, os.Stderr
os.Stdout, os.Stderr = nil, nil // panic on write
t.Cleanup(func() {
os.Stdout, os.Stderr = stdout, stderr
})

// Just ensure nothing panics during normal usage
l := New(DiscardHandler)
l.Info("msg", "a", 1, "b", 2)
l.Debug("bg", Int("a", 1), "b", 2)
l.Warn("w", Duration("dur", 3*time.Second))
l.Error("bad", "a", 1)
l.Log(ctx, LevelWarn+1, "w", Int("a", 1), String("b", "two"))
l.LogAttrs(ctx, LevelInfo+1, "a b c", Int("a", 1), String("b", "two"))
l.Info("info", "a", []Attr{Int("i", 1)})
l.Info("info", "a", GroupValue(Int("i", 1)))
}
21 changes: 10 additions & 11 deletions src/log/slog/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func TestCallDepth(t *testing.T) {

func TestAlloc(t *testing.T) {
ctx := context.Background()
dl := New(discardHandler{})
dl := New(discardTestHandler{})
defer SetDefault(Default()) // restore
SetDefault(dl)

Expand All @@ -258,7 +258,7 @@ func TestAlloc(t *testing.T) {
})
})
t.Run("2 pairs disabled inline", func(t *testing.T) {
l := New(discardHandler{disabled: true})
l := New(DiscardHandler)
s := "abc"
i := 2000
wantAllocs(t, 2, func() {
Expand All @@ -269,7 +269,7 @@ func TestAlloc(t *testing.T) {
})
})
t.Run("2 pairs disabled", func(t *testing.T) {
l := New(discardHandler{disabled: true})
l := New(DiscardHandler)
s := "abc"
i := 2000
wantAllocs(t, 0, func() {
Expand Down Expand Up @@ -305,7 +305,7 @@ func TestAlloc(t *testing.T) {
})
})
t.Run("attrs3 disabled", func(t *testing.T) {
logger := New(discardHandler{disabled: true})
logger := New(DiscardHandler)
wantAllocs(t, 0, func() {
logger.LogAttrs(ctx, LevelInfo, "hello", Int("a", 1), String("b", "two"), Duration("c", time.Second))
})
Expand Down Expand Up @@ -568,18 +568,17 @@ func (c *captureHandler) clear() {
c.r = Record{}
}

type discardHandler struct {
disabled bool
attrs []Attr
type discardTestHandler struct {
attrs []Attr
}

func (d discardHandler) Enabled(context.Context, Level) bool { return !d.disabled }
func (discardHandler) Handle(context.Context, Record) error { return nil }
func (d discardHandler) WithAttrs(as []Attr) Handler {
func (d discardTestHandler) Enabled(context.Context, Level) bool { return true }
func (discardTestHandler) Handle(context.Context, Record) error { return nil }
func (d discardTestHandler) WithAttrs(as []Attr) Handler {
d.attrs = concat(d.attrs, as)
return d
}
func (h discardHandler) WithGroup(name string) Handler {
func (h discardTestHandler) WithGroup(name string) Handler {
return h
}

Expand Down