pkg log/slog, func Log(context.Context, Level, string, ...interface{}) #56345
pkg log/slog, func LogAttrs(context.Context, Level, string, ...Attr) #56345
pkg log/slog, func New(Handler) *Logger #56345
-pkg log/slog, func NewJSONHandler(io.Writer) *JSONHandler #56345
pkg log/slog, func NewLogLogger(Handler, Level) *log.Logger #56345
pkg log/slog, func NewRecord(time.Time, Level, string, uintptr) Record #56345
-pkg log/slog, func NewTextHandler(io.Writer) *TextHandler #56345
pkg log/slog, func SetDefault(*Logger) #56345
pkg log/slog, func String(string, string) Attr #56345
pkg log/slog, func StringValue(string) Value #56345
pkg log/slog, method (*TextHandler) WithGroup(string) Handler #56345
pkg log/slog, method (Attr) Equal(Attr) bool #56345
pkg log/slog, method (Attr) String() string #56345
-pkg log/slog, method (HandlerOptions) NewJSONHandler(io.Writer) *JSONHandler #56345
-pkg log/slog, method (HandlerOptions) NewTextHandler(io.Writer) *TextHandler #56345
pkg log/slog, method (Kind) String() string #56345
pkg log/slog, method (Level) Level() Level #56345
pkg log/slog, method (Level) MarshalJSON() ([]uint8, error) #56345
--- /dev/null
+pkg log/slog, func NewJSONHandler(io.Writer, *HandlerOptions) *JSONHandler #59339
+pkg log/slog, func NewTextHandler(io.Writer, *HandlerOptions) *TextHandler #59339
This statement uses [New] to create a new logger with a TextHandler
that writes structured records in text form to standard error:
- logger := slog.New(slog.NewTextHandler(os.Stderr))
+ logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
[TextHandler] output is a sequence of key=value pairs, easily and unambiguously
parsed by machine. This statement:
The package also provides [JSONHandler], whose output is line-delimited JSON:
- logger := slog.New(slog.NewJSONHandler(os.Stdout))
+ logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
logger.Info("hello", "count", 3)
produces this output:
Then use the LevelVar to construct a handler, and make it the default:
- h := slog.HandlerOptions{Level: programLevel}.NewJSONHandler(os.Stderr)
+ h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})
slog.SetDefault(slog.New(h))
Now the program can change its logging level with a single statement:
LevelEmergency = slog.Level(12)
)
- th := slog.HandlerOptions{
+ th := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
// Set a custom level to show all log output. The default value is
// LevelInfo, which would drop Debug and Trace logs.
Level: LevelTrace,
return a
},
- }.NewTextHandler(os.Stdout)
+ })
logger := slog.New(th)
logger.Log(nil, LevelEmergency, "missing pilots")
// Another typical use would be to decrease the log level (to LevelDebug, say)
// during a part of the program that was suspected of containing a bug.
func ExampleHandler_levelHandler() {
- th := slog.HandlerOptions{ReplaceAttr: slogtest.RemoveTime}.NewTextHandler(os.Stdout)
+ th := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: slogtest.RemoveTime})
logger := slog.New(NewLevelHandler(slog.LevelWarn, th))
logger.Info("not printed")
logger.Warn("printed")
// with an alternative representation to avoid revealing secrets.
func ExampleLogValuer_secret() {
t := Token("shhhh!")
- logger := slog.New(slog.HandlerOptions{ReplaceAttr: slogtest.RemoveTime}.
- NewTextHandler(os.Stdout))
+ logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: slogtest.RemoveTime}))
logger.Info("permission granted", "user", "Perry", "token", t)
// Output:
r, _ := http.NewRequest("GET", "localhost", nil)
// ...
- logger := slog.New(slog.HandlerOptions{ReplaceAttr: slogtest.RemoveTime}.NewTextHandler(os.Stdout))
+ logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: slogtest.RemoveTime}))
logger.Info("finished",
slog.Group("req",
slog.String("method", r.Method),
}
return a
}
- logger := slog.New(slog.HandlerOptions{AddSource: true, ReplaceAttr: replace}.NewTextHandler(os.Stdout))
+ logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: true, ReplaceAttr: replace}))
Infof(logger, "message, %s", "formatted")
// Output:
h Handler
want string
}{
- {"text", opts.NewTextHandler(&buf), test.wantText},
- {"json", opts.NewJSONHandler(&buf), test.wantJSON},
+ {"text", NewTextHandler(&buf, &opts), test.wantText},
+ {"json", NewJSONHandler(&buf, &opts), test.wantJSON},
} {
t.Run(handler.name, func(t *testing.T) {
h := handler.h
// Verify that a second call to Logger.With does not corrupt
// the original.
var buf bytes.Buffer
- h := HandlerOptions{ReplaceAttr: removeKeys(TimeKey)}.NewTextHandler(&buf)
+ h := NewTextHandler(&buf, &HandlerOptions{ReplaceAttr: removeKeys(TimeKey)})
logger := New(h).With(
String("app", "playground"),
String("role", "tester"),
var got []ga
- h := HandlerOptions{ReplaceAttr: func(gs []string, a Attr) Attr {
+ h := NewTextHandler(io.Discard, &HandlerOptions{ReplaceAttr: func(gs []string, a Attr) Attr {
v := a.Value.String()
if a.Key == TimeKey {
v = "<now>"
}
got = append(got, ga{strings.Join(gs, ","), a.Key, v})
return a
- }}.NewTextHandler(io.Discard)
+ }})
New(h).
With(Int("a", 1)).
WithGroup("g1").
{"disabled", disabledHandler{}, false},
{"async discard", newAsyncHandler(), true},
{"fastText discard", newFastTextHandler(io.Discard), false},
- {"Text discard", slog.NewTextHandler(io.Discard), false},
- {"JSON discard", slog.NewJSONHandler(io.Discard), false},
+ {"Text discard", slog.NewTextHandler(io.Discard, nil), false},
+ {"JSON discard", slog.NewJSONHandler(io.Discard, nil), false},
} {
logger := slog.New(handler.h)
b.Run(handler.name, func(b *testing.B) {
}
// NewJSONHandler creates a JSONHandler that writes to w,
-// using the default options.
-func NewJSONHandler(w io.Writer) *JSONHandler {
- return (HandlerOptions{}).NewJSONHandler(w)
-}
-
-// NewJSONHandler creates a JSONHandler with the given options that writes to w.
-func (opts HandlerOptions) NewJSONHandler(w io.Writer) *JSONHandler {
+// using the given options.
+// If opts is nil, the default options are used.
+func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler {
+ if opts == nil {
+ opts = &HandlerOptions{}
+ }
return &JSONHandler{
&commonHandler{
json: true,
w: w,
- opts: opts,
+ opts: *opts,
},
}
}
} {
t.Run(test.name, func(t *testing.T) {
var buf bytes.Buffer
- h := test.opts.NewJSONHandler(&buf)
+ h := NewJSONHandler(&buf, &test.opts)
r := NewRecord(testTime, LevelInfo, "m", 0)
r.AddAttrs(Int("a", 1), Any("m", map[string]int{"b": 2}))
if err := h.Handle(context.Background(), r); err != nil {
}},
} {
b.Run(bench.name, func(b *testing.B) {
- l := New(bench.opts.NewJSONHandler(io.Discard)).With(
+ l := New(NewJSONHandler(io.Discard, &bench.opts)).With(
String("program", "my-test-program"),
String("package", "log/slog"),
String("traceID", "2039232309232309"),
{"struct file", outFile, structAttrs},
} {
b.Run(bench.name, func(b *testing.B) {
- l := New(NewJSONHandler(bench.wc)).With(bench.attrs...)
+ l := New(NewJSONHandler(bench.wc, nil)).With(bench.attrs...)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
func TestLogTextHandler(t *testing.T) {
var buf bytes.Buffer
- l := New(NewTextHandler(&buf))
+ l := New(NewTextHandler(&buf, nil))
check := func(want string) {
t.Helper()
// Once slog.SetDefault is called, the direction is reversed: the default
// log.Logger's output goes through the handler.
- SetDefault(New(HandlerOptions{AddSource: true}.NewTextHandler(&slogbuf)))
+ SetDefault(New(NewTextHandler(&slogbuf, &HandlerOptions{AddSource: true})))
log.Print("msg2")
checkLogOutput(t, slogbuf.String(), "time="+timeRE+` level=INFO source=.*logger_test.go:\d{3} msg=msg2`)
// The default log.Logger always outputs at Info level.
slogbuf.Reset()
- SetDefault(New(HandlerOptions{Level: LevelWarn}.NewTextHandler(&slogbuf)))
+ SetDefault(New(NewTextHandler(&slogbuf, &HandlerOptions{Level: LevelWarn})))
log.Print("should not appear")
if got := slogbuf.String(); got != "" {
t.Errorf("got %q, want empty", got)
}
return a
}
- l := New(HandlerOptions{ReplaceAttr: removeTime}.NewTextHandler(&buf))
+ l := New(NewTextHandler(&buf, &HandlerOptions{ReplaceAttr: removeTime}))
l.Error("msg", "err", io.EOF, "a", 1)
checkLogOutput(t, buf.String(), `level=ERROR msg=msg err=EOF a=1`)
buf.Reset()
func TestNewLogLogger(t *testing.T) {
var buf bytes.Buffer
- h := NewTextHandler(&buf)
+ h := NewTextHandler(&buf, nil)
ll := NewLogLogger(h, LevelWarn)
ll.Print("hello")
checkLogOutput(t, buf.String(), "time="+timeRE+` level=WARN msg=hello`)
}
// NewTextHandler creates a TextHandler that writes to w,
-// using the default options.
-func NewTextHandler(w io.Writer) *TextHandler {
- return (HandlerOptions{}).NewTextHandler(w)
-}
-
-// NewTextHandler creates a TextHandler with the given options that writes to w.
-func (opts HandlerOptions) NewTextHandler(w io.Writer) *TextHandler {
+// using the given options.
+// If opts is nil, the default options are used.
+func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler {
+ if opts == nil {
+ opts = &HandlerOptions{}
+ }
return &TextHandler{
&commonHandler{
json: false,
w: w,
- opts: opts,
+ opts: *opts,
},
}
}
} {
t.Run(opts.name, func(t *testing.T) {
var buf bytes.Buffer
- h := opts.opts.NewTextHandler(&buf)
+ h := NewTextHandler(&buf, &opts.opts)
r := NewRecord(testTime, LevelInfo, "a message", 0)
r.AddAttrs(test.attr)
if err := h.Handle(context.Background(), r); err != nil {
func TestTextHandlerPreformatted(t *testing.T) {
var buf bytes.Buffer
- var h Handler = NewTextHandler(&buf)
+ var h Handler = NewTextHandler(&buf, nil)
h = h.WithAttrs([]Attr{Duration("dur", time.Minute), Bool("b", true)})
// Also test omitting time.
r := NewRecord(time.Time{}, 0 /* 0 Level is INFO */, "m", 0)
for i := 0; i < 10; i++ {
r.AddAttrs(Int("x = y", i))
}
- var h Handler = NewTextHandler(io.Discard)
+ var h Handler = NewTextHandler(io.Discard, nil)
wantAllocs(t, 0, func() { h.Handle(context.Background(), r) })
h = h.WithGroup("s")
// format when given a pointer to a map[string]any.
func Example_parsing() {
var buf bytes.Buffer
- h := slog.NewJSONHandler(&buf)
+ h := slog.NewJSONHandler(&buf, nil)
results := func() []map[string]any {
var ms []map[string]any