Skip to content

Commit 91042ec

Browse files
committed
slog: Logger
The Logger type and its methods. Change-Id: I2447ec7a3dab5c10c8851adcc2b5ae8e7b8e210d Reviewed-on: https://go-review.googlesource.com/c/exp/+/429436 Reviewed-by: Alan Donovan <[email protected]> Run-TryBot: Jonathan Amsterdam <[email protected]>
1 parent 60527bc commit 91042ec

File tree

3 files changed

+582
-0
lines changed

3 files changed

+582
-0
lines changed

slog/logger.go

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
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+
"log"
9+
"sync/atomic"
10+
"time"
11+
)
12+
13+
var defaultLogger atomic.Value
14+
15+
func init() {
16+
defaultLogger.Store(&Logger{
17+
handler: &defaultHandler{},
18+
})
19+
}
20+
21+
// Default returns the default Logger.
22+
func Default() *Logger { return defaultLogger.Load().(*Logger) }
23+
24+
// SetDefault makes l the default Logger.
25+
// After this call, output from the log package's default Logger
26+
// (as with [log.Print], etc.) will be logged at InfoLevel using l's Handler.
27+
func SetDefault(l *Logger) {
28+
defaultLogger.Store(l)
29+
log.SetOutput(&handlerWriter{l.Handler(), log.Flags()})
30+
log.SetFlags(0) // we want just the log message, no time or location
31+
}
32+
33+
// handlerWriter is an io.Writer that calls a Handler.
34+
// It is used to link the default log.Logger to the default slog.Logger.
35+
type handlerWriter struct {
36+
h Handler
37+
flags int
38+
}
39+
40+
func (w *handlerWriter) Write(buf []byte) (int, error) {
41+
var depth int
42+
if w.flags&(log.Lshortfile|log.Llongfile) != 0 {
43+
depth = 2
44+
}
45+
// Remove final newline.
46+
origLen := len(buf) // Report that the entire buf was written.
47+
if len(buf) > 0 && buf[len(buf)-1] == '\n' {
48+
buf = buf[:len(buf)-1]
49+
}
50+
r := MakeRecord(time.Now(), InfoLevel, string(buf), depth)
51+
return origLen, w.h.Handle(r)
52+
}
53+
54+
// A Logger records structured information about each call to its
55+
// Log, Debug, Info, Warn, and Error methods.
56+
// For each call, it creates a Record and passes it to a Handler.
57+
//
58+
// Loggers are immutable; to create a new one, call [New] or [Logger.With].
59+
type Logger struct {
60+
handler Handler // for structured logging
61+
}
62+
63+
// Handler returns l's Handler.
64+
func (l *Logger) Handler() Handler { return l.handler }
65+
66+
// With returns a new Logger whose handler's attributes are a concatenation of
67+
// l's attributes and the given arguments, converted to Attrs as in
68+
// [Logger.Log].
69+
func (l *Logger) With(attrs ...any) *Logger {
70+
return &Logger{handler: l.handler.With(argsToAttrs(attrs))}
71+
}
72+
73+
func argsToAttrs(args []any) []Attr {
74+
var r Record
75+
setAttrs(&r, args)
76+
return r.Attrs()
77+
}
78+
79+
// New creates a new Logger with the given Handler.
80+
func New(h Handler) *Logger { return &Logger{handler: h} }
81+
82+
// With calls Logger.With on the default logger.
83+
func With(attrs ...any) *Logger {
84+
return Default().With(attrs...)
85+
}
86+
87+
// Enabled reports whether l emits log records at the given level.
88+
func (l *Logger) Enabled(level Level) bool {
89+
return l.Handler().Enabled(level)
90+
}
91+
92+
// Log emits a log record with the current time and the given level and message.
93+
// The Record's Attrs consist of the Logger's attributes followed by
94+
// the Attrs specified by args.
95+
//
96+
// The attribute arguments are processed as follows:
97+
// - If an argument is an Attr, it is used as is.
98+
// - If an argument is a string and this is not the last argument,
99+
// the following argument is treated as the value and the two are combined
100+
// into an Attr.
101+
// - Otherwise, the argument is treated as a value with key "!BADKEY".
102+
func (l *Logger) Log(level Level, msg string, args ...any) {
103+
l.LogDepth(0, level, msg, args...)
104+
}
105+
106+
// LogDepth is like [Logger.Log], but accepts a call depth to adjust the
107+
// file and line number in the log record. 0 refers to the caller
108+
// of LogDepth; 1 refers to the caller's caller; and so on.
109+
func (l *Logger) LogDepth(calldepth int, level Level, msg string, args ...any) {
110+
if !l.Enabled(level) {
111+
return
112+
}
113+
r := l.makeRecord(msg, level, calldepth)
114+
setAttrs(&r, args)
115+
_ = l.Handler().Handle(r)
116+
}
117+
118+
var useSourceLine = true
119+
120+
// Temporary, for benchmarking.
121+
// Eventually, getting the pc should be fast.
122+
func disableSourceLine() { useSourceLine = false }
123+
124+
func (l *Logger) makeRecord(msg string, level Level, depth int) Record {
125+
if useSourceLine {
126+
depth += 5
127+
}
128+
return MakeRecord(time.Now(), level, msg, depth)
129+
}
130+
131+
const badKey = "!BADKEY"
132+
133+
func setAttrs(r *Record, args []any) {
134+
var attr Attr
135+
for len(args) > 0 {
136+
attr, args = argsToAttr(args)
137+
r.AddAttr(attr)
138+
}
139+
}
140+
141+
// argsToAttrs turns a prefix of the args slice into an Attr and returns
142+
// the unused portion of the slice.
143+
// If args[0] is an Attr, it returns it.
144+
// If args[0] is a string, it treats the first two elements as
145+
// a key-value pair.
146+
// Otherwise, it treats args[0] as a value with a missing key.
147+
func argsToAttr(args []any) (Attr, []any) {
148+
switch x := args[0].(type) {
149+
case string:
150+
if len(args) == 1 {
151+
return String(badKey, x), nil
152+
}
153+
return Any(x, args[1]), args[2:]
154+
155+
case Attr:
156+
return x, args[1:]
157+
158+
default:
159+
return Any(badKey, x), args[1:]
160+
}
161+
}
162+
163+
// LogAttrs is a more efficient version of [Logger.Log] that accepts only Attrs.
164+
func (l *Logger) LogAttrs(level Level, msg string, attrs ...Attr) {
165+
l.LogAttrsDepth(0, level, msg, attrs...)
166+
}
167+
168+
// LogAttrsDepth is like [Logger.LogAttrs], but accepts a call depth argument
169+
// which it interprets like [Logger.LogDepth].
170+
func (l *Logger) LogAttrsDepth(calldepth int, level Level, msg string, attrs ...Attr) {
171+
if !l.Enabled(level) {
172+
return
173+
}
174+
r := l.makeRecord(msg, level, calldepth)
175+
r.addAttrs(attrs)
176+
_ = l.Handler().Handle(r)
177+
}
178+
179+
// Debug logs at DebugLevel.
180+
func (l *Logger) Debug(msg string, args ...any) {
181+
l.LogDepth(0, DebugLevel, msg, args...)
182+
}
183+
184+
// Info logs at InfoLevel.
185+
func (l *Logger) Info(msg string, args ...any) {
186+
l.LogDepth(0, InfoLevel, msg, args...)
187+
}
188+
189+
// Warn logs at WarnLevel.
190+
func (l *Logger) Warn(msg string, args ...any) {
191+
l.LogDepth(0, WarnLevel, msg, args...)
192+
}
193+
194+
// Error logs at ErrorLevel.
195+
// If err is non-nil, Error appends Any("err", err)
196+
// to the list of attributes.
197+
func (l *Logger) Error(msg string, err error, args ...any) {
198+
if err != nil {
199+
// TODO: avoid the copy.
200+
args = append(args[:len(args):len(args)], Any("err", err))
201+
}
202+
l.LogDepth(0, ErrorLevel, msg, args...)
203+
}
204+
205+
// Debug calls Logger.Debug on the default logger.
206+
func Debug(msg string, args ...any) {
207+
Default().LogDepth(0, DebugLevel, msg, args...)
208+
}
209+
210+
// Info calls Logger.Info on the default logger.
211+
func Info(msg string, args ...any) {
212+
Default().LogDepth(0, InfoLevel, msg, args...)
213+
}
214+
215+
// Warn calls Logger.Warn on the default logger.
216+
func Warn(msg string, args ...any) {
217+
Default().LogDepth(0, WarnLevel, msg, args...)
218+
}
219+
220+
// Error calls Logger.Error on the default logger.
221+
func Error(msg string, err error, args ...any) {
222+
if err != nil {
223+
// TODO: avoid the copy.
224+
args = append(args[:len(args):len(args)], Any("err", err))
225+
}
226+
Default().LogDepth(0, ErrorLevel, msg, args...)
227+
}
228+
229+
// Log calls Logger.Log on the default logger.
230+
func Log(level Level, msg string, args ...any) {
231+
Default().LogDepth(0, level, msg, args...)
232+
}
233+
234+
// LogAttrs calls Logger.LogAttrs on the default logger.
235+
func LogAttrs(level Level, msg string, attrs ...Attr) {
236+
Default().LogAttrsDepth(0, level, msg, attrs...)
237+
}

0 commit comments

Comments
 (0)