summaryrefslogtreecommitdiffhomepage
path: root/internal/logging
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-03-21 18:20:28 +0100
committerKujtim Hoxha <[email protected]>2025-03-21 18:20:28 +0100
commit4b0ea68d7af9a6031a7ffda7ad66e0cb83315750 (patch)
tree8220c1bf6f107ea76dd78c7f57b77000c0c98a22 /internal/logging
downloadopencode-4b0ea68d7af9a6031a7ffda7ad66e0cb83315750.tar.gz
opencode-4b0ea68d7af9a6031a7ffda7ad66e0cb83315750.zip
initial
Diffstat (limited to 'internal/logging')
-rw-r--r--internal/logging/default.go12
-rw-r--r--internal/logging/logger.go100
-rw-r--r--internal/logging/logging.go17
-rw-r--r--internal/logging/message.go19
-rw-r--r--internal/logging/writer.go49
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
+}