golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/slog/doc.go (about)

     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  Some attributes are common to many log calls.
    82  For example, you may wish to include the URL or trace identifier of a server request
    83  with all log events arising from the request.
    84  Rather than repeat the attribute with every log call, you can use [Logger.With]
    85  to construct a new Logger containing the attributes:
    86  
    87  	logger2 := logger.With("url", r.URL)
    88  
    89  The arguments to With are the same key-value pairs used in [Logger.Info].
    90  The result is a new Logger with the same handler as the original, but additional
    91  attributes that will appear in the output of every call.
    92  
    93  # Levels
    94  
    95  A [Level] is an integer representing the importance or severity of a log event.
    96  The higher the level, the more severe the event.
    97  This package defines constants for the most common levels,
    98  but any int can be used as a level.
    99  
   100  In an application, you may wish to log messages only at a certain level or greater.
   101  One common configuration is to log messages at Info or higher levels,
   102  suppressing debug logging until it is needed.
   103  The built-in handlers can be configured with the minimum level to output by
   104  setting [HandlerOptions.Level].
   105  The program's `main` function typically does this.
   106  The default value is LevelInfo.
   107  
   108  Setting the [HandlerOptions.Level] field to a [Level] value
   109  fixes the handler's minimum level throughout its lifetime.
   110  Setting it to a [LevelVar] allows the level to be varied dynamically.
   111  A LevelVar holds a Level and is safe to read or write from multiple
   112  goroutines.
   113  To vary the level dynamically for an entire program, first initialize
   114  a global LevelVar:
   115  
   116  	var programLevel = new(slog.LevelVar) // Info by default
   117  
   118  Then use the LevelVar to construct a handler, and make it the default:
   119  
   120  	h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})
   121  	slog.SetDefault(slog.New(h))
   122  
   123  Now the program can change its logging level with a single statement:
   124  
   125  	programLevel.Set(slog.LevelDebug)
   126  
   127  # Groups
   128  
   129  Attributes can be collected into groups.
   130  A group has a name that is used to qualify the names of its attributes.
   131  How 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  
   135  Use [Group] to create a Group attribute from a name and a list of key-value pairs:
   136  
   137  	slog.Group("request",
   138  	    "method", r.Method,
   139  	    "url", r.URL)
   140  
   141  TextHandler would display this group as
   142  
   143  	request.method=GET request.url=http://example.com
   144  
   145  JSONHandler would display it as
   146  
   147  	"request":{"method":"GET","url":"http://example.com"}
   148  
   149  Use [Logger.WithGroup] to qualify all of a Logger's output
   150  with a group name. Calling WithGroup on a Logger results in a
   151  new Logger with the same Handler as the original, but with all
   152  its attributes qualified by the group name.
   153  
   154  This can help prevent duplicate attribute keys in large systems,
   155  where subsystems might use the same keys.
   156  Pass each subsystem a different Logger with its own group name so that
   157  potential duplicates are qualified:
   158  
   159  	logger := slog.Default().With("id", systemID)
   160  	parserLogger := logger.WithGroup("parser")
   161  	parseInput(input, parserLogger)
   162  
   163  When parseInput logs with parserLogger, its keys will be qualified with "parser",
   164  so even if it uses the common key "id", the log line will have distinct keys.
   165  
   166  # Contexts
   167  
   168  Some handlers may wish to include information from the [context.Context] that is
   169  available at the call site. One example of such information
   170  is the identifier for the current span when tracing is enabled.
   171  
   172  The [Logger.Log] and [Logger.LogAttrs] methods take a context as a first
   173  argument, as do their corresponding top-level functions.
   174  
   175  Although the convenience methods on Logger (Info and so on) and the
   176  corresponding top-level functions do not take a context, the alternatives ending
   177  in "Context" do. For example,
   178  
   179  	slog.InfoContext(ctx, "message")
   180  
   181  It is recommended to pass a context to an output method if one is available.
   182  
   183  # Attrs and Values
   184  
   185  An [Attr] is a key-value pair. The Logger output methods accept Attrs as well as
   186  alternating keys and values. The statement
   187  
   188  	slog.Info("hello", slog.Int("count", 3))
   189  
   190  behaves the same as
   191  
   192  	slog.Info("hello", "count", 3)
   193  
   194  There are convenience constructors for [Attr] such as [Int], [String], and [Bool]
   195  for common types, as well as the function [Any] for constructing Attrs of any
   196  type.
   197  
   198  The value part of an Attr is a type called [Value].
   199  Like an [any], a Value can hold any Go value,
   200  but it can represent typical values, including all numbers and strings,
   201  without an allocation.
   202  
   203  For the most efficient log output, use [Logger.LogAttrs].
   204  It is similar to [Logger.Log] but accepts only Attrs, not alternating
   205  keys and values; this allows it, too, to avoid allocation.
   206  
   207  The call
   208  
   209  	logger.LogAttrs(nil, slog.LevelInfo, "hello", slog.Int("count", 3))
   210  
   211  is the most efficient way to achieve the same output as
   212  
   213  	slog.Info("hello", "count", 3)
   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