From d19ff35f50eaf745a5ec17aa540b60532da870bf Mon Sep 17 00:00:00 2001 From: Jonathon Leight Date: Sun, 5 Jul 2026 15:54:07 -0400 Subject: [PATCH] Last batch of error logging --- internal/analytics/analytics.go | 13 ++++++++++--- internal/core/confirm.go | 5 ++++- internal/core/console.go | 18 +++++++++++++----- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/internal/analytics/analytics.go b/internal/analytics/analytics.go index 77adb7c..1f0b869 100644 --- a/internal/analytics/analytics.go +++ b/internal/analytics/analytics.go @@ -10,6 +10,7 @@ import ( "context" "crypto/sha256" "encoding/hex" + "log/slog" "net" "net/http" "strings" @@ -60,7 +61,9 @@ func (rec *Recorder) Run(ctx context.Context) { if len(batch) == 0 { return } - _ = rec.st.InsertAnalyticsEvents(c, batch) + if err := rec.st.InsertAnalyticsEvents(c, batch); err != nil { + slog.Error("analytics: insert events", "count", len(batch), "err", err) + } batch = batch[:0] } for { @@ -86,8 +89,12 @@ func (rec *Recorder) Run(ctx context.Context) { } func (rec *Recorder) rollup(ctx context.Context) { - _ = rec.st.RollupAnalytics(ctx) - _ = rec.st.PruneAnalytics(ctx, retentionDays) + if err := rec.st.RollupAnalytics(ctx); err != nil { + slog.Error("analytics: rollup", "err", err) + } + if err := rec.st.PruneAnalytics(ctx, retentionDays); err != nil { + slog.Error("analytics: prune", "err", err) + } } // Handler wraps a handler, recording each request that isn't filtered out. A nil diff --git a/internal/core/confirm.go b/internal/core/confirm.go index 7c5c788..27b9504 100644 --- a/internal/core/confirm.go +++ b/internal/core/confirm.go @@ -15,6 +15,7 @@ import ( "github.com/meshcore-go/meshcore-go/hardware" "github.com/jleight/meshtender/internal/mesh" + "github.com/jleight/meshtender/internal/web" "github.com/jleight/meshtender/internal/wsbridge" ) @@ -92,7 +93,7 @@ func (s *Handlers) wsConfirm(w http.ResponseWriter, r *http.Request) { } repeaterID, err := meshcore.NewIdentityFromHex(rep.PublicKeyHex) if err != nil { - http.Error(w, "stored repeater key invalid", http.StatusInternalServerError) + s.ServerError(w, r, "stored repeater key invalid", err) return } @@ -203,6 +204,7 @@ func (s *Handlers) wsConfirm(w http.ResponseWriter, r *http.Request) { } if err := s.Store.SetRepeaterConfirmed(ctx, id, uid, lr.IsAdmin, int16(lr.Permissions)); err != nil { + web.LogError(r, "confirm: save confirmation", err, "repeater_id", id) _ = bridge.Status("error", "could not save confirmation: "+err.Error()) return } @@ -255,6 +257,7 @@ func (s *Handlers) wsConfirm(w http.ResponseWriter, r *http.Request) { }) if okLat && okLon { if err := s.Store.SetRepeaterLocation(ctx, id, lat, lon); err != nil { + web.LogError(r, "confirm: store location", err, "repeater_id", id) _ = bridge.Status("error", "could not store location: "+err.Error()) } else { _ = bridge.Status("info", fmt.Sprintf("Stored location: %.5f, %.5f", lat, lon)) diff --git a/internal/core/console.go b/internal/core/console.go index dd4a2b0..5b821f7 100644 --- a/internal/core/console.go +++ b/internal/core/console.go @@ -16,6 +16,7 @@ import ( "github.com/jleight/meshtender/internal/mesh" "github.com/jleight/meshtender/internal/store" + "github.com/jleight/meshtender/internal/web" "github.com/jleight/meshtender/internal/wsbridge" ) @@ -148,7 +149,7 @@ func (s *Handlers) pageConsole(w http.ResponseWriter, r *http.Request) { } catalog, err := s.Store.ListCommands(r.Context()) if err != nil { - http.Error(w, "could not load commands", http.StatusInternalServerError) + s.ServerError(w, r, "could not load commands", err) return } allowed := s.allowedCommands(r.Context(), rep, uid, catalog) @@ -174,12 +175,12 @@ func (s *Handlers) wsConsole(w http.ResponseWriter, r *http.Request) { } repeaterID, err := meshcore.NewIdentityFromHex(rep.PublicKeyHex) if err != nil { - http.Error(w, "stored repeater key invalid", http.StatusInternalServerError) + s.ServerError(w, r, "stored repeater key invalid", err) return } catalog, err := s.Store.ListCommands(r.Context()) if err != nil { - http.Error(w, "could not load commands", http.StatusInternalServerError) + s.ServerError(w, r, "could not load commands", err) return } @@ -208,6 +209,7 @@ func (s *Handlers) wsConsole(w http.ResponseWriter, r *http.Request) { // Group this connection's commands into a session (required for logging). sessionID, err := s.Store.StartConsoleSession(ctx, id, uid) if err != nil { + web.LogError(r, "console: start session", err, "repeater_id", id) _ = bridge.Status("error", "could not start session") return } @@ -311,6 +313,7 @@ func (s *Handlers) wsConsole(w http.ResponseWriter, r *http.Request) { } allowed, err := s.Store.CanSendCommand(ctx, uid, id, cmd.ID) if err != nil { + web.LogError(r, "console: permission check", err, "repeater_id", id, "command_id", cmd.ID) _ = bridge.Status("error", "permission check failed") return } @@ -319,7 +322,10 @@ func (s *Handlers) wsConsole(w http.ResponseWriter, r *http.Request) { return } - logID, _ := s.Store.LogCommand(ctx, id, uid, sessionID, cmd.ID, text) + logID, err := s.Store.LogCommand(ctx, id, uid, sessionID, cmd.ID, text) + if err != nil { + web.LogError(r, "console: log command", err, "repeater_id", id, "command_id", cmd.ID) + } reply, err := ex.Command(ctx, text, func(attempt, max int) { if attempt == 1 { @@ -331,7 +337,9 @@ func (s *Handlers) wsConsole(w http.ResponseWriter, r *http.Request) { switch { case err == nil: if logID != 0 { - _ = s.Store.MarkCommandReply(ctx, logID, reply) + if err := s.Store.MarkCommandReply(ctx, logID, reply); err != nil { + web.LogError(r, "console: mark command reply", err, "log_id", logID) + } } _ = bridge.Status("reply", reply) case errors.Is(err, mesh.ErrNoReply):