Source file src/log/slog/logger.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  package slog
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"log"
    11  	loginternal "log/internal"
    12  	"log/slog/internal"
    13  	"runtime"
    14  	"sync/atomic"
    15  	"time"
    16  )
    17  
    18  var defaultLogger atomic.Pointer[Logger]
    19  
    20  var logLoggerLevel LevelVar
    21  
    22  // SetLogLoggerLevel controls the level for the bridge to the [log] package.
    23  //
    24  // Before [SetDefault] is called, slog top-level logging functions call the default [log.Logger].
    25  // In that mode, SetLogLoggerLevel sets the minimum level for those calls.
    26  // By default, the minimum level is Info, so calls to [Debug]
    27  // (as well as top-level logging calls at lower levels)
    28  // will not be passed to the log.Logger. After calling
    29  //
    30  //	slog.SetLogLoggerLevel(slog.LevelDebug)
    31  //
    32  // calls to [Debug] will be passed to the log.Logger.
    33  //
    34  // After [SetDefault] is called, calls to the default [log.Logger] are passed to the
    35  // slog default handler. In that mode,
    36  // SetLogLoggerLevel sets the level at which those calls are logged.
    37  // That is, after calling
    38  //
    39  //	slog.SetLogLoggerLevel(slog.LevelDebug)
    40  //
    41  // A call to [log.Printf] will result in output at level [LevelDebug].
    42  //
    43  // SetLogLoggerLevel returns the previous value.
    44  func SetLogLoggerLevel(level Level) (oldLevel Level) {
    45  	oldLevel = logLoggerLevel.Level()
    46  	logLoggerLevel.Set(level)
    47  	return
    48  }
    49  
    50  func init() {
    51  	defaultLogger.Store(New(newDefaultHandler(loginternal.DefaultOutput)))
    52  }
    53  
    54  // Default returns the default [Logger].
    55  func Default() *Logger { return defaultLogger.Load() }
    56  
    57  // SetDefault makes l the default [Logger], which is used by
    58  // the top-level functions [Info], [Debug] and so on.
    59  // After this call, output from the log package's default Logger
    60  // (as with [log.Print], etc.) will be logged using l's Handler,
    61  // at a level controlled by [SetLogLoggerLevel].
    62  func SetDefault(l *Logger) {
    63  	defaultLogger.Store(l)
    64  	// If the default's handler is a defaultHandler, then don't use a handleWriter,
    65  	// or we'll deadlock as they both try to acquire the log default mutex.
    66  	// The defaultHandler will use whatever the log default writer is currently
    67  	// set to, which is correct.
    68  	// This can occur with SetDefault(Default()).
    69  	// See TestSetDefault.
    70  	if _, ok := l.Handler().(*defaultHandler); !ok {
    71  		capturePC := log.Flags()&(log.Lshortfile|log.Llongfile) != 0
    72  		log.SetOutput(&handlerWriter{l.Handler(), &logLoggerLevel, capturePC})
    73  		log.SetFlags(0) // we want just the log message, no time or location
    74  	}
    75  }
    76  
    77  // handlerWriter is an io.Writer that calls a Handler.
    78  // It is used to link the default log.Logger to the default slog.Logger.
    79  type handlerWriter struct {
    80  	h         Handler
    81  	level     Leveler
    82  	capturePC bool
    83  }
    84  
    85  func (w *handlerWriter) Write(buf []byte) (int, error) {
    86  	level := w.level.Level()
    87  	if !w.h.Enabled(context.Background(), level) {
    88  		return 0, nil
    89  	}
    90  	var pc uintptr
    91  	if !internal.IgnorePC && w.capturePC {
    92  		// skip [runtime.Callers, w.Write, Logger.Output, log.Print]
    93  		var pcs [1]uintptr
    94  		runtime.Callers(4, pcs[:])
    95  		pc = pcs[0]
    96  	}
    97  
    98  	// Remove final newline.
    99  	origLen := len(buf) // Report that the entire buf was written.
   100  	buf = bytes.TrimSuffix(buf, []byte{'\n'})
   101  	r := NewRecord(time.Now(), level, string(buf), pc)
   102  	return origLen, w.h.Handle(context.Background(), r)
   103  }
   104  
   105  // A Logger records structured information about each call to its
   106  // Log, Debug, Info, Warn, and Error methods.
   107  // For each call, it creates a [Record] and passes it to a [Handler].
   108  //
   109  // To create a new Logger, call [New] or a Logger method
   110  // that begins "With".
   111  type Logger struct {
   112  	handler Handler // for structured logging
   113  }
   114  
   115  func (l *Logger) clone() *Logger {
   116  	c := *l
   117  	return &c
   118  }
   119  
   120  // Handler returns l's Handler.
   121  func (l *Logger) Handler() Handler { return l.handler }
   122  
   123  // With returns a Logger that includes the given attributes
   124  // in each output operation. Arguments are converted to
   125  // attributes as if by [Logger.Log].
   126  func (l *Logger) With(args ...any) *Logger {
   127  	if len(args) == 0 {
   128  		return l
   129  	}
   130  	c := l.clone()
   131  	c.handler = l.handler.WithAttrs(argsToAttrSlice(args))
   132  	return c
   133  }
   134  
   135  // WithGroup returns a Logger that starts a group, if name is non-empty.
   136  // The keys of all attributes added to the Logger will be qualified by the given
   137  // name. (How that qualification happens depends on the [Handler.WithGroup]
   138  // method of the Logger's Handler.)
   139  //
   140  // If name is empty, WithGroup returns the receiver.
   141  func (l *Logger) WithGroup(name string) *Logger {
   142  	if name == "" {
   143  		return l
   144  	}
   145  	c := l.clone()
   146  	c.handler = l.handler.WithGroup(name)
   147  	return c
   148  }
   149  
   150  // New creates a new Logger with the given non-nil Handler.
   151  func New(h Handler) *Logger {
   152  	if h == nil {
   153  		panic("nil Handler")
   154  	}
   155  	return &Logger{handler: h}
   156  }
   157  
   158  // With calls [Logger.With] on the default logger.
   159  func With(args ...any) *Logger {
   160  	return Default().With(args...)
   161  }
   162  
   163  // Enabled reports whether l emits log records at the given context and level.
   164  func (l *Logger) Enabled(ctx context.Context, level Level) bool {
   165  	if ctx == nil {
   166  		ctx = context.Background()
   167  	}
   168  	return l.Handler().Enabled(ctx, level)
   169  }
   170  
   171  // NewLogLogger returns a new [log.Logger] such that each call to its Output method
   172  // dispatches a Record to the specified handler. The logger acts as a bridge from
   173  // the older log API to newer structured logging handlers.
   174  func NewLogLogger(h Handler, level Level) *log.Logger {
   175  	return log.New(&handlerWriter{h, level, true}, "", 0)
   176  }
   177  
   178  // Log emits a log record with the current time and the given level and message.
   179  // The Record's Attrs consist of the Logger's attributes followed by
   180  // the Attrs specified by args.
   181  //
   182  // The attribute arguments are processed as follows:
   183  //   - If an argument is an Attr, it is used as is.
   184  //   - If an argument is a string and this is not the last argument,
   185  //     the following argument is treated as the value and the two are combined
   186  //     into an Attr.
   187  //   - Otherwise, the argument is treated as a value with key "!BADKEY".
   188  func (l *Logger) Log(ctx context.Context, level Level, msg string, args ...any) {
   189  	l.log(ctx, level, msg, args...)
   190  }
   191  
   192  // LogAttrs is a more efficient version of [Logger.Log] that accepts only Attrs.
   193  func (l *Logger) LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
   194  	l.logAttrs(ctx, level, msg, attrs...)
   195  }
   196  
   197  // Debug logs at [LevelDebug].
   198  func (l *Logger) Debug(msg string, args ...any) {
   199  	l.log(context.Background(), LevelDebug, msg, args...)
   200  }
   201  
   202  // DebugContext logs at [LevelDebug] with the given context.
   203  func (l *Logger) DebugContext(ctx context.Context, msg string, args ...any) {
   204  	l.log(ctx, LevelDebug, msg, args...)
   205  }
   206  
   207  // Info logs at [LevelInfo].
   208  func (l *Logger) Info(msg string, args ...any) {
   209  	l.log(context.Background(), LevelInfo, msg, args...)
   210  }
   211  
   212  // InfoContext logs at [LevelInfo] with the given context.
   213  func (l *Logger) InfoContext(ctx context.Context, msg string, args ...any) {
   214  	l.log(ctx, LevelInfo, msg, args...)
   215  }
   216  
   217  // Warn logs at [LevelWarn].
   218  func (l *Logger) Warn(msg string, args ...any) {
   219  	l.log(context.Background(), LevelWarn, msg, args...)
   220  }
   221  
   222  // WarnContext logs at [LevelWarn] with the given context.
   223  func (l *Logger) WarnContext(ctx context.Context, msg string, args ...any) {
   224  	l.log(ctx, LevelWarn, msg, args...)
   225  }
   226  
   227  // Error logs at [LevelError].
   228  func (l *Logger) Error(msg string, args ...any) {
   229  	l.log(context.Background(), LevelError, msg, args...)
   230  }
   231  
   232  // ErrorContext logs at [LevelError] with the given context.
   233  func (l *Logger) ErrorContext(ctx context.Context, msg string, args ...any) {
   234  	l.log(ctx, LevelError, msg, args...)
   235  }
   236  
   237  // log is the low-level logging method for methods that take ...any.
   238  // It must always be called directly by an exported logging method
   239  // or function, because it uses a fixed call depth to obtain the pc.
   240  func (l *Logger) log(ctx context.Context, level Level, msg string, args ...any) {
   241  	if !l.Enabled(ctx, level) {
   242  		return
   243  	}
   244  	var pc uintptr
   245  	if !internal.IgnorePC {
   246  		var pcs [1]uintptr
   247  		// skip [runtime.Callers, this function, this function's caller]
   248  		runtime.Callers(3, pcs[:])
   249  		pc = pcs[0]
   250  	}
   251  	r := NewRecord(time.Now(), level, msg, pc)
   252  	r.Add(args...)
   253  	if ctx == nil {
   254  		ctx = context.Background()
   255  	}
   256  	_ = l.Handler().Handle(ctx, r)
   257  }
   258  
   259  // logAttrs is like [Logger.log], but for methods that take ...Attr.
   260  func (l *Logger) logAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
   261  	if !l.Enabled(ctx, level) {
   262  		return
   263  	}
   264  	var pc uintptr
   265  	if !internal.IgnorePC {
   266  		var pcs [1]uintptr
   267  		// skip [runtime.Callers, this function, this function's caller]
   268  		runtime.Callers(3, pcs[:])
   269  		pc = pcs[0]
   270  	}
   271  	r := NewRecord(time.Now(), level, msg, pc)
   272  	r.AddAttrs(attrs...)
   273  	if ctx == nil {
   274  		ctx = context.Background()
   275  	}
   276  	_ = l.Handler().Handle(ctx, r)
   277  }
   278  
   279  // Debug calls [Logger.Debug] on the default logger.
   280  func Debug(msg string, args ...any) {
   281  	Default().log(context.Background(), LevelDebug, msg, args...)
   282  }
   283  
   284  // DebugContext calls [Logger.DebugContext] on the default logger.
   285  func DebugContext(ctx context.Context, msg string, args ...any) {
   286  	Default().log(ctx, LevelDebug, msg, args...)
   287  }
   288  
   289  // Info calls [Logger.Info] on the default logger.
   290  func Info(msg string, args ...any) {
   291  	Default().log(context.Background(), LevelInfo, msg, args...)
   292  }
   293  
   294  // InfoContext calls [Logger.InfoContext] on the default logger.
   295  func InfoContext(ctx context.Context, msg string, args ...any) {
   296  	Default().log(ctx, LevelInfo, msg, args...)
   297  }
   298  
   299  // Warn calls [Logger.Warn] on the default logger.
   300  func Warn(msg string, args ...any) {
   301  	Default().log(context.Background(), LevelWarn, msg, args...)
   302  }
   303  
   304  // WarnContext calls [Logger.WarnContext] on the default logger.
   305  func WarnContext(ctx context.Context, msg string, args ...any) {
   306  	Default().log(ctx, LevelWarn, msg, args...)
   307  }
   308  
   309  // Error calls [Logger.Error] on the default logger.
   310  func Error(msg string, args ...any) {
   311  	Default().log(context.Background(), LevelError, msg, args...)
   312  }
   313  
   314  // ErrorContext calls [Logger.ErrorContext] on the default logger.
   315  func ErrorContext(ctx context.Context, msg string, args ...any) {
   316  	Default().log(ctx, LevelError, msg, args...)
   317  }
   318  
   319  // Log calls [Logger.Log] on the default logger.
   320  func Log(ctx context.Context, level Level, msg string, args ...any) {
   321  	Default().log(ctx, level, msg, args...)
   322  }
   323  
   324  // LogAttrs calls [Logger.LogAttrs] on the default logger.
   325  func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
   326  	Default().logAttrs(ctx, level, msg, attrs...)
   327  }
   328  

View as plain text