aboutsummaryrefslogtreecommitdiff
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
parentb584b8442ca46e0aa71509ab6bc567e242c629bc (diff)
downloadmuhqs-game-790706dbf1e797607dfb6c6717ab863a9e4cb5ca.tar.gz
muhqs-game-790706dbf1e797607dfb6c6717ab863a9e4cb5ca.zip
add simlpe plain text log handler and consider loglevel from the env
-rw-r--r--go/client/game.go8
-rw-r--r--go/log/handler.go116
-rw-r--r--go/log/log.go11
3 files changed, 124 insertions, 11 deletions
diff --git a/go/client/game.go b/go/client/game.go
index caf73933..3dacd209 100644
--- a/go/client/game.go
+++ b/go/client/game.go
@@ -3,13 +3,14 @@ package main
import (
"fmt"
"image/color"
- "log"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"muhq.space/muhqs-game/go/activities"
+ "muhq.space/muhqs-game/go/assets"
"muhq.space/muhqs-game/go/game"
+ "muhq.space/muhqs-game/go/log"
"muhq.space/muhqs-game/go/ui"
"muhq.space/muhqs-game/go/utils"
)
@@ -544,7 +545,7 @@ func (g *Game) handlePlayerNotifications() {
// FIXME
}
for n.Valid() {
- log.Println("Received", n)
+ log.Debug("Received", "notification", n)
switch n.Notification {
case game.PriorityNotification:
g.hasPriority = true
@@ -616,13 +617,14 @@ func (g *Game) progressPlayAction(a *game.PlayAction) {
g.addCancelablePrompt(a, fmt.Sprintf("Select targets to play %v", a.Card.Name))
g.handLayer.HighlightCard(a.Card, ui.HighlightSelectionColor)
} else {
+ log.Debug("No targets or no selection allowed", "action", a)
g.declareAction(a)
}
}
func (g *Game) handleSelection(obj any, x, y int) {
if obj == nil {
- log.Printf("No object found at cursor position (%d, %d)\n", x, y)
+ log.Debug("No object found at cursor", "cursor", game.Position{x, y})
return
}
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
+}
diff --git a/go/log/log.go b/go/log/log.go
index dbe3af66..b9262675 100644
--- a/go/log/log.go
+++ b/go/log/log.go
@@ -6,6 +6,7 @@ import (
"log"
"log/slog"
"os"
+ "strconv"
)
var GameLogLevel slog.Level = 99
@@ -21,9 +22,13 @@ func RegisterGameRecordHandler(handler func(any)) {
}
func init() {
- Logger = slog.New(slog.NewTextHandler(
- os.Stdout,
- &slog.HandlerOptions{AddSource: true}))
+ logLevel := 0
+ if logLevelVar, found := os.LookupEnv("LOGLEVEL"); found {
+ if envLogLevel, err := strconv.Atoi(logLevelVar); err == nil {
+ logLevel = envLogLevel
+ }
+ }
+ Logger = slog.New(NewGameLogHandler([]string{}, &slog.HandlerOptions{Level: slog.Level(logLevel)}))
slog.SetDefault(Logger)
}