Files
DivaCode/internal/log/handler.go

126 lines
2.2 KiB
Go

package log
import (
"context"
"fmt"
"io"
"log/slog"
"os"
"runtime"
"strings"
"sync"
)
type Handler struct {
mu sync.Mutex
w io.Writer
level slog.Level
attrs []slog.Attr
groups []string
}
func New(w io.Writer, level slog.Level) *Handler {
return &Handler{w: w, level: level}
}
func (h *Handler) Enabled(_ context.Context, l slog.Level) bool {
return l >= h.level
}
func (h *Handler) Handle(_ context.Context, r slog.Record) error {
h.mu.Lock()
defer h.mu.Unlock()
level := r.Level.String()
switch r.Level {
case slog.LevelDebug:
level = "DEBUG"
case slog.LevelInfo:
level = "INFO"
case slog.LevelWarn:
level = "WARN"
case slog.LevelError:
level = "ERROR"
}
_, file, line, ok := runtime.Caller(4)
if !ok {
file = "???"
}
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
t := r.Time.Format("2006-01-02 15:04:05.000")
var b strings.Builder
b.WriteString(fmt.Sprintf("[%s] [%s] [%s:%d]", level, t, short, line))
if len(h.groups) > 0 {
b.WriteString(" [" + strings.Join(h.groups, ".") + "]")
}
if r.Message != "" {
b.WriteString(" " + r.Message)
}
r.Attrs(func(a slog.Attr) bool {
b.WriteString(fmt.Sprintf(" %s=%v", a.Key, a.Value.Any()))
return true
})
for _, a := range h.attrs {
b.WriteString(fmt.Sprintf(" %s=%v", a.Key, a.Value.Any()))
}
b.WriteString("\n")
_, err := h.w.Write([]byte(b.String()))
return err
}
func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
if len(attrs) == 0 {
return h
}
h2 := h.clone()
h2.attrs = append(h2.attrs, attrs...)
return h2
}
func (h *Handler) WithGroup(name string) slog.Handler {
if name == "" {
return h
}
h2 := h.clone()
h2.groups = append(h2.groups, name)
return h2
}
func (h *Handler) clone() *Handler {
return &Handler{
w: h.w,
level: h.level,
attrs: append([]slog.Attr{}, h.attrs...),
groups: append([]string{}, h.groups...),
}
}
func Init(level string) {
var l slog.Level
switch strings.ToUpper(level) {
case "DEBUG":
l = slog.LevelDebug
case "INFO":
l = slog.LevelInfo
case "WARN":
l = slog.LevelWarn
case "ERROR":
l = slog.LevelError
default:
l = slog.LevelInfo
}
slog.SetDefault(slog.New(New(os.Stderr, l)))
}