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
15const (
16 persistKeyArg = "$persist"
17 PersistTimeArg = "$persist_time"
18)
19
20var levels = map[string]slog.Level{
21 "debug": slog.LevelDebug,
22 DefaultLevel: slog.LevelInfo,
23 "warn": slog.LevelWarn,
24 "error": slog.LevelError,
25}
26
27func ValidLevels() []string {
28 keys := maps.Keys(levels)
29 slices.SortFunc(keys, func(a, b string) int {
30 if a == DefaultLevel {
31 return -1
32 }
33 if b == DefaultLevel {
34 return 1
35 }
36 if a < b {
37 return -1
38 }
39 return 1
40 })
41 return keys
42}
43
44func NewLogger(opts Options) Interface {
45 logger := &Logger{}
46 broker := pubsub.NewBroker[LogMessage]()
47 writer := &writer{
48 messages: []LogMessage{},
49 Broker: broker,
50 }
51
52 handler := slog.NewTextHandler(
53 io.MultiWriter(writer),
54 &slog.HandlerOptions{
55 Level: slog.Level(levels[opts.Level]),
56 },
57 )
58 logger.logger = slog.New(handler)
59 logger.writer = writer
60
61 return logger
62}
63
64type Options struct {
65 Level string
66}
67
68type Logger struct {
69 logger *slog.Logger
70 writer *writer
71}
72
73func (l *Logger) SetLevel(level string) {
74 if _, ok := levels[level]; !ok {
75 level = DefaultLevel
76 }
77 handler := slog.NewTextHandler(
78 io.MultiWriter(l.writer),
79 &slog.HandlerOptions{
80 Level: levels[level],
81 },
82 )
83 l.logger = slog.New(handler)
84}
85
86// PersistDebug implements Interface.
87func (l *Logger) PersistDebug(msg string, args ...any) {
88 args = append(args, persistKeyArg, true)
89 l.Debug(msg, args...)
90}
91
92// PersistError implements Interface.
93func (l *Logger) PersistError(msg string, args ...any) {
94 args = append(args, persistKeyArg, true)
95 l.Error(msg, args...)
96}
97
98// PersistInfo implements Interface.
99func (l *Logger) PersistInfo(msg string, args ...any) {
100 args = append(args, persistKeyArg, true)
101 l.Info(msg, args...)
102}
103
104// PersistWarn implements Interface.
105func (l *Logger) PersistWarn(msg string, args ...any) {
106 args = append(args, persistKeyArg, true)
107 l.Warn(msg, args...)
108}
109
110func (l *Logger) Debug(msg string, args ...any) {
111 l.logger.Debug(msg, args...)
112}
113
114func (l *Logger) Info(msg string, args ...any) {
115 l.logger.Info(msg, args...)
116}
117
118func (l *Logger) Warn(msg string, args ...any) {
119 l.logger.Warn(msg, args...)
120}
121
122func (l *Logger) Error(msg string, args ...any) {
123 l.logger.Error(msg, args...)
124}
125
126func (l *Logger) List() []LogMessage {
127 return l.writer.messages
128}
129
130func (l *Logger) Get(id string) (LogMessage, error) {
131 for _, msg := range l.writer.messages {
132 if msg.ID == id {
133 return msg, nil
134 }
135 }
136 return LogMessage{}, io.EOF
137}
138
139func (l *Logger) Subscribe(ctx context.Context) <-chan pubsub.Event[LogMessage] {
140 return l.writer.Subscribe(ctx)
141}