]> Cypherpunks.ru repositories - gostls13.git/blob - src/log/slog/doc.go
log/slog: built-in handler constructors take options as a second arg
[gostls13.git] / src / log / slog / doc.go
1 // Copyright 2022 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 /*
6 Package slog provides structured logging,
7 in which log records include a message,
8 a severity level, and various other attributes
9 expressed as key-value pairs.
10
11 It defines a type, [Logger],
12 which provides several methods (such as [Logger.Info] and [Logger.Error])
13 for reporting events of interest.
14
15 Each Logger is associated with a [Handler].
16 A Logger output method creates a [Record] from the method arguments
17 and passes it to the Handler, which decides how to handle it.
18 There is a default Logger accessible through top-level functions
19 (such as [Info] and [Error]) that call the corresponding Logger methods.
20
21 A log record consists of a time, a level, a message, and a set of key-value
22 pairs, where the keys are strings and the values may be of any type.
23 As an example,
24
25         slog.Info("hello", "count", 3)
26
27 creates a record containing the time of the call,
28 a level of Info, the message "hello", and a single
29 pair with key "count" and value 3.
30
31 The [Info] top-level function calls the [Logger.Info] method on the default Logger.
32 In addition to [Logger.Info], there are methods for Debug, Warn and Error levels.
33 Besides these convenience methods for common levels,
34 there is also a [Logger.Log] method which takes the level as an argument.
35 Each of these methods has a corresponding top-level function that uses the
36 default logger.
37
38 The default handler formats the log record's message, time, level, and attributes
39 as a string and passes it to the [log] package.
40
41         2022/11/08 15:28:26 INFO hello count=3
42
43 For more control over the output format, create a logger with a different handler.
44 This statement uses [New] to create a new logger with a TextHandler
45 that writes structured records in text form to standard error:
46
47         logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
48
49 [TextHandler] output is a sequence of key=value pairs, easily and unambiguously
50 parsed by machine. This statement:
51
52         logger.Info("hello", "count", 3)
53
54 produces this output:
55
56         time=2022-11-08T15:28:26.000-05:00 level=INFO msg=hello count=3
57
58 The package also provides [JSONHandler], whose output is line-delimited JSON:
59
60         logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
61         logger.Info("hello", "count", 3)
62
63 produces this output:
64
65         {"time":"2022-11-08T15:28:26.000000000-05:00","level":"INFO","msg":"hello","count":3}
66
67 Both [TextHandler] and [JSONHandler] can be configured with [HandlerOptions].
68 There are options for setting the minimum level (see Levels, below),
69 displaying the source file and line of the log call, and
70 modifying attributes before they are logged.
71
72 Setting a logger as the default with
73
74         slog.SetDefault(logger)
75
76 will cause the top-level functions like [Info] to use it.
77 [SetDefault] also updates the default logger used by the [log] package,
78 so that existing applications that use [log.Printf] and related functions
79 will send log records to the logger's handler without needing to be rewritten.
80
81 # Attrs and Values
82
83 An [Attr] is a key-value pair. The Logger output methods accept Attrs as well as
84 alternating keys and values. The statement
85
86         slog.Info("hello", slog.Int("count", 3))
87
88 behaves the same as
89
90         slog.Info("hello", "count", 3)
91
92 There are convenience constructors for [Attr] such as [Int], [String], and [Bool]
93 for common types, as well as the function [Any] for constructing Attrs of any
94 type.
95
96 The value part of an Attr is a type called [Value].
97 Like an [any], a Value can hold any Go value,
98 but it can represent typical values, including all numbers and strings,
99 without an allocation.
100
101 For the most efficient log output, use [Logger.LogAttrs].
102 It is similar to [Logger.Log] but accepts only Attrs, not alternating
103 keys and values; this allows it, too, to avoid allocation.
104
105 The call
106
107         logger.LogAttrs(nil, slog.LevelInfo, "hello", slog.Int("count", 3))
108
109 is the most efficient way to achieve the same output as
110
111         slog.Info("hello", "count", 3)
112
113 Some attributes are common to many log calls.
114 For example, you may wish to include the URL or trace identifier of a server request
115 with all log events arising from the request.
116 Rather than repeat the attribute with every log call, you can use [Logger.With]
117 to construct a new Logger containing the attributes:
118
119         logger2 := logger.With("url", r.URL)
120
121 The arguments to With are the same key-value pairs used in [Logger.Info].
122 The result is a new Logger with the same handler as the original, but additional
123 attributes that will appear in the output of every call.
124
125 # Levels
126
127 A [Level] is an integer representing the importance or severity of a log event.
128 The higher the level, the more severe the event.
129 This package defines constants for the most common levels,
130 but any int can be used as a level.
131
132 In an application, you may wish to log messages only at a certain level or greater.
133 One common configuration is to log messages at Info or higher levels,
134 suppressing debug logging until it is needed.
135 The built-in handlers can be configured with the minimum level to output by
136 setting [HandlerOptions.Level].
137 The program's `main` function typically does this.
138 The default value is LevelInfo.
139
140 Setting the [HandlerOptions.Level] field to a [Level] value
141 fixes the handler's minimum level throughout its lifetime.
142 Setting it to a [LevelVar] allows the level to be varied dynamically.
143 A LevelVar holds a Level and is safe to read or write from multiple
144 goroutines.
145 To vary the level dynamically for an entire program, first initialize
146 a global LevelVar:
147
148         var programLevel = new(slog.LevelVar) // Info by default
149
150 Then use the LevelVar to construct a handler, and make it the default:
151
152         h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})
153         slog.SetDefault(slog.New(h))
154
155 Now the program can change its logging level with a single statement:
156
157         programLevel.Set(slog.LevelDebug)
158
159 # Groups
160
161 Attributes can be collected into groups.
162 A group has a name that is used to qualify the names of its attributes.
163 How this qualification is displayed depends on the handler.
164 [TextHandler] separates the group and attribute names with a dot.
165 [JSONHandler] treats each group as a separate JSON object, with the group name as the key.
166
167 Use [Group] to create a Group Attr from a name and a list of Attrs:
168
169         slog.Group("request",
170             slog.String("method", r.Method),
171             slog.Any("url", r.URL))
172
173 TextHandler would display this group as
174
175         request.method=GET request.url=http://example.com
176
177 JSONHandler would display it as
178
179         "request":{"method":"GET","url":"http://example.com"}
180
181 Use [Logger.WithGroup] to qualify all of a Logger's output
182 with a group name. Calling WithGroup on a Logger results in a
183 new Logger with the same Handler as the original, but with all
184 its attributes qualified by the group name.
185
186 This can help prevent duplicate attribute keys in large systems,
187 where subsystems might use the same keys.
188 Pass each subsystem a different Logger with its own group name so that
189 potential duplicates are qualified:
190
191         logger := slog.Default().With("id", systemID)
192         parserLogger := logger.WithGroup("parser")
193         parseInput(input, parserLogger)
194
195 When parseInput logs with parserLogger, its keys will be qualified with "parser",
196 so even if it uses the common key "id", the log line will have distinct keys.
197
198 # Contexts
199
200 Some handlers may wish to include information from the [context.Context] that is
201 available at the call site. One example of such information
202 is the identifier for the current span when tracing is enabled.
203
204 The [Logger.Log] and [Logger.LogAttrs] methods take a context as a first
205 argument, as do their corresponding top-level functions.
206
207 Although the convenience methods on Logger (Info and so on) and the
208 corresponding top-level functions do not take a context, the alternatives ending
209 in "Ctx" do. For example,
210
211         slog.InfoCtx(ctx, "message")
212
213 It is recommended to pass a context to an output method if one is available.
214
215 # Customizing a type's logging behavior
216
217 If a type implements the [LogValuer] interface, the [Value] returned from its LogValue
218 method is used for logging. You can use this to control how values of the type
219 appear in logs. For example, you can redact secret information like passwords,
220 or gather a struct's fields in a Group. See the examples under [LogValuer] for
221 details.
222
223 A LogValue method may return a Value that itself implements [LogValuer]. The [Value.Resolve]
224 method handles these cases carefully, avoiding infinite loops and unbounded recursion.
225 Handler authors and others may wish to use Value.Resolve instead of calling LogValue directly.
226
227 # Wrapping output methods
228
229 The logger functions use reflection over the call stack to find the file name
230 and line number of the logging call within the application. This can produce
231 incorrect source information for functions that wrap slog. For instance, if you
232 define this function in file mylog.go:
233
234         func Infof(format string, args ...any) {
235             slog.Default().Info(fmt.Sprintf(format, args...))
236         }
237
238 and you call it like this in main.go:
239
240         Infof(slog.Default(), "hello, %s", "world")
241
242 then slog will report the source file as mylog.go, not main.go.
243
244 A correct implementation of Infof will obtain the source location
245 (pc) and pass it to NewRecord.
246 The Infof function in the package-level example called "wrapping"
247 demonstrates how to do this.
248
249 # Working with Records
250
251 Sometimes a Handler will need to modify a Record
252 before passing it on to another Handler or backend.
253 A Record contains a mixture of simple public fields (e.g. Time, Level, Message)
254 and hidden fields that refer to state (such as attributes) indirectly. This
255 means that modifying a simple copy of a Record (e.g. by calling
256 [Record.Add] or [Record.AddAttrs] to add attributes)
257 may have unexpected effects on the original.
258 Before modifying a Record, use [Clone] to
259 create a copy that shares no state with the original,
260 or create a new Record with [NewRecord]
261 and build up its Attrs by traversing the old ones with [Record.Attrs].
262
263 # Performance considerations
264
265 If profiling your application demonstrates that logging is taking significant time,
266 the following suggestions may help.
267
268 If many log lines have a common attribute, use [Logger.With] to create a Logger with
269 that attribute. The built-in handlers will format that attribute only once, at the
270 call to [Logger.With]. The [Handler] interface is designed to allow that optimization,
271 and a well-written Handler should take advantage of it.
272
273 The arguments to a log call are always evaluated, even if the log event is discarded.
274 If possible, defer computation so that it happens only if the value is actually logged.
275 For example, consider the call
276
277         slog.Info("starting request", "url", r.URL.String())  // may compute String unnecessarily
278
279 The URL.String method will be called even if the logger discards Info-level events.
280 Instead, pass the URL directly:
281
282         slog.Info("starting request", "url", &r.URL) // calls URL.String only if needed
283
284 The built-in [TextHandler] will call its String method, but only
285 if the log event is enabled.
286 Avoiding the call to String also preserves the structure of the underlying value.
287 For example [JSONHandler] emits the components of the parsed URL as a JSON object.
288 If you want to avoid eagerly paying the cost of the String call
289 without causing the handler to potentially inspect the structure of the value,
290 wrap the value in a fmt.Stringer implementation that hides its Marshal methods.
291
292 You can also use the [LogValuer] interface to avoid unnecessary work in disabled log
293 calls. Say you need to log some expensive value:
294
295         slog.Debug("frobbing", "value", computeExpensiveValue(arg))
296
297 Even if this line is disabled, computeExpensiveValue will be called.
298 To avoid that, define a type implementing LogValuer:
299
300         type expensive struct { arg int }
301
302         func (e expensive) LogValue() slog.Value {
303             return slog.AnyValue(computeExpensiveValue(e.arg))
304         }
305
306 Then use a value of that type in log calls:
307
308         slog.Debug("frobbing", "value", expensive{arg})
309
310 Now computeExpensiveValue will only be called when the line is enabled.
311
312 The built-in handlers acquire a lock before calling [io.Writer.Write]
313 to ensure that each record is written in one piece. User-defined
314 handlers are responsible for their own locking.
315 */
316 package slog