databricks-cli/libs/log/handler/friendly.go

103 lines
2.5 KiB
Go

package handler
import (
"context"
"fmt"
"io"
"log/slog"
"sync"
"github.com/databricks/cli/libs/log"
)
// friendlyHandler implements a custom [slog.Handler] that writes
// human readable (and colorized) log lines to a terminal.
//
// The implementation is based on the guide at:
// https://github.com/golang/example/blob/master/slog-handler-guide/README.md
type friendlyHandler struct {
opts Options
mu *sync.Mutex
out io.Writer
// List of colors to use for formatting.
ttyColors
// Cache (colorized) level strings.
levelTrace string
levelDebug string
levelInfo string
levelWarn string
levelError string
}
func NewFriendlyHandler(out io.Writer, opts *Options) slog.Handler {
h := &friendlyHandler{out: out, mu: &sync.Mutex{}}
if opts != nil {
h.opts = *opts
}
if h.opts.Level == nil {
h.opts.Level = slog.LevelInfo
}
h.ttyColors = newColors(opts.Color)
// Cache (colorized) level strings.
// The colors to use for each level are configured in `colors.go`.
h.levelTrace = h.sprintf(ttyColorLevelTrace, "%s", "Trace: ")
h.levelDebug = h.sprintf(ttyColorLevelDebug, "%s", "Debug: ")
h.levelInfo = h.sprintf(ttyColorLevelInfo, "%s", "Info: ")
h.levelWarn = h.sprintf(ttyColorLevelWarn, "%s", "Warn: ")
h.levelError = h.sprintf(ttyColorLevelError, "%s", "Error: ")
return h
}
func (h *friendlyHandler) sprint(color ttyColor, args ...any) string {
return h.ttyColors[color].Sprint(args...)
}
func (h *friendlyHandler) sprintf(color ttyColor, format string, args ...any) string {
return h.ttyColors[color].Sprintf(format, args...)
}
func (h *friendlyHandler) coloredLevel(r slog.Record) string {
switch r.Level {
case log.LevelTrace:
return h.levelTrace
case log.LevelDebug:
return h.levelDebug
case log.LevelInfo:
return h.levelInfo
case log.LevelWarn:
return h.levelWarn
case log.LevelError:
return h.levelError
}
return ""
}
// Enabled implements slog.Handler.
func (h *friendlyHandler) Enabled(ctx context.Context, level slog.Level) bool {
return level >= h.opts.Level.Level()
}
// Handle implements slog.Handler.
func (h *friendlyHandler) Handle(ctx context.Context, r slog.Record) error {
out := fmt.Sprintf("%s%s\n", h.coloredLevel(r), h.sprint(ttyColorMessage, r.Message))
h.mu.Lock()
defer h.mu.Unlock()
_, err := h.out.Write([]byte(out))
return err
}
// WithGroup implements slog.Handler.
func (h *friendlyHandler) WithGroup(name string) slog.Handler {
return h
}
// WithAttrs implements slog.Handler.
func (h *friendlyHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return h
}