| // Copyright 2022 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package slog | |
| import ( | |
| "bytes" | |
| "context" | |
| "log" | |
| loginternal "log/internal" | |
| "log/slog/internal" | |
| "runtime" | |
| "sync/atomic" | |
| "time" | |
| ) | |
| var defaultLogger atomic.Pointer[Logger] | |
| var logLoggerLevel LevelVar | |
| // SetLogLoggerLevel controls the level for the bridge to the [log] package. | |
| // | |
| // Before [SetDefault] is called, slog top-level logging functions call the default [log.Logger]. | |
| // In that mode, SetLogLoggerLevel sets the minimum level for those calls. | |
| // By default, the minimum level is Info, so calls to [Debug] | |
| // (as well as top-level logging calls at lower levels) | |
| // will not be passed to the log.Logger. After calling | |
| // | |
| // slog.SetLogLoggerLevel(slog.LevelDebug) | |
| // | |
| // calls to [Debug] will be passed to the log.Logger. | |
| // | |
| // After [SetDefault] is called, calls to the default [log.Logger] are passed to the | |
| // slog default handler. In that mode, | |
| // SetLogLoggerLevel sets the level at which those calls are logged. | |
| // That is, after calling | |
| // | |
| // slog.SetLogLoggerLevel(slog.LevelDebug) | |
| // | |
| // A call to [log.Printf] will result in output at level [LevelDebug]. | |
| // | |
| // SetLogLoggerLevel returns the previous value. | |
| func SetLogLoggerLevel(level Level) (oldLevel Level) { | |
| oldLevel = logLoggerLevel.Level() | |
| logLoggerLevel.Set(level) | |
| return | |
| } | |
| func init() { | |
| defaultLogger.Store(New(newDefaultHandler(loginternal.DefaultOutput))) | |
| } | |
| // Default returns the default [Logger]. | |
| func Default() *Logger { return defaultLogger.Load() } | |
| // SetDefault makes l the default [Logger], which is used by | |
| // the top-level functions [Info], [Debug] and so on. | |
| // After this call, output from the log package's default Logger | |
| // (as with [log.Print], etc.) will be logged using l's Handler, | |
| // at a level controlled by [SetLogLoggerLevel]. | |
| func SetDefault(l *Logger) { | |
| defaultLogger.Store(l) | |
| // If the default's handler is a defaultHandler, then don't use a handleWriter, | |
| // or we'll deadlock as they both try to acquire the log default mutex. | |
| // The defaultHandler will use whatever the log default writer is currently | |
| // set to, which is correct. | |
| // This can occur with SetDefault(Default()). | |
| // See TestSetDefault. | |
| if _, ok := l.Handler().(*defaultHandler); !ok { | |
| capturePC := log.Flags()&(log.Lshortfile|log.Llongfile) != 0 | |
| log.SetOutput(&handlerWriter{l.Handler(), &logLoggerLevel, capturePC}) | |
| log.SetFlags(0) // we want just the log message, no time or location | |
| } | |
| } | |
| // handlerWriter is an io.Writer that calls a Handler. | |
| // It is used to link the default log.Logger to the default slog.Logger. | |
| type handlerWriter struct { | |
| h Handler | |
| level Leveler | |
| capturePC bool | |
| } | |
| func (w *handlerWriter) Write(buf []byte) (int, error) { | |
| level := w.level.Level() | |
| if !w.h.Enabled(context.Background(), level) { | |
| return 0, nil | |
| } | |
| var pc uintptr | |
| if !internal.IgnorePC && w.capturePC { | |
| // skip [runtime.Callers, w.Write, Logger.Output, log.Print] | |
| var pcs [1]uintptr | |
| runtime.Callers(4, pcs[:]) | |
| pc = pcs[0] | |
| } | |
| // Remove final newline. | |
| origLen := len(buf) // Report that the entire buf was written. | |
| buf = bytes.TrimSuffix(buf, []byte{'\n'}) | |
| r := NewRecord(time.Now(), level, string(buf), pc) | |
| return origLen, w.h.Handle(context.Background(), r) | |
| } | |
| // A Logger records structured information about each call to its | |
| // Log, Debug, Info, Warn, and Error methods. | |
| // For each call, it creates a [Record] and passes it to a [Handler]. | |
| // | |
| // To create a new Logger, call [New] or a Logger method | |
| // that begins "With". | |
| type Logger struct { | |
| handler Handler // for structured logging | |
| } | |
| func (l *Logger) clone() *Logger { | |
| c := *l | |
| return &c | |
| } | |
| // Handler returns l's Handler. | |
| func (l *Logger) Handler() Handler { return l.handler } | |
| // With returns a Logger that includes the given attributes | |
| // in each output operation. Arguments are converted to | |
| // attributes as if by [Logger.Log]. | |
| func (l *Logger) With(args ...any) *Logger { | |
| if len(args) == 0 { | |
| return l | |
| } | |
| c := l.clone() | |
| c.handler = l.handler.WithAttrs(argsToAttrSlice(args)) | |
| return c | |
| } | |
| // WithGroup returns a Logger that starts a group, if name is non-empty. | |
| // The keys of all attributes added to the Logger will be qualified by the given | |
| // name. (How that qualification happens depends on the [Handler.WithGroup] | |
| // method of the Logger's Handler.) | |
| // | |
| // If name is empty, WithGroup returns the receiver. | |
| func (l *Logger) WithGroup(name string) *Logger { | |
| if name == "" { | |
| return l | |
| } | |
| c := l.clone() | |
| c.handler = l.handler.WithGroup(name) | |
| return c | |
| } | |
| // New creates a new Logger with the given non-nil Handler. | |
| func New(h Handler) *Logger { | |
| if h == nil { | |
| panic("nil Handler") | |
| } | |
| return &Logger{handler: h} | |
| } | |
| // With calls [Logger.With] on the default logger. | |
| func With(args ...any) *Logger { | |
| return Default().With(args...) | |
| } | |
| // Enabled reports whether l emits log records at the given context and level. | |
| func (l *Logger) Enabled(ctx context.Context, level Level) bool { | |
| if ctx == nil { | |
| ctx = context.Background() | |
| } | |
| return l.Handler().Enabled(ctx, level) | |
| } | |
| // NewLogLogger returns a new [log.Logger] such that each call to its Output method | |
| // dispatches a Record to the specified handler. The logger acts as a bridge from | |
| // the older log API to newer structured logging handlers. | |
| func NewLogLogger(h Handler, level Level) *log.Logger { | |
| return log.New(&handlerWriter{h, level, true}, "", 0) | |
| } | |
| // Log emits a log record with the current time and the given level and message. | |
| // The Record's Attrs consist of the Logger's attributes followed by | |
| // the Attrs specified by args. | |
| // | |
| // The attribute arguments are processed as follows: | |
| // - If an argument is an Attr, it is used as is. | |
| // - If an argument is a string and this is not the last argument, | |
| // the following argument is treated as the value and the two are combined | |
| // into an Attr. | |
| // - Otherwise, the argument is treated as a value with key "!BADKEY". | |
| func (l *Logger) Log(ctx context.Context, level Level, msg string, args ...any) { | |
| l.log(ctx, level, msg, args...) | |
| } | |
| // LogAttrs is a more efficient version of [Logger.Log] that accepts only Attrs. | |
| func (l *Logger) LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) { | |
| l.logAttrs(ctx, level, msg, attrs...) | |
| } | |
| // Debug logs at [LevelDebug]. | |
| func (l *Logger) Debug(msg string, args ...any) { | |
| l.log(context.Background(), LevelDebug, msg, args...) | |
| } | |
| // DebugContext logs at [LevelDebug] with the given context. | |
| func (l *Logger) DebugContext(ctx context.Context, msg string, args ...any) { | |
| l.log(ctx, LevelDebug, msg, args...) | |
| } | |
| // Info logs at [LevelInfo]. | |
| func (l *Logger) Info(msg string, args ...any) { | |
| l.log(context.Background(), LevelInfo, msg, args...) | |
| } | |
| // InfoContext logs at [LevelInfo] with the given context. | |
| func (l *Logger) InfoContext(ctx context.Context, msg string, args ...any) { | |
| l.log(ctx, LevelInfo, msg, args...) | |
| } | |
| // Warn logs at [LevelWarn]. | |
| func (l *Logger) Warn(msg string, args ...any) { | |
| l.log(context.Background(), LevelWarn, msg, args...) | |
| } | |
| // WarnContext logs at [LevelWarn] with the given context. | |
| func (l *Logger) WarnContext(ctx context.Context, msg string, args ...any) { | |
| l.log(ctx, LevelWarn, msg, args...) | |
| } | |
| // Error logs at [LevelError]. | |
| func (l *Logger) Error(msg string, args ...any) { | |
| l.log(context.Background(), LevelError, msg, args...) | |
| } | |
| // ErrorContext logs at [LevelError] with the given context. | |
| func (l *Logger) ErrorContext(ctx context.Context, msg string, args ...any) { | |
| l.log(ctx, LevelError, msg, args...) | |
| } | |
| // log is the low-level logging method for methods that take ...any. | |
| // It must always be called directly by an exported logging method | |
| // or function, because it uses a fixed call depth to obtain the pc. | |
| func (l *Logger) log(ctx context.Context, level Level, msg string, args ...any) { | |
| if ctx == nil { | |
| ctx = context.Background() | |
| } | |
| if !l.Enabled(ctx, level) { | |
| return | |
| } | |
| var pc uintptr | |
| if !internal.IgnorePC { | |
| var pcs [1]uintptr | |
| // skip [runtime.Callers, this function, this function's caller] | |
| runtime.Callers(3, pcs[:]) | |
| pc = pcs[0] | |
| } | |
| r := NewRecord(time.Now(), level, msg, pc) | |
| r.Add(args...) | |
| _ = l.Handler().Handle(ctx, r) | |
| } | |
| // logAttrs is like [Logger.log], but for methods that take ...Attr. | |
| func (l *Logger) logAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) { | |
| if ctx == nil { | |
| ctx = context.Background() | |
| } | |
| if !l.Enabled(ctx, level) { | |
| return | |
| } | |
| var pc uintptr | |
| if !internal.IgnorePC { | |
| var pcs [1]uintptr | |
| // skip [runtime.Callers, this function, this function's caller] | |
| runtime.Callers(3, pcs[:]) | |
| pc = pcs[0] | |
| } | |
| r := NewRecord(time.Now(), level, msg, pc) | |
| r.AddAttrs(attrs...) | |
| _ = l.Handler().Handle(ctx, r) | |
| } | |
| // Debug calls [Logger.Debug] on the default logger. | |
| func Debug(msg string, args ...any) { | |
| Default().log(context.Background(), LevelDebug, msg, args...) | |
| } | |
| // DebugContext calls [Logger.DebugContext] on the default logger. | |
| func DebugContext(ctx context.Context, msg string, args ...any) { | |
| Default().log(ctx, LevelDebug, msg, args...) | |
| } | |
| // Info calls [Logger.Info] on the default logger. | |
| func Info(msg string, args ...any) { | |
| Default().log(context.Background(), LevelInfo, msg, args...) | |
| } | |
| // InfoContext calls [Logger.InfoContext] on the default logger. | |
| func InfoContext(ctx context.Context, msg string, args ...any) { | |
| Default().log(ctx, LevelInfo, msg, args...) | |
| } | |
| // Warn calls [Logger.Warn] on the default logger. | |
| func Warn(msg string, args ...any) { | |
| Default().log(context.Background(), LevelWarn, msg, args...) | |
| } | |
| // WarnContext calls [Logger.WarnContext] on the default logger. | |
| func WarnContext(ctx context.Context, msg string, args ...any) { | |
| Default().log(ctx, LevelWarn, msg, args...) | |
| } | |
| // Error calls [Logger.Error] on the default logger. | |
| func Error(msg string, args ...any) { | |
| Default().log(context.Background(), LevelError, msg, args...) | |
| } | |
| // ErrorContext calls [Logger.ErrorContext] on the default logger. | |
| func ErrorContext(ctx context.Context, msg string, args ...any) { | |
| Default().log(ctx, LevelError, msg, args...) | |
| } | |
| // Log calls [Logger.Log] on the default logger. | |
| func Log(ctx context.Context, level Level, msg string, args ...any) { | |
| Default().log(ctx, level, msg, args...) | |
| } | |
| // LogAttrs calls [Logger.LogAttrs] on the default logger. | |
| func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) { | |
| Default().logAttrs(ctx, level, msg, attrs...) | |
| } | |