package log import ( "context" "fmt" "io" "log/slog" "sync" "time" ) // ANSI color codes const ( colorReset = "\033[0m" colorRed = "\033[31m" colorYellow = "\033[33m" colorBlue = "\033[34m" colorGray = "\033[90m" colorGreen = "\033[32m" colorCyan = "\033[36m" colorBold = "\033[1m" ) // TTYHandler is a custom handler for TTY output with colors type TTYHandler struct { opts slog.HandlerOptions mu sync.Mutex out io.Writer } // NewTTYHandler creates a new TTY handler func NewTTYHandler(out io.Writer, opts *slog.HandlerOptions) *TTYHandler { if opts == nil { opts = &slog.HandlerOptions{} } return &TTYHandler{ out: out, opts: *opts, } } // Enabled reports whether the handler handles records at the given level func (h *TTYHandler) Enabled(_ context.Context, level slog.Level) bool { return level >= h.opts.Level.Level() } // Handle writes the log record func (h *TTYHandler) Handle(_ context.Context, r slog.Record) error { h.mu.Lock() defer h.mu.Unlock() // Format timestamp timestamp := r.Time.Format("15:04:05") // Level and color level := r.Level.String() var levelColor string switch r.Level { case slog.LevelDebug: levelColor = colorGray level = "DEBUG" case slog.LevelInfo: levelColor = colorGreen level = "INFO " case slog.LevelWarn: levelColor = colorYellow level = "WARN " case slog.LevelError: levelColor = colorRed level = "ERROR" default: levelColor = colorReset } // Print main message _, _ = fmt.Fprintf(h.out, "%s%s%s %s%s%s %s%s%s", colorGray, timestamp, colorReset, levelColor, level, colorReset, colorBold, r.Message, colorReset) // Print attributes r.Attrs(func(a slog.Attr) bool { value := a.Value.String() // Special handling for certain attribute types switch a.Value.Kind() { case slog.KindDuration: if d, ok := a.Value.Any().(time.Duration); ok { value = formatDuration(d) } case slog.KindInt64: if a.Key == "bytes" { value = formatBytes(a.Value.Int64()) } } _, _ = fmt.Fprintf(h.out, " %s%s%s=%s%s%s", colorCyan, a.Key, colorReset, colorBlue, value, colorReset) return true }) _, _ = fmt.Fprintln(h.out) return nil } // WithAttrs returns a new handler with the given attributes func (h *TTYHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return h // Simplified for now } // WithGroup returns a new handler with the given group name func (h *TTYHandler) WithGroup(name string) slog.Handler { return h // Simplified for now } // formatDuration formats a duration in a human-readable way func formatDuration(d time.Duration) string { if d < time.Millisecond { return fmt.Sprintf("%dµs", d.Microseconds()) } else if d < time.Second { return fmt.Sprintf("%dms", d.Milliseconds()) } else if d < time.Minute { return fmt.Sprintf("%.1fs", d.Seconds()) } return d.String() } // formatBytes formats bytes in a human-readable way func formatBytes(b int64) string { const unit = 1024 if b < unit { return fmt.Sprintf("%d B", b) } div, exp := int64(unit), 0 for n := b / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "KMGTPE"[exp]) }