Skip to content

Commit 7ee68fb

Browse files
authored
Merge pull request #21 from gobuffalo/use-slog-as-default
Add default logger instance and package-level API
2 parents b55092c + 30feb47 commit 7ee68fb

8 files changed

Lines changed: 1233 additions & 107 deletions

File tree

default.go

Lines changed: 72 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,126 @@
11
package logger
22

33
import (
4-
"context"
5-
"fmt"
6-
"io"
74
"log/slog"
85
"os"
6+
"sync"
97
)
108

11-
// fieldLogger is a Logger implementation backed by log/slog
12-
type fieldLogger struct {
13-
logger *slog.Logger
14-
attrs []slog.Attr
15-
}
16-
17-
// SetOutput sets the output writer for the logger.
18-
func (s *fieldLogger) SetOutput(w io.Writer) {
19-
opts := &slog.HandlerOptions{}
20-
s.logger = slog.New(slog.NewTextHandler(w, opts))
21-
}
22-
23-
func (s *fieldLogger) WithField(key string, value any) FieldLogger {
24-
return s.WithFields(map[string]any{key: value})
25-
}
26-
27-
func (s *fieldLogger) WithFields(m map[string]any) FieldLogger {
28-
attrs := make([]slog.Attr, 0, len(m)+len(s.attrs))
29-
attrs = append(attrs, s.attrs...)
30-
for k, v := range m {
31-
attrs = append(attrs, slog.Any(k, v))
32-
}
33-
return &fieldLogger{
34-
logger: s.logger,
35-
attrs: attrs,
36-
}
37-
}
9+
var (
10+
defaultLogger FieldLogger
11+
defaultLoggerOnce sync.Once
12+
)
3813

