aboutsummaryrefslogtreecommitdiff
path: root/go/log/handler.go
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2025-08-19 15:36:10 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2025-08-20 15:57:40 +0200
commit790706dbf1e797607dfb6c6717ab863a9e4cb5ca (patch)
tree1c31d30048fa0de6dd605aa6e242a7c0977cd2f1 /go/log/handler.go
parentb584b8442ca46e0aa71509ab6bc567e242c629bc (diff)
downloadmuhqs-game-790706dbf1e797607dfb6c6717ab863a9e4cb5ca.tar.gz
muhqs-game-790706dbf1e797607dfb6c6717ab863a9e4cb5ca.zip
add simlpe plain text log handler and consider loglevel from the env
Diffstat (limited to 'go/log/handler.go')
-rw-r--r--go/log/handler.go116
1 files changed, 111 insertions, 5 deletions
diff --git a/go/log/handler.go b/go/log/handler.go
index 75231098..05d19fb3 100644
--- a/go/log/handler.go
+++ b/go/log/handler.go
@@ -2,9 +2,14 @@ package log
import (
"context"
+ "fmt"
+ "io"
"log/slog"
"os"
+ "path"
"runtime"
+ "sync"
+ "time"
"golang.org/x/exp/slices"
)
@@ -15,7 +20,7 @@ type gameLogHandler struct {
}
func NewGameLogHandler(files []string, opts *slog.HandlerOptions) *gameLogHandler {
- return &gameLogHandler{h: slog.NewTextHandler(os.Stdout, opts), files: files}
+ return &gameLogHandler{h: newTextHandler(os.Stdout, opts), files: files}
}
func (h *gameLogHandler) Enabled(ctx context.Context, level slog.Level) bool {
@@ -23,10 +28,13 @@ func (h *gameLogHandler) Enabled(ctx context.Context, level slog.Level) bool {
}
func (h *gameLogHandler) Handle(ctx context.Context, r slog.Record) error {
- frames := runtime.CallersFrames([]uintptr{r.PC})
- frame, _ := frames.Next()
- if slices.Contains(h.files, frame.File) {
- return nil
+ if pc, file, _, ok := runtime.Caller(4); ok {
+ basename := path.Base(file)
+ if len(h.files) > 0 && !slices.Contains(h.files, basename) {
+ return nil
+ }
+
+ r.PC = pc
}
return h.h.Handle(ctx, r)
@@ -39,3 +47,101 @@ func (h *gameLogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
func (h *gameLogHandler) WithGroup(name string) slog.Handler {
return &gameLogHandler{h: h.h.WithGroup(name), files: h.files}
}
+
+type TextHandler struct {
+ opts *slog.HandlerOptions
+ // TODO: state for WithGroup and WithAttrs
+ mu *sync.Mutex
+ out io.Writer
+}
+
+func newTextHandler(out io.Writer, opts *slog.HandlerOptions) *TextHandler {
+ h := &TextHandler{out: out, mu: &sync.Mutex{}}
+ if opts != nil {
+ h.opts = opts
+ }
+ if h.opts.Level == nil {
+ h.opts.Level = slog.LevelInfo
+ }
+ return h
+}
+
+func (h *TextHandler) Enabled(ctx context.Context, level slog.Level) bool {
+ return level >= h.opts.Level.Level()
+}
+
+func (h *TextHandler) WithGroup(name string) slog.Handler {
+ // TODO: implement.
+ return h
+}
+
+func (h *TextHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ // TODO: implement.
+ return h
+}
+
+func (h *TextHandler) Handle(ctx context.Context, r slog.Record) error {
+ buf := make([]byte, 0, 1024)
+ if !r.Time.IsZero() {
+ buf = h.appendAttr(buf, slog.Time(slog.TimeKey, r.Time))
+ }
+
+ if r.Level == 99 {
+ buf = fmt.Append(buf, "GAME ")
+ } else {
+ buf = fmt.Appendf(buf, "%s ", r.Level)
+ }
+
+ fs := runtime.CallersFrames([]uintptr{r.PC})
+ f, _ := fs.Next()
+ basename := path.Base(f.File)
+ buf = fmt.Appendf(buf, "%s:%d ", basename, f.Line)
+
+ buf = h.appendAttr(buf, slog.String(slog.MessageKey, r.Message))
+ r.Attrs(func(a slog.Attr) bool {
+ buf = h.appendAttr(buf, a)
+ return true
+ })
+
+ // Trim last whitespace and add newline
+ buf = fmt.Append(buf[:len(buf)-1], "\n")
+
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ _, err := h.out.Write(buf)
+ return err
+}
+
+func (h *TextHandler) appendAttr(buf []byte, a slog.Attr) []byte {
+ // Resolve the Attr's value before doing anything else.
+ a.Value = a.Value.Resolve()
+ // Ignore empty Attrs.
+ if a.Equal(slog.Attr{}) {
+ return buf
+ }
+ switch a.Value.Kind() {
+ case slog.KindTime:
+ buf = fmt.Appendf(buf, "%s", a.Value.Time().Format(time.TimeOnly))
+ case slog.KindGroup:
+ attrs := a.Value.Group()
+ // Ignore empty groups.
+ if len(attrs) == 0 {
+ return buf
+ }
+ // If the key is non-empty, write it out and indent the rest of the attrs.
+ // Otherwise, inline the attrs.
+ if a.Key != "" {
+ buf = fmt.Appendf(buf, "%s: ", a.Key)
+ }
+ buf = fmt.Append(buf, "[", a.Key)
+ for _, ga := range attrs {
+ buf = h.appendAttr(buf, ga)
+ buf = fmt.Append(buf, ", ", a.Key)
+ }
+ buf = fmt.Append(buf[:len(buf)-2], "]", a.Key)
+ default:
+ buf = fmt.Appendf(buf, "%s", a.Value)
+ }
+ buf = fmt.Appendf(buf, "%*s", 1, "")
+ return buf
+}