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"
+}