1 package log
2 3 import (
4 "context"
5 "fmt"
6 "io"
7 "log/slog"
8 "os"
9 )
10 11 var (
12 // slogNOPHandler is a discarding slog.Handler
13 slogNOPHandler = slog.NewTextHandler(io.Discard, nil)
14 // slogDefaultHandler is default slog.Handler
15 slogDefaultHandler = NewSlogHandler(os.Stderr, &slog.HandlerOptions{
16 Level: slog.LevelInfo,
17 })
18 )
19 20 const (
21 // LevelTrace is custom trace slog level
22 LevelTrace = slog.Level(-8)
23 // LevelFatal is custom fatal slog level
24 LevelFatal = slog.Level(12)
25 )
26 27 // NewSlogAdapter returns SlogAdapter with specified slog.Handler
28 func NewSlogAdapter(h slog.Handler) SlogAdapter {
29 l := slog.New(h)
30 return SlogAdapter{l}
31 }
32 33 // SlogAdapter is wrapper for slog.Logger
34 type SlogAdapter struct {
35 *slog.Logger
36 }
37 38 // Trace logs at trace level
39 // The attribute arguments are processed as follows:
40 // 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 key - value pair.
41 // Otherwise, the argument is treated as a value with key "!BADKEY".
42 func (l SlogAdapter) Trace(msg string, args ...any) {
43 l.Logger.Log(context.Background(), LevelTrace, msg, args...)
44 }
45 46 // Debug logs at debug level,
47 // The attribute arguments are processed as follows:
48 // 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 key - value pair.
49 // Otherwise, the argument is treated as a value with key "!BADKEY".
50 func (l SlogAdapter) Debug(msg string, args ...any) {
51 l.Logger.Debug(msg, logArgs(args)...)
52 }
53 54 // Info logs at info level
55 // The attribute arguments are processed as follows:
56 // 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 key - value pair.
57 // Otherwise, the argument is treated as a value with key "!BADKEY".
58 func (l SlogAdapter) Info(msg string, args ...any) {
59 l.Logger.Info(msg, logArgs(args)...)
60 }
61 62 // Warn logs at warn level
63 // The attribute arguments are processed as follows:
64 // 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 key - value pair.
65 // Otherwise, the argument is treated as a value with key "!BADKEY".
66 func (l SlogAdapter) Warn(msg string, args ...any) {
67 l.Logger.Warn(msg, logArgs(args)...)
68 }
69 70 // Error logs at error level
71 // The attribute arguments are processed as follows:
72 // 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 key - value pair.
73 // Otherwise, the argument is treated as a value with key "!BADKEY".
74 func (l SlogAdapter) Error(msg string, args ...any) {
75 l.Logger.Error(msg, logArgs(args)...)
76 }
77 78 // Fatal logs at fatal level, followed by an exit.
79 // The attribute arguments are processed as follows:
80 // 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 key - value pair.
81 // Otherwise, the argument is treated as a value with key "!BADKEY".
82 func (l SlogAdapter) Fatal(msg string, args ...any) {
83 l.Logger.Log(context.Background(), LevelFatal, msg, args...)
84 os.Exit(1)
85 }
86 87 // Tracef formats according to a format specifier and logs the resulting string at trace level
88 func (l SlogAdapter) Tracef(msg string, args ...any) {
89 m := fmt.Sprintf(msg, args...)
90 l.Logger.Log(context.Background(), LevelTrace, m)
91 }
92 93 // Debugf formats according to a format specifier and logs the resulting string at debug level
94 func (l SlogAdapter) Debugf(msg string, args ...any) {
95 m := fmt.Sprintf(msg, args...)
96 l.Logger.Debug(m)
97 }
98 99 // Infof formats according to a format specifier and logs the resulting string at info level
100 func (l SlogAdapter) Infof(msg string, args ...any) {
101 m := fmt.Sprintf(msg, args...)
102 l.Logger.Info(m)
103 }
104 105 // Warnf formats according to a format specifier and logs the resulting string at warn level
106 func (l SlogAdapter) Warnf(msg string, args ...any) {
107 m := fmt.Sprintf(msg, args...)
108 l.Logger.Warn(m)
109 }
110 111 // Errorf formats according to a format specifier and logs the resulting string at error level
112 func (l SlogAdapter) Errorf(msg string, args ...any) {
113 m := fmt.Sprintf(msg, args...)
114 l.Logger.Error(m)
115 }
116 117 // Fatalf formats according to a format specifier and logs the resulting string at fatal level, followed by an exit.
118 func (l SlogAdapter) Fatalf(msg string, args ...any) {
119 m := fmt.Sprintf(msg, args...)
120 l.Logger.Log(context.Background(), LevelFatal, m)
121 os.Exit(1)
122 }
123 124 // With returns new Interface that includes given fields with each operation
125 // returned logger will log each field on every logging operation it will be used
126 func (l SlogAdapter) With(key string, fields Fields) Interface {
127 newLogger := l.Logger.With(key, fields)
128 return SlogAdapter{newLogger}
129 }
130 131 func logArgs(args []any) []any {
132 result := []any{}
133 for _, arg := range args {
134 switch v := arg.(type) {
135 case Fields:
136 result = append(result, v.Fields().Get()...)
137 default:
138 result = append(result, arg)
139 }
140 }
141 return result
142 }
143