| // 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 ( |
| "context" |
| "log" |
| "runtime" |
| "sync/atomic" |
| "time" |
| |
| "golang.org/x/exp/slog/internal" |
| ) |
| |
| var defaultLogger atomic.Value |
| |
| func init() { |
| defaultLogger.Store(New(newDefaultHandler(log.Output))) |
| } |
| |
| // Default returns the default Logger. |
| func Default() *Logger { return defaultLogger.Load().(*Logger) } |
| |
| // SetDefault makes l the default Logger. |
| // After this call, output from the log package's default Logger |
| // (as with [log.Print], etc.) will be logged at LevelInfo using l's Handler. |
| 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(), LevelInfo, 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 Level |
| capturePC bool |
| } |
| |
| func (w *handlerWriter) Write(buf []byte) (int, error) { |
| if !w.h.Enabled(context.Background(), w.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. |
| if len(buf) > 0 && buf[len(buf)-1] == '\n' { |
| buf = buf[:len(buf)-1] |
| } |
| r := NewRecord(time.Now(), w.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 new Logger that includes the given arguments, converted to |
| // Attrs as in [Logger.Log]. |
| // The Attrs will be added to each output from the Logger. |
| // The new Logger shares the old Logger's context. |
| // The new Logger's handler is the result of calling WithAttrs on the receiver's |
| // handler. |
| func (l *Logger) With(args ...any) *Logger { |
| c := l.clone() |
| c.handler = l.handler.WithAttrs(argsToAttrSlice(args)) |
| return c |
| } |
| |
| // WithGroup returns a new Logger that starts a group. 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.) |
| // The new Logger shares the old Logger's context. |
| // |
| // The new Logger's handler is the result of calling WithGroup on the receiver's |
| // handler. |
| func (l *Logger) WithGroup(name string) *Logger { |
| c := l.clone() |
| c.handler = l.handler.WithGroup(name) |
| return c |
| |
| } |
| |
| // New creates a new Logger with the given non-nil Handler and a nil context. |
| 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(nil, 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...) |
| } |
| |
| // DebugCtx logs at LevelDebug with the given context. |
| // Deprecated: Use Logger.DebugContext. |
| func (l *Logger) DebugCtx(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(nil, 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...) |
| } |
| |
| // InfoCtx logs at LevelInfo with the given context. |
| // Deprecated: Use Logger.InfoContext. |
| func (l *Logger) InfoCtx(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(nil, 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...) |
| } |
| |
| // WarnCtx logs at LevelWarn with the given context. |
| // Deprecated: Use Logger.WarnContext. |
| func (l *Logger) WarnCtx(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(nil, 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...) |
| } |
| |
| // ErrorCtx logs at LevelError with the given context. |
| // Deprecated: Use Logger.ErrorContext. |
| func (l *Logger) ErrorCtx(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 !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...) |
| if ctx == nil { |
| ctx = context.Background() |
| } |
| _ = 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 !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...) |
| if ctx == nil { |
| ctx = context.Background() |
| } |
| _ = l.Handler().Handle(ctx, r) |
| } |
| |
| // Debug calls Logger.Debug on the default logger. |
| func Debug(msg string, args ...any) { |
| Default().log(nil, 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(nil, 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(nil, 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(nil, 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...) |
| } |
| |
| // DebugCtx calls Logger.DebugContext on the default logger. |
| // Deprecated: call DebugContext. |
| func DebugCtx(ctx context.Context, msg string, args ...any) { |
| Default().log(ctx, LevelDebug, msg, args...) |
| } |
| |
| // InfoCtx calls Logger.InfoContext on the default logger. |
| // Deprecated: call InfoContext. |
| func InfoCtx(ctx context.Context, msg string, args ...any) { |
| Default().log(ctx, LevelInfo, msg, args...) |
| } |
| |
| // WarnCtx calls Logger.WarnContext on the default logger. |
| // Deprecated: call WarnContext. |
| func WarnCtx(ctx context.Context, msg string, args ...any) { |
| Default().log(ctx, LevelWarn, msg, args...) |
| } |
| |
| // ErrorCtx calls Logger.ErrorContext on the default logger. |
| // Deprecated: call ErrorContext. |
| func ErrorCtx(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...) |
| } |