blob: 4beaf8674820a609c39d2cc651843e6aca2b4b5b [file] [log] [blame]
Jonathan Amsterdam85099212022-11-08 15:13:51 -05001// 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
Jonathan Amsterdam85099212022-11-08 15:13:51 -05005/*
6Package slog provides structured logging,
7in which log records include a message,
8a severity level, and various other attributes
9expressed as key-value pairs.
10
11It defines a type, [Logger],
12which provides several methods (such as [Logger.Info] and [Logger.Error])
13for reporting events of interest.
14
15Each Logger is associated with a [Handler].
16A Logger output method creates a [Record] from the method arguments
17and passes it to the Handler, which decides how to handle it.
18There is a default Logger accessible through top-level functions
19(such as [Info] and [Error]) that call the corresponding Logger methods.
20
21A log record consists of a time, a level, a message, and a set of key-value
22pairs, where the keys are strings and the values may be of any type.
23As an example,
24
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -050025 slog.Info("hello", "count", 3)
Jonathan Amsterdam85099212022-11-08 15:13:51 -050026
27creates a record containing the time of the call,
28a level of Info, the message "hello", and a single
29pair with key "count" and value 3.
30
31The [Info] top-level function calls the [Logger.Info] method on the default Logger.
32In addition to [Logger.Info], there are methods for Debug, Warn and Error levels.
33Besides these convenience methods for common levels,
34there is also a [Logger.Log] method which takes the level as an argument.
35Each of these methods has a corresponding top-level function that uses the
36default logger.
37
38The default handler formats the log record's message, time, level, and attributes
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -050039as a string and passes it to the [log] package.
Jonathan Amsterdam85099212022-11-08 15:13:51 -050040
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -050041 2022/11/08 15:28:26 INFO hello count=3
Jonathan Amsterdam85099212022-11-08 15:13:51 -050042
43For more control over the output format, create a logger with a different handler.
44This statement uses [New] to create a new logger with a TextHandler
45that writes structured records in text form to standard error:
46
Jonathan Amsterdamdd950f82023-05-10 16:24:09 -040047 logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
Jonathan Amsterdam85099212022-11-08 15:13:51 -050048
49[TextHandler] output is a sequence of key=value pairs, easily and unambiguously
50parsed by machine. This statement:
51
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -050052 logger.Info("hello", "count", 3)
Jonathan Amsterdam85099212022-11-08 15:13:51 -050053
54produces this output:
55
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -050056 time=2022-11-08T15:28:26.000-05:00 level=INFO msg=hello count=3
Jonathan Amsterdam85099212022-11-08 15:13:51 -050057
58The package also provides [JSONHandler], whose output is line-delimited JSON:
59
Jonathan Amsterdamdd950f82023-05-10 16:24:09 -040060 logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -050061 logger.Info("hello", "count", 3)
Jonathan Amsterdam85099212022-11-08 15:13:51 -050062
63produces this output:
64
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -050065 {"time":"2022-11-08T15:28:26.000000000-05:00","level":"INFO","msg":"hello","count":3}
Jonathan Amsterdam85099212022-11-08 15:13:51 -050066
Jonathan Amsterdamdd950f82023-05-10 16:24:09 -040067Both [TextHandler] and [JSONHandler] can be configured with [HandlerOptions].
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -050068There are options for setting the minimum level (see Levels, below),
69displaying the source file and line of the log call, and
70modifying attributes before they are logged.
71
Jonathan Amsterdam85099212022-11-08 15:13:51 -050072Setting a logger as the default with
73
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -050074 slog.SetDefault(logger)
Jonathan Amsterdam85099212022-11-08 15:13:51 -050075
76will cause the top-level functions like [Info] to use it.
77[SetDefault] also updates the default logger used by the [log] package,
78so that existing applications that use [log.Printf] and related functions
79will send log records to the logger's handler without needing to be rewritten.
80
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -050081Some attributes are common to many log calls.
82For example, you may wish to include the URL or trace identifier of a server request
83with all log events arising from the request.
84Rather than repeat the attribute with every log call, you can use [Logger.With]
85to construct a new Logger containing the attributes:
86
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -050087 logger2 := logger.With("url", r.URL)
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -050088
89The arguments to With are the same key-value pairs used in [Logger.Info].
90The result is a new Logger with the same handler as the original, but additional
91attributes that will appear in the output of every call.
92
Jonathan Amsterdam85099212022-11-08 15:13:51 -050093# Levels
94
Jonathan Amsterdam6dcec332022-11-13 08:36:00 -050095A [Level] is an integer representing the importance or severity of a log event.
96The higher the level, the more severe the event.
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -050097This package defines constants for the most common levels,
Jonathan Amsterdam6dcec332022-11-13 08:36:00 -050098but any int can be used as a level.
99
100In an application, you may wish to log messages only at a certain level or greater.
101One common configuration is to log messages at Info or higher levels,
102suppressing debug logging until it is needed.
103The built-in handlers can be configured with the minimum level to output by
104setting [HandlerOptions.Level].
105The program's `main` function typically does this.
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -0500106The default value is LevelInfo.
Jonathan Amsterdam6dcec332022-11-13 08:36:00 -0500107
108Setting the [HandlerOptions.Level] field to a [Level] value
109fixes the handler's minimum level throughout its lifetime.
110Setting it to a [LevelVar] allows the level to be varied dynamically.
111A LevelVar holds a Level and is safe to read or write from multiple
112goroutines.
113To vary the level dynamically for an entire program, first initialize
114a global LevelVar:
115
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500116 var programLevel = new(slog.LevelVar) // Info by default
Jonathan Amsterdam6dcec332022-11-13 08:36:00 -0500117
118Then use the LevelVar to construct a handler, and make it the default:
119
Jonathan Amsterdamdd950f82023-05-10 16:24:09 -0400120 h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500121 slog.SetDefault(slog.New(h))
Jonathan Amsterdam6dcec332022-11-13 08:36:00 -0500122
123Now the program can change its logging level with a single statement:
124
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500125 programLevel.Set(slog.LevelDebug)
Jonathan Amsterdam6dcec332022-11-13 08:36:00 -0500126
Jonathan Amsterdam85099212022-11-08 15:13:51 -0500127# Groups
128
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -0500129Attributes can be collected into groups.
130A group has a name that is used to qualify the names of its attributes.
131How this qualification is displayed depends on the handler.
132[TextHandler] separates the group and attribute names with a dot.
133[JSONHandler] treats each group as a separate JSON object, with the group name as the key.
134
Jonathan Amsterdamdd950f82023-05-10 16:24:09 -0400135Use [Group] to create a Group attribute from a name and a list of key-value pairs:
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -0500136
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500137 slog.Group("request",
Jonathan Amsterdamdd950f82023-05-10 16:24:09 -0400138 "method", r.Method,
139 "url", r.URL)
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -0500140
141TextHandler would display this group as
142
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500143 request.method=GET request.url=http://example.com
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -0500144
145JSONHandler would display it as
146
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500147 "request":{"method":"GET","url":"http://example.com"}
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -0500148
149Use [Logger.WithGroup] to qualify all of a Logger's output
150with a group name. Calling WithGroup on a Logger results in a
151new Logger with the same Handler as the original, but with all
152its attributes qualified by the group name.
153
154This can help prevent duplicate attribute keys in large systems,
155where subsystems might use the same keys.
156Pass each subsystem a different Logger with its own group name so that
157potential duplicates are qualified:
158
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500159 logger := slog.Default().With("id", systemID)
160 parserLogger := logger.WithGroup("parser")
161 parseInput(input, parserLogger)
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -0500162
163When parseInput logs with parserLogger, its keys will be qualified with "parser",
164so even if it uses the common key "id", the log line will have distinct keys.
165
Jonathan Amsterdam85099212022-11-08 15:13:51 -0500166# Contexts
167
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500168Some handlers may wish to include information from the [context.Context] that is
169available at the call site. One example of such information
Jonathan Amsterdamdd950f82023-05-10 16:24:09 -0400170is the identifier for the current span when tracing is enabled.
Jonathan Amsterdam971e4122022-12-12 07:21:03 -0500171
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500172The [Logger.Log] and [Logger.LogAttrs] methods take a context as a first
173argument, as do their corresponding top-level functions.
Jonathan Amsterdam971e4122022-12-12 07:21:03 -0500174
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500175Although the convenience methods on Logger (Info and so on) and the
176corresponding top-level functions do not take a context, the alternatives ending
Jonathan Amsterdam06a737e2023-07-06 13:10:23 -0400177in "Context" do. For example,
Jonathan Amsterdam971e4122022-12-12 07:21:03 -0500178
Jonathan Amsterdam06a737e2023-07-06 13:10:23 -0400179 slog.InfoContext(ctx, "message")
Jonathan Amsterdam971e4122022-12-12 07:21:03 -0500180
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500181It is recommended to pass a context to an output method if one is available.
Jonathan Amsterdam971e4122022-12-12 07:21:03 -0500182
Jonathan Amsterdamdd950f82023-05-10 16:24:09 -0400183# Attrs and Values
184
185An [Attr] is a key-value pair. The Logger output methods accept Attrs as well as
186alternating keys and values. The statement
187
188 slog.Info("hello", slog.Int("count", 3))
189
190behaves the same as
191
192 slog.Info("hello", "count", 3)
193
194There are convenience constructors for [Attr] such as [Int], [String], and [Bool]
195for common types, as well as the function [Any] for constructing Attrs of any
196type.
197
198The value part of an Attr is a type called [Value].
199Like an [any], a Value can hold any Go value,
200but it can represent typical values, including all numbers and strings,
201without an allocation.
202
203For the most efficient log output, use [Logger.LogAttrs].
204It is similar to [Logger.Log] but accepts only Attrs, not alternating
205keys and values; this allows it, too, to avoid allocation.
206
207The call
208
209 logger.LogAttrs(nil, slog.LevelInfo, "hello", slog.Int("count", 3))
210
211is the most efficient way to achieve the same output as
212
213 slog.Info("hello", "count", 3)
214
Jonathan Amsterdamdb074122023-03-14 13:59:12 -0400215# Customizing a type's logging behavior
Jonathan Amsterdam85099212022-11-08 15:13:51 -0500216
Jonathan Amsterdam02c3fc32022-12-17 11:13:41 -0500217If a type implements the [LogValuer] interface, the [Value] returned from its LogValue
218method is used for logging. You can use this to control how values of the type
219appear in logs. For example, you can redact secret information like passwords,
220or gather a struct's fields in a Group. See the examples under [LogValuer] for
221details.
222
223A LogValue method may return a Value that itself implements [LogValuer]. The [Value.Resolve]
224method handles these cases carefully, avoiding infinite loops and unbounded recursion.
225Handler authors and others may wish to use Value.Resolve instead of calling LogValue directly.
Jonathan Amsterdam85099212022-11-08 15:13:51 -0500226
Jonathan Amsterdamdb074122023-03-14 13:59:12 -0400227# Wrapping output methods
Jonathan Amsterdam85099212022-11-08 15:13:51 -0500228
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500229The logger functions use reflection over the call stack to find the file name
230and line number of the logging call within the application. This can produce
231incorrect source information for functions that wrap slog. For instance, if you
232define this function in file mylog.go:
Jonathan Amsterdam3160c0a2022-12-17 11:33:13 -0500233
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500234 func Infof(format string, args ...any) {
235 slog.Default().Info(fmt.Sprintf(format, args...))
236 }
Jonathan Amsterdam3160c0a2022-12-17 11:33:13 -0500237
238and you call it like this in main.go:
239
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500240 Infof(slog.Default(), "hello, %s", "world")
Jonathan Amsterdam3160c0a2022-12-17 11:33:13 -0500241
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500242then slog will report the source file as mylog.go, not main.go.
Jonathan Amsterdam3160c0a2022-12-17 11:33:13 -0500243
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500244A correct implementation of Infof will obtain the source location
245(pc) and pass it to NewRecord.
246The Infof function in the package-level example called "wrapping"
247demonstrates how to do this.
Jonathan Amsterdam85099212022-11-08 15:13:51 -0500248
Jonathan Amsterdamdb074122023-03-14 13:59:12 -0400249# Working with Records
Jonathan Amsterdam85099212022-11-08 15:13:51 -0500250
Jonathan Amsterdam738e83a2022-12-17 12:47:27 -0500251Sometimes a Handler will need to modify a Record
252before passing it on to another Handler or backend.
253A Record contains a mixture of simple public fields (e.g. Time, Level, Message)
254and hidden fields that refer to state (such as attributes) indirectly. This
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500255means that modifying a simple copy of a Record (e.g. by calling
256[Record.Add] or [Record.AddAttrs] to add attributes)
Jonathan Amsterdam738e83a2022-12-17 12:47:27 -0500257may have unexpected effects on the original.
258Before modifying a Record, use [Clone] to
259create a copy that shares no state with the original,
260or create a new Record with [NewRecord]
261and build up its Attrs by traversing the old ones with [Record.Attrs].
262
Jonathan Amsterdamdb074122023-03-14 13:59:12 -0400263# Performance considerations
Jonathan Amsterdam3c43f8b2022-12-11 20:42:58 -0500264
Jonathan Amsterdam738e83a2022-12-17 12:47:27 -0500265If profiling your application demonstrates that logging is taking significant time,
266the following suggestions may help.
267
268If many log lines have a common attribute, use [Logger.With] to create a Logger with
269that attribute. The built-in handlers will format that attribute only once, at the
270call to [Logger.With]. The [Handler] interface is designed to allow that optimization,
271and a well-written Handler should take advantage of it.
272
273The arguments to a log call are always evaluated, even if the log event is discarded.
274If possible, defer computation so that it happens only if the value is actually logged.
275For example, consider the call
276
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500277 slog.Info("starting request", "url", r.URL.String()) // may compute String unnecessarily
Jonathan Amsterdam738e83a2022-12-17 12:47:27 -0500278
279The URL.String method will be called even if the logger discards Info-level events.
280Instead, pass the URL directly:
281
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500282 slog.Info("starting request", "url", &r.URL) // calls URL.String only if needed
Jonathan Amsterdam738e83a2022-12-17 12:47:27 -0500283
284The built-in [TextHandler] will call its String method, but only
285if the log event is enabled.
286Avoiding the call to String also preserves the structure of the underlying value.
287For example [JSONHandler] emits the components of the parsed URL as a JSON object.
288If you want to avoid eagerly paying the cost of the String call
289without causing the handler to potentially inspect the structure of the value,
290wrap the value in a fmt.Stringer implementation that hides its Marshal methods.
291
292You can also use the [LogValuer] interface to avoid unnecessary work in disabled log
293calls. Say you need to log some expensive value:
294
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500295 slog.Debug("frobbing", "value", computeExpensiveValue(arg))
Jonathan Amsterdam738e83a2022-12-17 12:47:27 -0500296
297Even if this line is disabled, computeExpensiveValue will be called.
298To avoid that, define a type implementing LogValuer:
299
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500300 type expensive struct { arg int }
Jonathan Amsterdam738e83a2022-12-17 12:47:27 -0500301
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500302 func (e expensive) LogValue() slog.Value {
303 return slog.AnyValue(computeExpensiveValue(e.arg))
304 }
Jonathan Amsterdam738e83a2022-12-17 12:47:27 -0500305
306Then use a value of that type in log calls:
307
Jonathan Amsterdamc95f2b42023-02-21 13:20:15 -0500308 slog.Debug("frobbing", "value", expensive{arg})
Jonathan Amsterdam738e83a2022-12-17 12:47:27 -0500309
310Now computeExpensiveValue will only be called when the line is enabled.
Jonathan Amsterdamd38c7dc2023-01-24 09:40:34 -0500311
312The built-in handlers acquire a lock before calling [io.Writer.Write]
313to ensure that each record is written in one piece. User-defined
314handlers are responsible for their own locking.
Jonathan Amsterdam85099212022-11-08 15:13:51 -0500315*/
Piers5e25df02023-01-14 13:17:09 +0000316package slog