logger.go

  1package logging
  2
  3import (
  4	"context"
  5	"io"
  6	"log/slog"
  7	"slices"
  8
  9	"github.com/kujtimiihoxha/termai/internal/pubsub"
 10	"golang.org/x/exp/maps"
 11)
 12
 13const DefaultLevel = "info"
 14
 15var levels = map[string]slog.Level{
 16	"debug":      slog.LevelDebug,
 17	DefaultLevel: slog.LevelInfo,
 18	"warn":       slog.LevelWarn,
 19	"error":      slog.LevelError,
 20}
 21
 22func ValidLevels() []string {
 23	keys := maps.Keys(levels)
 24	slices.SortFunc(keys, func(a, b string) int {
 25		if a == DefaultLevel {
 26			return -1
 27		}
 28		if b == DefaultLevel {
 29			return 1
 30		}
 31		if a < b {
 32			return -1
 33		}
 34		return 1
 35	})
 36	return keys
 37}
 38
 39func NewLogger(opts Options) *Logger {
 40	logger := &Logger{}
 41	broker := pubsub.NewBroker[Message]()
 42	writer := &writer{
 43		messages: []Message{},
 44		Broker:   broker,
 45	}
 46
 47	handler := slog.NewTextHandler(
 48		io.MultiWriter(append(opts.AdditionalWriters, writer)...),
 49		&slog.HandlerOptions{
 50			Level: slog.Level(levels[opts.Level]),
 51		},
 52	)
 53	logger.logger = slog.New(handler)
 54	logger.writer = writer
 55
 56	return logger
 57}
 58
 59type Options struct {
 60	Level             string
 61	AdditionalWriters []io.Writer
 62}
 63
 64type Logger struct {
 65	logger *slog.Logger
 66	writer *writer
 67}
 68
 69func (l *Logger) Debug(msg string, args ...any) {
 70	l.logger.Debug(msg, args...)
 71}
 72
 73func (l *Logger) Info(msg string, args ...any) {
 74	l.logger.Info(msg, args...)
 75}
 76
 77func (l *Logger) Warn(msg string, args ...any) {
 78	l.logger.Warn(msg, args...)
 79}
 80
 81func (l *Logger) Error(msg string, args ...any) {
 82	l.logger.Error(msg, args...)
 83}
 84
 85func (l *Logger) List() []Message {
 86	return l.writer.messages
 87}
 88
 89func (l *Logger) Get(id string) (Message, error) {
 90	for _, msg := range l.writer.messages {
 91		if msg.ID == id {
 92			return msg, nil
 93		}
 94	}
 95	return Message{}, io.EOF
 96}
 97
 98func (l *Logger) Subscribe(ctx context.Context) <-chan pubsub.Event[Message] {
 99	return l.writer.Subscribe(ctx)
100}