exp/slog: add example for custom levels and names

The recommended way to introduce new `Level` (or rename an existing
one) is via `HandlerOptions.ReplaceAttr`. However, this is not
immediately intuitive to package users.

This introduces an example that adds additional log levels and gives
them custom names in a `ReplaceAttr` function. Given the number of users
who will want to introduce custom log levels, this feels like a good
addition to the library documentation.

Change-Id: I175440368fa1d4ce548d9c769ca22fd613151941
Reviewed-on: https://go-review.googlesource.com/c/exp/+/462435
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/slog/example_custom_levels_test.go b/slog/example_custom_levels_test.go
new file mode 100644
index 0000000..d0ec3a7
--- /dev/null
+++ b/slog/example_custom_levels_test.go
@@ -0,0 +1,93 @@
+// Copyright 2023 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 (
+	"fmt"
+	"os"
+
+	"golang.org/x/exp/slog"
+)
+
+// This example demonstrates using custom log levels and custom log level names.
+// In addition to the default log levels, it introduces Trace, Notice, and
+// Emergency levels. The ReplaceAttr changes the way levels are printed for both
+// the standard log levels and the custom log levels.
+func ExampleHandlerOptions_customLevels() {
+	// Exported constants from a custom logging package.
+	const (
+		LevelTrace     = slog.Level(-8)
+		LevelDebug     = slog.LevelDebug
+		LevelInfo      = slog.LevelInfo
+		LevelNotice    = slog.Level(2)
+		LevelWarning   = slog.LevelWarn
+		LevelError     = slog.LevelError
+		LevelEmergency = slog.Level(12)
+	)
+
+	th := 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,
+
+		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
+			// Remove time from the output for predicable test output.
+			if a.Key == slog.TimeKey {
+				return slog.Attr{}
+			}
+
+			// Customize the name of the level key and the output string, including
+			// custom level values.
+			if a.Key == slog.LevelKey {
+				// Rename the level key from "level" to "sev".
+				a.Key = "sev"
+
+				// Handle custom level values.
+				level := a.Value.Any().(slog.Level)
+
+				// This could also look up the name from a map or other structure, but
+				// this demonstrates using a switch statement to rename levels. For
+				// maximum performance, the string values should be constants, but this
+				// example uses the raw strings for readability.
+				switch {
+				case level < LevelDebug:
+					a.Value = slog.StringValue("TRACE")
+				case level < LevelInfo:
+					a.Value = slog.StringValue("DEBUG")
+				case level < LevelNotice:
+					a.Value = slog.StringValue("INFO")
+				case level < LevelWarning:
+					a.Value = slog.StringValue("NOTICE")
+				case level < LevelError:
+					a.Value = slog.StringValue("WARNING")
+				case level < LevelEmergency:
+					a.Value = slog.StringValue("ERROR")
+				default:
+					a.Value = slog.StringValue("EMERGENCY")
+				}
+			}
+
+			return a
+		},
+	}.NewTextHandler(os.Stdout)
+
+	logger := slog.New(th)
+	logger.Log(LevelEmergency, "missing pilots")
+	logger.Error("failed to start engines", fmt.Errorf("missing fuel"))
+	logger.Warn("falling back to default value")
+	logger.Log(LevelNotice, "all systems are running")
+	logger.Info("initiating launch")
+	logger.Debug("starting background job")
+	logger.Log(LevelTrace, "button clicked")
+
+	// Output:
+	// sev=EMERGENCY msg="missing pilots"
+	// sev=ERROR msg="failed to start engines" err="missing fuel"
+	// sev=WARNING msg="falling back to default value"
+	// sev=NOTICE msg="all systems are running"
+	// sev=INFO msg="initiating launch"
+	// sev=DEBUG msg="starting background job"
+	// sev=TRACE msg="button clicked"
+}