slog: document LogValuer

Mention in the package doc, and add examples.

Change-Id: If83f4f6adb0a999aabe10bd779069cdc2ba12bdb
Reviewed-on: https://go-review.googlesource.com/c/exp/+/458205
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/slog/doc.go b/slog/doc.go
index 232bfff..20a500d 100644
--- a/slog/doc.go
+++ b/slog/doc.go
@@ -222,10 +222,17 @@
 # Advanced topics
 
 
-
 ## Customizing a type's logging behavior
 
-TODO: discuss LogValuer
+If a type implements the [LogValuer] interface, the [Value] returned from its LogValue
+method is used for logging. You can use this to control how values of the type
+appear in logs. For example, you can redact secret information like passwords,
+or gather a struct's fields in a Group. See the examples under [LogValuer] for
+details.
+
+A LogValue method may return a Value that itself implements [LogValuer]. The [Value.Resolve]
+method handles these cases carefully, avoiding infinite loops and unbounded recursion.
+Handler authors and others may wish to use Value.Resolve instead of calling LogValue directly.
 
 ## Wrapping output methods
 
diff --git a/slog/example_logvaluer_test.go b/slog/example_logvaluer_group_test.go
similarity index 78%
rename from slog/example_logvaluer_test.go
rename to slog/example_logvaluer_group_test.go
index f793e4e..85426d0 100644
--- a/slog/example_logvaluer_test.go
+++ b/slog/example_logvaluer_group_test.go
@@ -12,13 +12,16 @@
 	First, Last string
 }
 
+// LogValue implements slog.LogValuer.
+// It returns a group containing the fields of
+// the Name, so that they appear together in the log output.
 func (n Name) LogValue() slog.Value {
 	return slog.GroupValue(
 		slog.String("first", n.First),
 		slog.String("last", n.Last))
 }
 
-func ExampleLogValuer() {
+func ExampleLogValuer_group() {
 	n := Name{"Perry", "Platypus"}
 	slog.Info("mission accomplished", "agent", n)
 
diff --git a/slog/example_logvaluer_secret_test.go b/slog/example_logvaluer_secret_test.go
new file mode 100644
index 0000000..aa6c704
--- /dev/null
+++ b/slog/example_logvaluer_secret_test.go
@@ -0,0 +1,38 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package slog_test
+
+import (
+	"os"
+
+	"golang.org/x/exp/slog"
+)
+
+// A token is a secret value that grants permissions.
+type Token string
+
+// LogValue implements slog.LogValuer.
+// It avoids revealing the token.
+func (Token) LogValue() slog.Value {
+	return slog.StringValue("REDACTED_TOKEN")
+}
+
+// This example demonstrates a Value that replaces itself
+// with an alternative representation to avoid revealing secrets.
+func ExampleLogValuer_secret() {
+	t := Token("shhhh!")
+	// Remove the time attribute to make Output deterministic.
+	removeTime := func(groups []string, a slog.Attr) slog.Attr {
+		if a.Key == slog.TimeKey && len(groups) == 0 {
+			a.Key = ""
+		}
+		return a
+	}
+	logger := slog.New(slog.HandlerOptions{ReplaceAttr: removeTime}.NewTextHandler(os.Stdout))
+	logger.Info("permission granted", "user", "Perry", "token", t)
+
+	// Output:
+	// level=INFO msg="permission granted" user=Perry token=REDACTED_TOKEN
+}