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}