feat: structured events + log rotation/retention#25
Merged
Conversation
New internal/rlog package — io.WriteCloser that rotates the active log file by size, gzips the rolled file, enforces a count + total-bytes retention ceiling (oldest archives deleted first), and exposes a stub Shipper hook so operators can wire up remote offload later without touching the writer. Motivated by desk Pi disk-quota headroom: the daemon's --log-file tee (~/.marvel/log/daemon.log) currently grows unbounded. A single misbehaving reconciler can fill the filesystem overnight, especially while the RRD-style dedup idea (aae-orc-4wz) is still just an idea. Shipper stays as an interface with a NoopShipper impl; the real transports (scp, s3, rsync, http POST) are a separate probe when the offload-to-central-logger direction gets prioritized. Refs: aae-orc-k0t, Skippy session-025/026 raspi feedback.
Replaces the unbounded os.OpenFile append with rlog.Writer. Defaults set raspi-friendly caps: - --log-max-size 10 (MiB; rotate at 10 MiB) - --log-max-files 5 (keep 5 gzipped archives) - --log-max-total 0 (total-bytes cap disabled by default) Zero in any slot disables that specific limit. Backward-compatible for the common path — `marvel daemon --log-file X` now rotates into `X.<timestamp>.gz` archives instead of growing forever. Refs: aae-orc-k0t, Skippy session-025/026 raspi feedback.
New internal/events package — structured events (SessionCreated, SessionCrashed, SessionRestarted, HealthCheckFailed, ShiftStarted, etc.) stored in a bounded ring, filterable by workspace/team/role/ session/kind/severity. Complements internal/logbuf (raw daemon stderr stream) with queryable, structured history — marvel's equivalent of 'kubectl get events'. Producers (session.Manager, team.Controller) will be wired in the next commit. Emit() is nil-safe so a producer instantiated without a ring still runs without panic. Refs: aae-orc-k0t
Wire events.Emitter through both producers. Nil-safe (events.Emit
no-ops on a nil Emitter) so tests that don't inject a ring stay
quiet and existing callers that haven't been updated still compile.
Emission sites:
- Manager.Create → session.created
- Manager.Delete → session.deleted
- Manager.ReapDead → session.crashed (warning)
- Controller.restartSession:
- role.saturated (warning) when MaxRestarts reached
- health.crashloop-backoff (warning) on first backoff tick
- session.restarted (warning) on actual restart
- Controller.evaluateHealth → health.failed (warning) on first
failure-threshold breach
- Controller.InitiateShift → team.shift-started
- Controller.reconcileShift completion → team.shift-completed
Refs: aae-orc-k0t
Daemon owns an events.Ring (default 2000-event capacity), wires it to session.Manager and team.Controller at construction. New 'events' RPC method returns a filtered snapshot; new top-level 'marvel events' command prints a tabulated view with filters for workspace, team, role, session key, kind, and warnings-only. Usage: marvel events # last 100 events marvel events -n 500 marvel events --session demo/shell-g1-0 marvel events --kind session.crashed marvel events --warnings marvel --cluster desk events # remote via mrvl:// Complements 'marvel daemon logs' (raw stderr stream) with structured, queryable state-transition history — marvel's 'kubectl get events'. Refs: aae-orc-k0t
08ec6b3 to
a181065
Compare
This was referenced Apr 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two observability features landed together because they share the same motivation — operators need bounded, queryable signal on low-quota hosts like the desk Pi.
marvel daemon logs(raw stderr stream) with queryable, severity-tagged event history. Marvel's equivalent of `kubectl get events`.--log-filenow rotates by size, gzip-compresses archives, enforces count + total-bytes retention, and has a stub `Shipper` hook so remote-offload can land later without touching the writer.Closes ticket 0og (audit completed separately — no code changes needed, both optional-valued flags already had `NoOptDefVal` set).
New CLI surface
`marvel events`
Top-level command, remote-capable via mrvl://.
```sh
marvel events # last 100 events
marvel events -n 500
marvel events --session demo/shell-g1-0 # filter by session key
marvel events --workspace demo
marvel events --kind session.crashed
marvel events --warnings # severity filter
marvel --cluster desk events # remote via mrvl://
```
Emitted event kinds: `session.created`, `session.deleted`, `session.crashed`, `session.restarted`, `health.failed`, `health.crashloop-backoff`, `role.saturated`, `team.shift-started`, `team.shift-completed`.
`marvel daemon --log-max-*` flags
New on `marvel daemon`:
Zero in any slot disables that specific limit. Archives land alongside the active file as `daemon.log.20260418T183045Z.gz` — lexicographic sort == chronological, oldest-first retention.
Design notes
Test plan
Related
Refs: aae-orc-k0t, Skippy session-025/026 raspi feedback