slog: ensure Handler context is non-nil

We want to allow nil contexts in calls like

    logger.Log(nil, slog.LevelInfo, "msg")

to make them easier to write. But want to relieve handler authors from
having to deal with nil contexts.

So make sure every context passed to Handler.Handle or Handler.Enabled
is non-nil.

Change-Id: I1d305c2bccf45f907ce09ee264e7700d018a95f8
Reviewed-on: https://go-review.googlesource.com/c/exp/+/472182
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/slog/benchmarks/handlers_test.go b/slog/benchmarks/handlers_test.go
index 8e98031..f29eae9 100644
--- a/slog/benchmarks/handlers_test.go
+++ b/slog/benchmarks/handlers_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"bytes"
+	"context"
 	"testing"
 
 	"golang.org/x/exp/slices"
@@ -9,12 +10,13 @@
 )
 
 func TestHandlers(t *testing.T) {
+	ctx := context.Background()
 	r := slog.NewRecord(TestTime, slog.LevelInfo, TestMessage, 0)
 	r.AddAttrs(TestAttrs...)
 	t.Run("text", func(t *testing.T) {
 		var b bytes.Buffer
 		h := newFastTextHandler(&b)
-		if err := h.Handle(nil, r); err != nil {
+		if err := h.Handle(ctx, r); err != nil {
 			t.Fatal(err)
 		}
 		got := b.String()
@@ -24,7 +26,7 @@
 	})
 	t.Run("async", func(t *testing.T) {
 		h := newAsyncHandler()
-		if err := h.Handle(nil, r); err != nil {
+		if err := h.Handle(ctx, r); err != nil {
 			t.Fatal(err)
 		}
 		got := h.ringBuffer[0]
diff --git a/slog/example_depth_test.go b/slog/example_depth_test.go
index 34630c1..599cf16 100644
--- a/slog/example_depth_test.go
+++ b/slog/example_depth_test.go
@@ -5,6 +5,7 @@
 package slog_test
 
 import (
+	"context"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -18,13 +19,13 @@
 // The log record contains the source position of the caller of Infof.
 func Infof(format string, args ...any) {
 	l := slog.Default()
-	if !l.Enabled(nil, slog.LevelInfo) {
+	if !l.Enabled(context.Background(), slog.LevelInfo) {
 		return
 	}
 	var pcs [1]uintptr
 	runtime.Callers(2, pcs[:]) // skip [Callers, Infof]
 	r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf(format, args...), pcs[0])
-	_ = l.Handler().Handle(nil, r)
+	_ = l.Handler().Handle(context.Background(), r)
 }
 
 func Example_wrapping() {
@@ -46,5 +47,5 @@
 	Infof("message, %s", "formatted")
 
 	// Output:
-	// level=INFO source=example_depth_test.go:46 msg="message, formatted"
+	// level=INFO source=example_depth_test.go:47 msg="message, formatted"
 }
diff --git a/slog/handler.go b/slog/handler.go
index 9392fd5..60d58bb 100644
--- a/slog/handler.go
+++ b/slog/handler.go
@@ -30,15 +30,16 @@
 	// The handler ignores records whose level is lower.
 	// It is called early, before any arguments are processed,
 	// to save effort if the log event should be discarded.
-	// The Logger's context is passed so Enabled can use its values
-	// to make a decision. The context may be nil.
+	// If called from a Logger method, the first argument is the context
+	// passed to that method, or context.Background() if nil was passed
+	// or the method does not take a context.
+	// The context is passed so Enabled can use its values
+	// to make a decision.
 	Enabled(context.Context, Level) bool
 
 	// Handle handles the Record.
-	// It will only be called if Enabled returns true.
-	//
-	// The first argument is the context of the Logger that created the Record,
-	// which may be nil.
+	// It will only be called Enabled returns true.
+	// The Context argument is as for Enabled.
 	// It is present solely to provide Handlers access to the context's values.
 	// Canceling the context should not affect record processing.
 	// (Among other things, log messages may be necessary to debug a
diff --git a/slog/handler_test.go b/slog/handler_test.go
index 6324dfa..8b1b669 100644
--- a/slog/handler_test.go
+++ b/slog/handler_test.go
@@ -8,6 +8,7 @@
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"io"
 	"strings"
@@ -19,6 +20,7 @@
 )
 
 func TestDefaultHandle(t *testing.T) {
+	ctx := context.Background()
 	preAttrs := []Attr{Int("pre", 0)}
 	attrs := []Attr{Int("a", 1), String("b", "two")}
 	for _, test := range []struct {
@@ -93,7 +95,7 @@
 			}
 			r := NewRecord(time.Time{}, LevelInfo, "message", 0)
 			r.AddAttrs(test.attrs...)
-			if err := h.Handle(nil, r); err != nil {
+			if err := h.Handle(ctx, r); err != nil {
 				t.Fatal(err)
 			}
 			if got != test.want {
@@ -105,6 +107,7 @@
 
 // Verify the common parts of TextHandler and JSONHandler.
 func TestJSONAndTextHandlers(t *testing.T) {
+	ctx := context.Background()
 
 	// ReplaceAttr functions
 
@@ -313,7 +316,7 @@
 						h = test.with(h)
 					}
 					buf.Reset()
-					if err := h.Handle(nil, r); err != nil {
+					if err := h.Handle(ctx, r); err != nil {
 						t.Fatal(err)
 					}
 					got := strings.TrimSuffix(buf.String(), "\n")
diff --git a/slog/json_handler_test.go b/slog/json_handler_test.go
index dfaf00f..64b3b4d 100644
--- a/slog/json_handler_test.go
+++ b/slog/json_handler_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -41,7 +42,7 @@
 			h := test.opts.NewJSONHandler(&buf)
 			r := NewRecord(testTime, LevelInfo, "m", 0)
 			r.AddAttrs(Int("a", 1), Any("m", map[string]int{"b": 2}))
-			if err := h.Handle(nil, r); err != nil {
+			if err := h.Handle(context.Background(), r); err != nil {
 				t.Fatal(err)
 			}
 			got := strings.TrimSuffix(buf.String(), "\n")
diff --git a/slog/logger.go b/slog/logger.go
index 77599cc..043ba15 100644
--- a/slog/logger.go
+++ b/slog/logger.go
@@ -50,7 +50,7 @@
 }
 
 func (w *handlerWriter) Write(buf []byte) (int, error) {
-	if !w.h.Enabled(nil, w.level) {
+	if !w.h.Enabled(context.Background(), w.level) {
 		return 0, nil
 	}
 	var pc uintptr
@@ -67,7 +67,7 @@
 		buf = buf[:len(buf)-1]
 	}
 	r := NewRecord(time.Now(), w.level, string(buf), pc)
-	return origLen, w.h.Handle(nil, r)
+	return origLen, w.h.Handle(context.Background(), r)
 }
 
 // A Logger records structured information about each call to its
@@ -136,6 +136,9 @@
 
 // Enabled reports whether l emits log records at the given context and level.
 func (l *Logger) Enabled(ctx context.Context, level Level) bool {
+	if ctx == nil {
+		ctx = context.Background()
+	}
 	return l.Handler().Enabled(ctx, level)
 }
 
@@ -230,6 +233,9 @@
 		r.nFront++
 	}
 	r.Add(args...)
+	if ctx == nil {
+		ctx = context.Background()
+	}
 	_ = l.Handler().Handle(ctx, r)
 }
 
@@ -247,6 +253,9 @@
 	}
 	r := NewRecord(time.Now(), level, msg, pc)
 	r.AddAttrs(attrs...)
+	if ctx == nil {
+		ctx = context.Background()
+	}
 	_ = l.Handler().Handle(ctx, r)
 }
 
diff --git a/slog/text_handler_test.go b/slog/text_handler_test.go
index ab82488..d88d20c 100644
--- a/slog/text_handler_test.go
+++ b/slog/text_handler_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"bytes"
+	"context"
 	"errors"
 	"fmt"
 	"io"
@@ -79,7 +80,7 @@
 					h := opts.opts.NewTextHandler(&buf)
 					r := NewRecord(testTime, LevelInfo, "a message", 0)
 					r.AddAttrs(test.attr)
-					if err := h.Handle(nil, r); err != nil {
+					if err := h.Handle(context.Background(), r); err != nil {
 						t.Fatal(err)
 					}
 					got := buf.String()
@@ -120,7 +121,7 @@
 	var buf bytes.Buffer
 	h := HandlerOptions{AddSource: true}.NewTextHandler(&buf)
 	r := NewRecord(testTime, LevelInfo, "m", callerPC(2))
-	if err := h.Handle(nil, r); err != nil {
+	if err := h.Handle(context.Background(), r); err != nil {
 		t.Fatal(err)
 	}
 	if got := buf.String(); !sourceRegexp.MatchString(got) {
@@ -149,7 +150,7 @@
 	// Also test omitting time.
 	r := NewRecord(time.Time{}, 0 /* 0 Level is INFO */, "m", 0)
 	r.AddAttrs(Int("a", 1))
-	if err := h.Handle(nil, r); err != nil {
+	if err := h.Handle(context.Background(), r); err != nil {
 		t.Fatal(err)
 	}
 	got := strings.TrimSuffix(buf.String(), "\n")
@@ -165,11 +166,11 @@
 		r.AddAttrs(Int("x = y", i))
 	}
 	var h Handler = NewTextHandler(io.Discard)
-	wantAllocs(t, 0, func() { h.Handle(nil, r) })
+	wantAllocs(t, 0, func() { h.Handle(context.Background(), r) })
 
 	h = h.WithGroup("s")
 	r.AddAttrs(Group("g", Int("a", 1)))
-	wantAllocs(t, 0, func() { h.Handle(nil, r) })
+	wantAllocs(t, 0, func() { h.Handle(context.Background(), r) })
 }
 
 func TestNeedsQuoting(t *testing.T) {