39-
func (s *fieldLogger) log(level slog.Level, msg string, args ...any) {
40-
if len(args) > 0 {
41-
msg = fmt.Sprintf(msg, args...)
14+
// NewLogger based on the specified log level, defaults to "debug".
15+
// See `New` for more details.
16+
func NewLogger(level string) FieldLogger {
17+
lvl, err := ParseLevel(level)
18+
if err != nil {
19+
lvl = DebugLevel
4220
}
43-
s.logger.Log(context.Background(), level, msg, s.attrsToAny()...)
21+
return New(lvl)
4422
}
4523

46-
func (s *fieldLogger) attrsToAny() []any {
47-
result := make([]any, len(s.attrs))
48-
for i, a := range s.attrs {
49-
result[i] = a
50-
}
51-
return result
24+
// Default returns the default FieldLogger instance.
25+
// The default logger is initialized with InfoLevel on first call.
26+
func Default() FieldLogger {
27+
defaultLoggerOnce.Do(func() {
28+
if defaultLogger == nil {
29+
defaultLogger = New(InfoLevel)
30+
}
31+
})
32+
return defaultLogger
5233
}
5334

54-
func (s *fieldLogger) Debugf(msg string, args ...any) {
55-
s.log(slog.LevelDebug, msg, args...)
35+
// SetDefault sets the default logger instance.
36+
// Subsequent calls to Default() and package-level logging functions will use this logger.
37+
func SetDefault(l FieldLogger) {
38+
defaultLogger = l
5639
}
5740

58-
func (s *fieldLogger) Infof(msg string, args ...any) {
59-
s.log(slog.LevelInfo, msg, args...)
41+
// Debugf logs a formatted debug message using the default logger.
42+
func Debugf(msg string, args ...any) {
43+
Default().Debugf(msg, args...)
6044
}
6145

62-
func (s *fieldLogger) Printf(msg string, args ...any) {
63-
s.Infof(msg, args...)
46+
// Infof logs a formatted info message using the default logger.
47+
func Infof(msg string, args ...any) {
48+
Default().Infof(msg, args...)
6449
}
6550

66-
func (s *fieldLogger) Warnf(msg string, args ...any) {
67-
s.log(slog.LevelWarn, msg, args...)
51+
// Printf logs a formatted message using the default logger.
52+
func Printf(msg string, args ...any) {
53+
Default().Printf(msg, args...)
6854
}
6955

70-
func (s *fieldLogger) Errorf(msg string, args ...any) {
71-
s.log(slog.LevelError, msg, args...)
56+
// Warnf logs a formatted warning message using the default logger.
57+
func Warnf(msg string, args ...any) {
58+
Default().Warnf(msg, args...)
7259
}
7360

74-
func (s *fieldLogger) Fatalf(msg string, args ...any) {
75-
s.log(slog.Level(12), msg, args...)
76-
os.Exit(1)
61+
// Errorf logs a formatted error message using the default logger.
62+
func Errorf(msg string, args ...any) {
63+
Default().Errorf(msg, args...)
7764
}
7865

79-
func (s *fieldLogger) Debug(args ...any) {
80-
s.log(slog.LevelDebug, fmt.Sprint(args...))
66+
// Fatalf logs a formatted fatal message using the default logger and exits the program.
67+
func Fatalf(msg string, args ...any) {
68+
Default().Fatalf(msg, args...)
8169
}
8270

83-
func (s *fieldLogger) Info(args ...any) {
84-
s.log(slog.LevelInfo, fmt.Sprint(args...))
71+
// Debug logs a debug message using the default logger.
72+
func Debug(args ...any) {
73+
Default().Debug(args...)
8574
}
8675

87-
func (s *fieldLogger) Warn(args ...any) {
88-
s.log(slog.LevelWarn, fmt.Sprint(args...))
76+
// Info logs an info message using the default logger.
77+
func Info(args ...any) {
78+
Default().Info(args...)
8979
}
9080

91-
func (s *fieldLogger) Error(args ...any) {
92-
s.log(slog.LevelError, fmt.Sprint(args...))
81+
// Warn logs a warning message using the default logger.
82+
func Warn(args ...any) {
83+
Default().Warn(args...)
9384
}
9485

95-
func (s *fieldLogger) Fatal(args ...any) {
96-
s.log(slog.Level(12), fmt.Sprint(args...))
97-
os.Exit(1)
86+
// Error logs an error message using the default logger.
87+
func Error(args ...any) {
88+
Default().Error(args...)
9889
}
9990

100-
func (s *fieldLogger) Panic(args ...any) {
101-
msg := fmt.Sprint(args...)
102-
s.log(slog.Level(12), msg)
103-
panic(msg)
91+
// Fatal logs a fatal message using the default logger and exits the program.
92+
func Fatal(args ...any) {
93+
Default().Fatal(args...)
10494
}
10595

106-
// ParseLevel parses a string level into a Level.
107-
func ParseLevel(level string) (Level, error) {
108-
switch level {
109-
case "panic":
110-
return PanicLevel, nil
111-
case "fatal":
112-
return FatalLevel, nil
113-
case "error":
114-
return ErrorLevel, nil
115-
case "warn", "warning":
116-
return WarnLevel, nil
117-
case "info":
118-
return InfoLevel, nil
119-
case "debug":
120-
return DebugLevel, nil
121-
}
122-
123-
var l Level
124-
return l, fmt.Errorf("not a valid Level: %q", level)
96+
// Panic logs a panic message using the default logger and panics.
97+
func Panic(args ...any) {
98+
Default().Panic(args...)
12599
}
126100

127-
// NewLogger based on the specified log level, defaults to "debug".
128-
// See `New` for more details.
129-
func NewLogger(level string) FieldLogger {
130-
lvl, err := ParseLevel(level)
131-
if err != nil {
132-
lvl = DebugLevel
133-
}
134-
return New(lvl)
101+
// WithField adds a field to the default logger and returns a new FieldLogger.
102+
func WithField(key string, value any) FieldLogger {
103+
return Default().WithField(key, value)
135104
}
136105

137-
// New based on the specified log level, defaults to "debug".
138-
// This logger will log to the STDOUT in a human readable,
139-
// but parseable form.
140-
//
141-
// Example: time="2016-12-01T21:02:07-05:00" level=info duration=225.283µs human_size="106 B" method=GET path="/" render=199.79µs request_id=2265736089 size=106 status=200
142-
func New(lvl Level) FieldLogger {
143-
return newSlog(lvl)
106+
// WithFields adds multiple fields to the default logger and returns a new FieldLogger.
107+
func WithFields(fields map[string]any) FieldLogger {
108+
return Default().WithFields(fields)
144109
}
145110

146-
func newSlog(lvl Level) FieldLogger {
111+
func newDefault(lvl Level) FieldLogger {
147112
opts := &slog.HandlerOptions{
148-
Level: toSlogLevel(lvl),
113+
Level: toDefaultLevel(lvl),
149114
}
150115
handler := slog.NewTextHandler(os.Stdout, opts)
151116
return &fieldLogger{
152117
logger: slog.New(handler),
118+
level: lvl,
153119
}
154120
}
155121

156-
// toSlogLevel converts our Level to slog.Level.
157-
func toSlogLevel(l Level) slog.Level {
122+
// toDefaultLevel converts our Level to slog.Level.
123+
func toDefaultLevel(l Level) slog.Level {
158124
switch l {
159125
case PanicLevel, FatalLevel:
160126
return slog.Level(12) // custom level above error

0 commit comments

Comments
 (0)