github.com/oarkflow/log@v1.0.78/logger_std_slog.go (about) 1 //go:build go1.21 2 // +build go1.21 3 4 package log 5 6 import ( 7 "context" 8 "log/slog" 9 ) 10 11 func slogAttrEval(e *Entry, a slog.Attr) *Entry { 12 if a.Equal(slog.Attr{}) { 13 return e 14 } 15 value := a.Value.Resolve() 16 switch value.Kind() { 17 case slog.KindGroup: 18 if len(value.Group()) == 0 { 19 return e 20 } 21 if a.Key == "" { 22 for _, attr := range value.Group() { 23 e = slogAttrEval(e, attr) 24 } 25 return e 26 } 27 e.buf = append(e.buf, ',', '"') 28 e.buf = append(e.buf, a.Key...) 29 e.buf = append(e.buf, '"', ':') 30 i := len(e.buf) 31 for _, attr := range value.Group() { 32 e = slogAttrEval(e, attr) 33 } 34 e.buf[i] = '{' 35 e.buf = append(e.buf, '}') 36 return e 37 case slog.KindBool: 38 return e.Bool(a.Key, value.Bool()) 39 case slog.KindDuration: 40 return e.Dur(a.Key, value.Duration()) 41 case slog.KindFloat64: 42 return e.Float64(a.Key, value.Float64()) 43 case slog.KindInt64: 44 return e.Int64(a.Key, value.Int64()) 45 case slog.KindString: 46 return e.Str(a.Key, value.String()) 47 case slog.KindTime: 48 return e.Time(a.Key, value.Time()) 49 case slog.KindUint64: 50 return e.Uint64(a.Key, value.Uint64()) 51 case slog.KindAny: 52 fallthrough 53 default: 54 return e.Any(a.Key, value.Any()) 55 } 56 } 57 58 type slogHandler struct { 59 logger Logger 60 caller int 61 grouping bool 62 groups int 63 entry Entry 64 } 65 66 func (h slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { 67 if len(attrs) == 0 { 68 return &h 69 } 70 i := len(h.entry.buf) 71 for _, attr := range attrs { 72 h.entry = *slogAttrEval(&h.entry, attr) 73 } 74 if h.grouping { 75 h.entry.buf[i] = '{' 76 } 77 h.grouping = false 78 return &h 79 } 80 81 func (h slogHandler) WithGroup(name string) slog.Handler { 82 if name != "" { 83 if h.grouping { 84 h.entry.buf = append(h.entry.buf, '{') 85 } else { 86 h.entry.buf = append(h.entry.buf, ',') 87 } 88 h.entry.buf = append(h.entry.buf, '"') 89 h.entry.buf = append(h.entry.buf, name...) 90 h.entry.buf = append(h.entry.buf, '"', ':') 91 h.grouping = true 92 h.groups++ 93 } 94 return &h 95 } 96 97 func (h *slogHandler) Enabled(_ context.Context, level slog.Level) bool { 98 switch level { 99 case slog.LevelDebug: 100 return h.logger.Level <= DebugLevel 101 case slog.LevelInfo: 102 return h.logger.Level <= InfoLevel 103 case slog.LevelWarn: 104 return h.logger.Level <= WarnLevel 105 case slog.LevelError: 106 return h.logger.Level <= ErrorLevel 107 } 108 return false 109 } 110 111 func (h *slogHandler) Handle(_ context.Context, r slog.Record) error { 112 var e *Entry 113 switch r.Level { 114 case slog.LevelDebug: 115 e = h.logger.Debug() 116 case slog.LevelInfo: 117 e = h.logger.Info() 118 case slog.LevelWarn: 119 e = h.logger.Warn() 120 case slog.LevelError: 121 e = h.logger.Error() 122 default: 123 e = h.logger.Log() 124 } 125 126 if h.caller != 0 { 127 e.caller(1, r.PC, h.caller < 0) 128 } 129 130 // msg 131 e = e.Str("message", r.Message) 132 133 // with 134 if b := h.entry.buf; len(b) != 0 { 135 e = e.Context(b) 136 } 137 i := len(e.buf) 138 139 // attrs 140 r.Attrs(func(attr slog.Attr) bool { 141 e = slogAttrEval(e, attr) 142 return true 143 }) 144 145 lastindex := func(buf []byte) int { 146 for i := len(buf) - 3; i >= 1; i-- { 147 if buf[i] == '"' && (buf[i-1] == ',' || buf[i-1] == '{') { 148 return i 149 } 150 } 151 return -1 152 } 153 154 // group attrs 155 if h.grouping { 156 if r.NumAttrs() > 0 { 157 e.buf[i] = '{' 158 } else if i = lastindex(e.buf); i > 0 { 159 e.buf = e.buf[:i-1] 160 h.groups-- 161 for e.buf[len(e.buf)-1] == ':' { 162 if i = lastindex(e.buf); i > 0 { 163 e.buf = e.buf[:i-1] 164 h.groups-- 165 } 166 } 167 } else { 168 e.buf = append(e.buf, '{') 169 } 170 } 171 172 // brackets closing 173 switch h.groups { 174 case 0: 175 break 176 case 1: 177 e.buf = append(e.buf, '}') 178 case 2: 179 e.buf = append(e.buf, '}', '}') 180 case 3: 181 e.buf = append(e.buf, '}', '}', '}') 182 case 4: 183 e.buf = append(e.buf, '}', '}', '}', '}') 184 default: 185 for i := 0; i < h.groups; i++ { 186 e.buf = append(e.buf, '}') 187 } 188 } 189 190 e.Msg("") 191 return nil 192 } 193 194 // Slog wraps the Logger to provide *slog.Logger 195 func (l *Logger) Slog() *slog.Logger { 196 h := &slogHandler{ 197 logger: *l, 198 caller: l.Caller, 199 } 200 201 h.logger.Caller = 0 202 203 return slog.New(h) 204 }