File: logger.go

package info (click to toggle)
golang-golang-x-exp 0.0~git20221028.83b7d23-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bookworm-backports
  • size: 6,276 kB
  • sloc: ansic: 1,900; sh: 278; objc: 276; asm: 48; makefile: 14
file content (230 lines) | stat: -rw-r--r-- 6,987 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// 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"
	"sync/atomic"
	"time"
)

var defaultLogger atomic.Value

func init() {
	defaultLogger.Store(Logger{
		handler: 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 InfoLevel 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 {
		log.SetOutput(&handlerWriter{l.Handler(), log.Flags()})
		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
	flags int
}

func (w *handlerWriter) Write(buf []byte) (int, error) {
	var depth int
	if w.flags&(log.Lshortfile|log.Llongfile) != 0 {
		depth = 2
	}
	// 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(), InfoLevel, string(buf), depth, nil)
	return origLen, w.h.Handle(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
	ctx     context.Context
}

// Handler returns l's Handler.
func (l Logger) Handler() Handler { return l.handler }

// Context returns l's context.
func (l Logger) Context() context.Context { return l.ctx }

// With returns a new Logger that includes the given arguments, converted to
// Attrs as in [Logger.Log]. The Attrs will be prepended to each output from the
// Logger.
//
// The new Logger's handler is the result of calling WithAttrs on the receiver's
// handler.
func (l Logger) With(args ...any) Logger {
	var (
		attr  Attr
		attrs []Attr
	)
	for len(args) > 0 {
		attr, args = argsToAttr(args)
		attrs = append(attrs, attr)
	}
	l.handler = l.handler.WithAttrs(attrs)
	return l
}

// 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.
func (l Logger) WithGroup(name string) Logger {
	l.handler = l.handler.WithGroup(name)
	return l
}

// WithContext returns a new Logger with the same handler
// as the receiver and the given context.
func (l Logger) WithContext(ctx context.Context) Logger {
	l.ctx = ctx
	return l
}

// New creates a new Logger with the given Handler.
func New(h Handler) Logger { 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 level.
func (l Logger) Enabled(level Level) bool {
	return l.Handler().Enabled(level)
}

// 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(level Level, msg string, args ...any) {
	l.LogDepth(0, level, msg, args...)
}

// LogDepth is like [Logger.Log], but accepts a call depth to adjust the
// file and line number in the log record. 0 refers to the caller
// of LogDepth; 1 refers to the caller's caller; and so on.
func (l Logger) LogDepth(calldepth int, level Level, msg string, args ...any) {
	if !l.Enabled(level) {
		return
	}
	r := l.makeRecord(msg, level, calldepth)
	r.setAttrsFromArgs(args)
	_ = l.Handler().Handle(r)
}

func (l Logger) makeRecord(msg string, level Level, depth int) Record {
	return NewRecord(time.Now(), level, msg, depth+5, l.ctx)
}

// LogAttrs is a more efficient version of [Logger.Log] that accepts only Attrs.
func (l Logger) LogAttrs(level Level, msg string, attrs ...Attr) {
	l.LogAttrsDepth(0, level, msg, attrs...)
}

// LogAttrsDepth is like [Logger.LogAttrs], but accepts a call depth argument
// which it interprets like [Logger.LogDepth].
func (l Logger) LogAttrsDepth(calldepth int, level Level, msg string, attrs ...Attr) {
	if !l.Enabled(level) {
		return
	}
	r := l.makeRecord(msg, level, calldepth)
	r.AddAttrs(attrs...)
	_ = l.Handler().Handle(r)
}

// Debug logs at DebugLevel.
func (l Logger) Debug(msg string, args ...any) {
	l.LogDepth(0, DebugLevel, msg, args...)
}

// Info logs at InfoLevel.
func (l Logger) Info(msg string, args ...any) {
	l.LogDepth(0, InfoLevel, msg, args...)
}

// Warn logs at WarnLevel.
func (l Logger) Warn(msg string, args ...any) {
	l.LogDepth(0, WarnLevel, msg, args...)
}

// Error logs at ErrorLevel.
// If err is non-nil, Error appends Any("err", err)
// to the list of attributes.
func (l Logger) Error(msg string, err error, args ...any) {
	if err != nil {
		// TODO: avoid the copy.
		args = append(args[:len(args):len(args)], Any("err", err))
	}
	l.LogDepth(0, ErrorLevel, msg, args...)
}

// Debug calls Logger.Debug on the default logger.
func Debug(msg string, args ...any) {
	Default().LogDepth(0, DebugLevel, msg, args...)
}

// Info calls Logger.Info on the default logger.
func Info(msg string, args ...any) {
	Default().LogDepth(0, InfoLevel, msg, args...)
}

// Warn calls Logger.Warn on the default logger.
func Warn(msg string, args ...any) {
	Default().LogDepth(0, WarnLevel, msg, args...)
}

// Error calls Logger.Error on the default logger.
func Error(msg string, err error, args ...any) {
	if err != nil {
		// TODO: avoid the copy.
		args = append(args[:len(args):len(args)], Any("err", err))
	}
	Default().LogDepth(0, ErrorLevel, msg, args...)
}

// Log calls Logger.Log on the default logger.
func Log(level Level, msg string, args ...any) {
	Default().LogDepth(0, level, msg, args...)
}

// LogAttrs calls Logger.LogAttrs on the default logger.
func LogAttrs(level Level, msg string, attrs ...Attr) {
	Default().LogAttrsDepth(0, level, msg, attrs...)
}