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