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

View as plain text