diff options
| author | Kujtim Hoxha <[email protected]> | 2025-03-21 18:20:28 +0100 |
|---|---|---|
| committer | Kujtim Hoxha <[email protected]> | 2025-03-21 18:20:28 +0100 |
| commit | 4b0ea68d7af9a6031a7ffda7ad66e0cb83315750 (patch) | |
| tree | 8220c1bf6f107ea76dd78c7f57b77000c0c98a22 /internal/logging | |
| download | opencode-4b0ea68d7af9a6031a7ffda7ad66e0cb83315750.tar.gz opencode-4b0ea68d7af9a6031a7ffda7ad66e0cb83315750.zip | |
initial
Diffstat (limited to 'internal/logging')
| -rw-r--r-- | internal/logging/default.go | 12 | ||||
| -rw-r--r-- | internal/logging/logger.go | 100 | ||||
| -rw-r--r-- | internal/logging/logging.go | 17 | ||||
| -rw-r--r-- | internal/logging/message.go | 19 | ||||
| -rw-r--r-- | internal/logging/writer.go | 49 |
5 files changed, 197 insertions, 0 deletions
diff --git a/internal/logging/default.go b/internal/logging/default.go new file mode 100644 index 000000000..54cfaa490 --- /dev/null +++ b/internal/logging/default.go @@ -0,0 +1,12 @@ +package logging + +var defaultLogger Interface + +func Get() Interface { + if defaultLogger == nil { + defaultLogger = NewLogger(Options{ + Level: "info", + }) + } + return defaultLogger +} diff --git a/internal/logging/logger.go b/internal/logging/logger.go new file mode 100644 index 000000000..d38caeae2 --- /dev/null +++ b/internal/logging/logger.go @@ -0,0 +1,100 @@ +package logging + +import ( + "context" + "io" + "log/slog" + "slices" + + "github.com/kujtimiihoxha/termai/internal/pubsub" + "golang.org/x/exp/maps" +) + +const DefaultLevel = "info" + +var levels = map[string]slog.Level{ + "debug": slog.LevelDebug, + DefaultLevel: slog.LevelInfo, + "warn": slog.LevelWarn, + "error": slog.LevelError, +} + +func ValidLevels() []string { + keys := maps.Keys(levels) + slices.SortFunc(keys, func(a, b string) int { + if a == DefaultLevel { + return -1 + } + if b == DefaultLevel { + return 1 + } + if a < b { + return -1 + } + return 1 + }) + return keys +} + +func NewLogger(opts Options) *Logger { + logger := &Logger{} + broker := pubsub.NewBroker[Message]() + writer := &writer{ + messages: []Message{}, + Broker: broker, + } + + handler := slog.NewTextHandler( + io.MultiWriter(append(opts.AdditionalWriters, writer)...), + &slog.HandlerOptions{ + Level: slog.Level(levels[opts.Level]), + }, + ) + logger.logger = slog.New(handler) + logger.writer = writer + + return logger +} + +type Options struct { + Level string + AdditionalWriters []io.Writer +} + +type Logger struct { + logger *slog.Logger + writer *writer +} + +func (l *Logger) Debug(msg string, args ...any) { + l.logger.Debug(msg, args...) +} + +func (l *Logger) Info(msg string, args ...any) { + l.logger.Info(msg, args...) +} + +func (l *Logger) Warn(msg string, args ...any) { + l.logger.Warn(msg, args...) +} + +func (l *Logger) Error(msg string, args ...any) { + l.logger.Error(msg, args...) +} + +func (l *Logger) List() []Message { + return l.writer.messages +} + +func (l *Logger) Get(id string) (Message, error) { + for _, msg := range l.writer.messages { + if msg.ID == id { + return msg, nil + } + } + return Message{}, io.EOF +} + +func (l *Logger) Subscribe(ctx context.Context) <-chan pubsub.Event[Message] { + return l.writer.Subscribe(ctx) +} diff --git a/internal/logging/logging.go b/internal/logging/logging.go new file mode 100644 index 000000000..14653d1bb --- /dev/null +++ b/internal/logging/logging.go @@ -0,0 +1,17 @@ +package logging + +import ( + "context" + + "github.com/kujtimiihoxha/termai/internal/pubsub" +) + +type Interface interface { + Debug(msg string, args ...any) + Info(msg string, args ...any) + Warn(msg string, args ...any) + Error(msg string, args ...any) + Subscribe(ctx context.Context) <-chan pubsub.Event[Message] + + List() []Message +} diff --git a/internal/logging/message.go b/internal/logging/message.go new file mode 100644 index 000000000..a71788911 --- /dev/null +++ b/internal/logging/message.go @@ -0,0 +1,19 @@ +package logging + +import ( + "time" +) + +// Message is the event payload for a log message +type Message struct { + ID string + Time time.Time + Level string + Message string `json:"msg"` + Attributes []Attr +} + +type Attr struct { + Key string + Value string +} diff --git a/internal/logging/writer.go b/internal/logging/writer.go new file mode 100644 index 000000000..b4e899b30 --- /dev/null +++ b/internal/logging/writer.go @@ -0,0 +1,49 @@ +package logging + +import ( + "bytes" + "fmt" + "time" + + "github.com/go-logfmt/logfmt" + "github.com/kujtimiihoxha/termai/internal/pubsub" +) + +type writer struct { + messages []Message + *pubsub.Broker[Message] +} + +func (w *writer) Write(p []byte) (int, error) { + d := logfmt.NewDecoder(bytes.NewReader(p)) + for d.ScanRecord() { + msg := Message{ + ID: time.Now().Format(time.RFC3339Nano), + } + for d.ScanKeyval() { + switch string(d.Key()) { + case "time": + parsed, err := time.Parse(time.RFC3339, string(d.Value())) + if err != nil { + return 0, fmt.Errorf("parsing time: %w", err) + } + msg.Time = parsed + case "level": + msg.Level = string(d.Value()) + case "msg": + msg.Message = string(d.Value()) + default: + msg.Attributes = append(msg.Attributes, Attr{ + Key: string(d.Key()), + Value: string(d.Value()), + }) + } + } + w.messages = append(w.messages, msg) + w.Publish(pubsub.CreatedEvent, msg) + } + if d.Err() != nil { + return 0, d.Err() + } + return len(p), nil +} |
