]> Cypherpunks.ru repositories - gostls13.git/blob - src/log/slog/logger.go
fceafe0cbad87ff27864a9822b88f2802bec36b2
[gostls13.git] / 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 func init() {
20         defaultLogger.Store(New(newDefaultHandler(loginternal.DefaultOutput)))
21 }
22
23 // Default returns the default [Logger].
24 func Default() *Logger { return defaultLogger.Load() }
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 Logger that includes the given attributes
92 // in each output operation. Arguments are converted to
93 // attributes as if by [Logger.Log].
94 func (l *Logger) With(args ...any) *Logger {
95         if len(args) == 0 {
96                 return l
97         }
98         c := l.clone()
99         c.handler = l.handler.WithAttrs(argsToAttrSlice(args))
100         return c
101 }
102
103 // WithGroup returns a Logger that starts a group, if name is non-empty.
104 // The keys of all attributes added to the Logger will be qualified by the given
105 // name. (How that qualification happens depends on the [Handler.WithGroup]
106 // method of the Logger's Handler.)
107 //
108 // If name is empty, WithGroup returns the receiver.
109 func (l *Logger) WithGroup(name string) *Logger {
110         if name == "" {
111                 return l
112         }
113         c := l.clone()
114         c.handler = l.handler.WithGroup(name)
115         return c
116
117 }
118
119 // New creates a new Logger with the given non-nil Handler.
120 func New(h Handler) *Logger {
121         if h == nil {
122                 panic("nil Handler")
123         }
124         return &Logger{handler: h}
125 }
126
127 // With calls [Logger.With] on the default logger.
128 func With(args ...any) *Logger {
129         return Default().With(args...)
130 }
131
132 // Enabled reports whether l emits log records at the given context and level.
133 func (l *Logger) Enabled(ctx context.Context, level Level) bool {
134         if ctx == nil {
135                 ctx = context.Background()
136         }
137         return l.Handler().Enabled(ctx, level)
138 }
139
140 // NewLogLogger returns a new [log.Logger] such that each call to its Output method
141 // dispatches a Record to the specified handler. The logger acts as a bridge from
142 // the older log API to newer structured logging handlers.
143 func NewLogLogger(h Handler, level Level) *log.Logger {
144         return log.New(&handlerWriter{h, level, true}, "", 0)
145 }
146
147 // Log emits a log record with the current time and the given level and message.
148 // The Record's Attrs consist of the Logger's attributes followed by
149 // the Attrs specified by args.
150 //
151 // The attribute arguments are processed as follows:
152 //   - If an argument is an Attr, it is used as is.
153 //   - If an argument is a string and this is not the last argument,
154 //     the following argument is treated as the value and the two are combined
155 //     into an Attr.
156 //   - Otherwise, the argument is treated as a value with key "!BADKEY".
157 func (l *Logger) Log(ctx context.Context, level Level, msg string, args ...any) {
158         l.log(ctx, level, msg, args...)
159 }
160
161 // LogAttrs is a more efficient version of [Logger.Log] that accepts only Attrs.
162 func (l *Logger) LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
163         l.logAttrs(ctx, level, msg, attrs...)
164 }
165
166 // Debug logs at [LevelDebug].
167 func (l *Logger) Debug(msg string, args ...any) {
168         l.log(context.Background(), LevelDebug, msg, args...)
169 }
170
171 // DebugContext logs at [LevelDebug] with the given context.
172 func (l *Logger) DebugContext(ctx context.Context, msg string, args ...any) {
173         l.log(ctx, LevelDebug, msg, args...)
174 }
175
176 // Info logs at [LevelInfo].
177 func (l *Logger) Info(msg string, args ...any) {
178         l.log(context.Background(), LevelInfo, msg, args...)
179 }
180
181 // InfoContext logs at [LevelInfo] with the given context.
182 func (l *Logger) InfoContext(ctx context.Context, msg string, args ...any) {
183         l.log(ctx, LevelInfo, msg, args...)
184 }
185
186 // Warn logs at [LevelWarn].
187 func (l *Logger) Warn(msg string, args ...any) {
188         l.log(context.Background(), LevelWarn, msg, args...)
189 }
190
191 // WarnContext logs at [LevelWarn] with the given context.
192 func (l *Logger) WarnContext(ctx context.Context, msg string, args ...any) {
193         l.log(ctx, LevelWarn, msg, args...)
194 }
195
196 // Error logs at [LevelError].
197 func (l *Logger) Error(msg string, args ...any) {
198         l.log(context.Background(), LevelError, msg, args...)
199 }
200
201 // ErrorContext logs at [LevelError] with the given context.
202 func (l *Logger) ErrorContext(ctx context.Context, msg string, args ...any) {
203         l.log(ctx, LevelError, msg, args...)
204 }
205
206 // log is the low-level logging method for methods that take ...any.
207 // It must always be called directly by an exported logging method
208 // or function, because it uses a fixed call depth to obtain the pc.
209 func (l *Logger) log(ctx context.Context, level Level, msg string, args ...any) {
210         if !l.Enabled(ctx, level) {
211                 return
212         }
213         var pc uintptr
214         if !internal.IgnorePC {
215                 var pcs [1]uintptr
216                 // skip [runtime.Callers, this function, this function's caller]
217                 runtime.Callers(3, pcs[:])
218                 pc = pcs[0]
219         }
220         r := NewRecord(time.Now(), level, msg, pc)
221         r.Add(args...)
222         if ctx == nil {
223                 ctx = context.Background()
224         }
225         _ = l.Handler().Handle(ctx, r)
226 }
227
228 // logAttrs is like [Logger.log], but for methods that take ...Attr.
229 func (l *Logger) logAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
230         if !l.Enabled(ctx, level) {
231                 return
232         }
233         var pc uintptr
234         if !internal.IgnorePC {
235                 var pcs [1]uintptr
236                 // skip [runtime.Callers, this function, this function's caller]
237                 runtime.Callers(3, pcs[:])
238                 pc = pcs[0]
239         }
240         r := NewRecord(time.Now(), level, msg, pc)
241         r.AddAttrs(attrs...)
242         if ctx == nil {
243                 ctx = context.Background()
244         }
245         _ = l.Handler().Handle(ctx, r)
246 }
247
248 // Debug calls [Logger.Debug] on the default logger.
249 func Debug(msg string, args ...any) {
250         Default().log(context.Background(), LevelDebug, msg, args...)
251 }
252
253 // DebugContext calls [Logger.DebugContext] on the default logger.
254 func DebugContext(ctx context.Context, msg string, args ...any) {
255         Default().log(ctx, LevelDebug, msg, args...)
256 }
257
258 // Info calls [Logger.Info] on the default logger.
259 func Info(msg string, args ...any) {
260         Default().log(context.Background(), LevelInfo, msg, args...)
261 }
262
263 // InfoContext calls [Logger.InfoContext] on the default logger.
264 func InfoContext(ctx context.Context, msg string, args ...any) {
265         Default().log(ctx, LevelInfo, msg, args...)
266 }
267
268 // Warn calls [Logger.Warn] on the default logger.
269 func Warn(msg string, args ...any) {
270         Default().log(context.Background(), LevelWarn, msg, args...)
271 }
272
273 // WarnContext calls [Logger.WarnContext] on the default logger.
274 func WarnContext(ctx context.Context, msg string, args ...any) {
275         Default().log(ctx, LevelWarn, msg, args...)
276 }
277
278 // Error calls [Logger.Error] on the default logger.
279 func Error(msg string, args ...any) {
280         Default().log(context.Background(), LevelError, msg, args...)
281 }
282
283 // ErrorContext calls [Logger.ErrorContext] on the default logger.
284 func ErrorContext(ctx context.Context, msg string, args ...any) {
285         Default().log(ctx, LevelError, msg, args...)
286 }
287
288 // Log calls [Logger.Log] on the default logger.
289 func Log(ctx context.Context, level Level, msg string, args ...any) {
290         Default().log(ctx, level, msg, args...)
291 }
292
293 // LogAttrs calls [Logger.LogAttrs] on the default logger.
294 func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
295         Default().logAttrs(ctx, level, msg, attrs...)
296 }