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))) }