-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
logger.go
205 lines (172 loc) · 4.73 KB
/
logger.go
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
// Package log provides logging functionality.
package log
import (
"context"
"io"
"log/slog"
"os"
"runtime"
"time"
)
// Logger provides fast, leveled, structured logging. All methods are safe
// for concurrent use.
type Logger struct {
logger *slog.Logger
handler slog.Handler
callerInfo bool
level *slog.LevelVar
}
// New returns a new Logger instance.
func New() *Logger {
cfg := Config{
Level: DefaultLevel(),
}
return NewWithConfig(cfg)
}
// NewWithConfig creates a new logger for the given config.
// If no level is set in the config, it will use the default level of
// this package.
func NewWithConfig(cfg Config) *Logger {
level := &slog.LevelVar{}
level.Set(cfg.Level)
opts := &slog.HandlerOptions{
AddSource: cfg.CallerInfo,
Level: level,
}
var output io.Writer
if cfg.Output == nil {
output = os.Stdout
} else {
output = cfg.Output
}
handler := cfg.Handler
if handler == nil {
opts.ReplaceAttr = ReplaceLevelName
consoleOpts := &ConsoleHandlerOptions{
SlogOptions: opts,
TimeFormat: cfg.TimeFormat,
}
if cfg.TimeFormat == "" {
consoleOpts.TimeFormat = DefaultTimeFormat
}
handler = NewConsoleHandler(output, consoleOpts)
}
l := slog.New(handler)
logger := &Logger{
logger: l,
handler: handler,
level: level,
callerInfo: cfg.CallerInfo,
}
return logger
}
// Named adds a new path segment to the logger's name. Segments are joined by
// periods. By default, Loggers are unnamed.
func (l *Logger) Named(name string) *Logger {
newLogger := l.logger.WithGroup(name)
return &Logger{
logger: newLogger,
level: l.level,
}
}
// With creates a child logger and adds structured context to it. Fields added
// to the child don't affect the parent, and vice versa.
func (l *Logger) With(fields ...any) *Logger {
newLogger := l.logger.With(fields...)
return &Logger{
logger: newLogger,
level: l.level,
}
}
// 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)
}
// Level returns the minimum enabled log level.
func (l *Logger) Level() Level {
return l.level.Level()
}
// SetLevel alters the logging level.
func (l *Logger) SetLevel(level Level) {
l.level.Set(level)
}
// Trace logs at TraceLevel.
func (l *Logger) Trace(msg string, args ...any) {
l.Log(nil, TraceLevel, msg, args...)
}
// TraceContext logs at TraceLevel with the given context.
func (l *Logger) TraceContext(ctx context.Context, msg string, args ...any) {
l.Log(ctx, TraceLevel, msg, args...)
}
// Debug logs at LevelDebug.
func (l *Logger) Debug(msg string, args ...any) {
l.Log(nil, DebugLevel, msg, args...)
}
// DebugContext logs at LevelDebug with the given context.
func (l *Logger) DebugContext(ctx context.Context, msg string, args ...any) {
l.Log(ctx, DebugLevel, msg, args...)
}
// Info logs at LevelInfo.
func (l *Logger) Info(msg string, args ...any) {
l.Log(nil, InfoLevel, msg, args...)
}
// InfoContext logs at LevelInfo with the given context.
func (l *Logger) InfoContext(ctx context.Context, msg string, args ...any) {
l.Log(ctx, InfoLevel, msg, args...)
}
// Warn logs at LevelWarn.
func (l *Logger) Warn(msg string, args ...any) {
l.Log(nil, WarnLevel, msg, args...)
}
// WarnContext logs at LevelWarn with the given context.
func (l *Logger) WarnContext(ctx context.Context, msg string, args ...any) {
l.Log(ctx, WarnLevel, msg, args...)
}
// Error logs at LevelError.
func (l *Logger) Error(msg string, args ...any) {
l.Log(nil, ErrorLevel, msg, args...)
}
// ErrorContext logs at LevelError with the given context.
func (l *Logger) ErrorContext(ctx context.Context, msg string, args ...any) {
l.Log(ctx, ErrorLevel, msg, args...)
}
// Fatal logs at FatalLevel.
func (l *Logger) Fatal(msg string, args ...any) {
l.Log(nil, FatalLevel, msg, args...)
fatalExitFunc()
}
// FatalContext logs at FatalLevel with the given context.
func (l *Logger) FatalContext(ctx context.Context, msg string, args ...any) {
l.Log(ctx, FatalLevel, msg, args...)
fatalExitFunc()
}
// Log emits a log record with the current time and the given level and message.
func (l *Logger) Log(ctx context.Context, level Level, msg string, args ...any) {
if ctx == nil {
ctx = context.Background()
}
if !l.handler.Enabled(ctx, level) {
return
}
r := slog.Record{
Time: time.Now(),
Message: msg,
Level: level,
}
if l.callerInfo {
var pcs [1]uintptr
runtime.Callers(3, pcs[:])
r.PC = pcs[0]
}
r.Add(args...)
_ = l.handler.Handle(ctx, r)
}
// fatalExitFunc defines the function to call when exiting due to a fatal log error.
// This is used in unit tests.
var fatalExitFunc = fatalExit
func fatalExit() {
os.Exit(1)
}