Skip to content

fix: cut log noise and add severity-based searching - #23

Merged
bmdavis419 merged 3 commits into
mainfrom
fix/log-noise-and-searchability
Jul 13, 2026
Merged

fix: cut log noise and add severity-based searching#23
bmdavis419 merged 3 commits into
mainfrom
fix/log-noise-and-searchability

Conversation

@bmdavis419

@bmdavis419 bmdavis419 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Problem

Live testing of the new first-class logs feature on three running boxes (columbia, liles-box, media-bot) found the durable streams flooded with noise:

  1. Heartbeat flood: executor daemon: reachable (remote) was logged by every 20s reconcile cycle — 17,360 of 17,419 agent events in 24h (99.7%) on media-bot, >94% of all events on every box. A plain tx9 logs <box> showed 200 identical lines.
  2. Executor duplicate line: nearly all executor events were the identical [executor] MCP session mode {...} line repeated 2–4× every ~11 minutes.
  3. Multi-line explosion: multi-line Hermes records (skill files, shutdown diagnostics with full ps output) split into 50–104 per-line events sharing one timestamp; grep returned contextless fragments, plus truncated duplicates ([Tool lo, [Tool loop warning: same_too) from re-reading the mid-write tail of the active log file.
  4. No severity field: no way to ask for "operational warnings only" — errors had to be found by grepping WARNING.
  5. Smaller bugs: loadavg-like fragments parsed as 1970-01-01 timestamps (invisible to every --since); ~80% of codex events had empty messages.

Fixes

  • guest/hb: reconcile-loop Executor reachability reporting is now state-change-only, tracked in a /tmp state file that resets with the container. Interactive paths (hb up, etc.) always print and never consume the loop's state, so a manual invocation can't swallow the next flip notification.
  • guest/tx9-logs capture: consecutive identical lines collapse into one structured event plus an output_repeated summary with the count (60s window, 1000-repeat cap). Raw text logs and the mirrored stdout/stderr stay byte-faithful. Blank lines no longer become structured events.
  • guest/tx9-logs query: Hermes raw-log continuation lines (no leading timestamp or level token) group into the record that introduced them, capped at 1000 lines/1MiB; the possibly mid-write final line of the active file is skipped (kills truncated duplicates); text output renders multi-line messages with indented continuations.
  • Severity: tx9.event.v1 gains an optional level field (debug|info|warn|error), best-effort parsed from python-logging prefixes, level= tokens, and native record fields (level/levelname/severity), omitted when unknown — backward compatible. New --level flag on tx9 logs and tx9 logs export keeps that level and above; unleveled events count as info, so --level warn silences unleveled chatter while --level info shows everything.
  • Timestamps: numeric values below epoch 1e9 (pre-2001) are rejected, so loadavg fragments fall back to file-mtime ingest time instead of 1970.
  • Empty events: native-history records with no extractable text are dropped.

Impact on media-bot: ~17k noise events/day → ~60 real ones.

Tests

  • tests/regressions-hb-workload.sh: state-change-only reporting across reachable→unreachable→reachable flips; interactive up always reports; reconcile still fails loudly when unreachable.
  • tests/regressions-logs.sh: capture dedup + output_repeated counts + raw-log fidelity; continuation grouping; active-file partial-tail skip; epoch-timestamp rejection; level parsing/filtering incl. bogus-value drop on schema-v1 records; multi-line text rendering; empty-message drop (flips one existing assertion that pinned the old behavior).
  • internal/cli/cmd_logs_test.go: --level in helper args, omitted when unset, value validation.

make check passes (syntax, shellcheck, static contracts, shell regressions, go vet/build/test).

Notes

  • Old boxes: passing --level to a pre-upgrade in-box helper fails with the existing "upgrade the box" guidance; the flag is never passed when unset.
  • Timestamp-less warning lines grouped under one record still share the mtime-based fallback timestamp — inherent to timestamp-less sources.

🤖 Generated with Claude Code


Open in Devin Review

Note

Add severity-based log filtering and suppress steady-state reconcile log noise

  • Adds --level flag (debug/info/warn/error) to tx9 logs and tx9 logs export; unlabeled events are treated as info. Level is validated in cmd_logs.go and forwarded to the Python helper.
  • Parses and normalizes severity from common log formats (prefix tokens, key-value pairs) in guest/tx9-logs, annotating structured events with a level field used for threshold filtering.
  • Collapses consecutive duplicate structured events within a time window into a single output_repeated summary; raw bytes and mirrored outputs remain unmodified.
  • Groups multi-line raw log records (e.g. Hermes tracebacks) into single events with timestamp fallback and level detection; partial active-file tail records are withheld until complete.
  • Suppresses repetitive executor reachability messages in the hb reconcile loop via _steady_report, which persists last-seen state and only logs on transitions when HB_STEADY_QUIET=1.
  • Behavioral Change: reconcile no longer logs executor reachability on every cycle; only state transitions produce output.

Macroscope summarized 23d6b8e.

Greptile Summary

This PR reduces log noise and adds severity-based log filtering. The main changes are:

  • Added --level filtering for log queries and exports.
  • Added best-effort severity parsing for structured and raw log events.
  • Collapsed repeated captured output into repeat-count events.
  • Grouped Hermes multi-line raw records into single events.
  • Suppressed steady-state executor reachability logs in the reconcile loop.
  • Added safer timestamp handling and empty-message filtering.

Confidence Score: 5/5

This looks safe to merge.

No blocking issues found in the changed code.

No files need attention.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a proof for a posted P1 finding.
  • T-Rex performed general-contract-validation-proof work by running regression checks, inspecting logs, and validating the focused repro and downstream test results, which show the regression command exit code 4, the focused repro reporting three matching stdout events before jq returns with JQ_EXIT: 4, and the heartbeat workload and CLI tests passing (exit code 0).

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
guest/tx9-logs Adds level parsing, filtering, repeated-output summaries, raw continuation grouping, active-tail handling, and empty-message filtering.
guest/hb Adds temporary state for state-change-only executor reachability reporting during reconcile.
internal/cli/cmd_logs.go Adds validated --level plumbing for logs query and export helper calls.
internal/cli/cmd_logs_test.go Covers --level helper arguments and value validation.
tests/regressions-logs.sh Adds coverage for deduplication, level filtering, grouped raw records, active JSONL tails, timestamp rejection, and empty-message drops.
tests/regressions-hb-workload.sh Adds coverage for state-change-only reconcile logging and interactive executor reporting.

Comments Outside Diff (4)

  1. guest/tx9-logs, line 1392-1394 (link)

    P1 Final JSONL Record Is Hidden

    When the active structured log ends with a valid JSON object that was flushed without a trailing newline, this branch drops the whole record. That can hide the last structured event from queries and exports even though the record is already present in the file.

    Artifacts

    Repro: focused harness that creates a final JSONL record without newline and invokes query/export

    • Contains supporting evidence from the run (text/x-python; charset=utf-8).

    Repro: command transcript showing valid bytes are omitted by query and export

    • Keeps the command output available without making the summary code-heavy.

    View artifacts

    T-Rex Ran code and verified through T-Rex

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: guest/tx9-logs
    Line: 1392-1394
    
    Comment:
    **Final JSONL Record Is Hidden**
    
    When the active structured log ends with a valid JSON object that was flushed without a trailing newline, this branch drops the whole record. That can hide the last structured event from queries and exports even though the record is already present in the file.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Codex

  2. General comment

    P1 Logs regression script fails because jq -e select assertion returns 4 after later non-matching records

    • Bug
      • The explicit logs regression test plan cannot pass on the head checkout. In the first capture/redaction subcase, executor.jsonl contains matching stdout output records, but the assertion uses jq -e 'select(.source == "executor" and .stream == "stdout" and .type == "output")' file >/dev/null. Since later records in the same JSONL stream are stderr/event records that do not match and produce no output, jq exits 4 even though earlier matching records were emitted.
    • Cause
      • The test assertion relies on jq -e select(...) over a multi-record stream. jq's -e status is based on the last output status, and a later input that produces no output makes the command fail despite earlier matches.
    • Fix
      • Change the assertion to aggregate over the stream, for example jq -e 'any(inputs?; .source == "executor" and .stream == "stdout" and .type == "output")' with appropriate input handling, or use jq -e '[. | select(...)] | length > 0' under slurp mode (-s).

    T-Rex Ran code and verified through T-Rex

  3. General comment

    P2 logs regression script fails because jq -e select assertion depends on the final JSONL record

    • Bug
      • tests/regressions-logs.sh fails at the first executor stdout JSONL assertion even though matching stdout output records exist. The command uses jq -e 'select(...)' file >/dev/null over the full JSONL stream. With jq -e, the process exit status is based on the last output value; because the final input record is process_exit and does not match the select, jq exits 4.
    • Cause
      • The shell test uses jq -e select(...) directly on a multi-record JSONL file where later non-matching records can make the assertion fail despite earlier matching records.
    • Fix
      • Change the assertion to aggregate matches or terminate successfully on the first match, for example jq -e 'any(inputs?; .source == "executor" and .stream == "stdout" and .type == "output")' with suitable JSONL handling, or jq -e 'select(...)' file | grep -q . without relying on jq's final-record exit semantics.

    T-Rex Ran code and verified through T-Rex

  4. General comment

    P1 regressions-logs.sh fails on a valid JSONL stream because jq -e select returns 4

    • Bug
      • The explicit log regression script aborts at its first JSONL validation. The generated executor.jsonl contains multiple records matching .source == "executor" and .stream == "stdout" and .type == "output", and the focused repro shows jq prints those matching objects. However, the command still exits 4, causing set -e to abort tests/regressions-logs.sh before the remaining deduplication, multiline, timestamp, and level-filtering coverage can run.
    • Cause
      • The changed test script uses jq -e 'select(...)' file >/dev/null. With -e, jq's exit status is based on the last result processed, not merely whether any earlier input matched. Because later records such as stderr/process_exit do not match the select predicate, jq exits 4 despite earlier matches.
    • Fix
      • Change the assertion to aggregate matches before applying -e, for example: jq -e 'any(.source == "executor" and .stream == "stdout" and .type == "output")' "$executor_root/logs/executor.jsonl" >/dev/null, or collect/select then test length > 0 with slurp mode.

    T-Rex Ran code and verified through T-Rex

Reviews (3): Last reviewed commit: "fix: preserve log record boundaries" | Re-trigger Greptile

Live testing on three boxes showed the durable streams were >94% noise:
one reconcile-loop heartbeat line accounted for 17,360 of 17,419 daily
agent events, executor logs were a single repeated MCP line, and
multi-line Hermes records exploded into contextless per-line fragments.

- hb: report Executor reachability state-change-only from the reconcile
  loop via a /tmp state file (resets with the container); interactive
  paths such as `hb up` always print and never consume the loop's state.
- tx9-logs capture: collapse consecutive identical lines into one
  structured event plus an output_repeated summary carrying the count;
  raw logs and mirrored stdout/stderr stay byte-faithful. Blank lines no
  longer become structured events.
- tx9-logs query: group timestamp-less continuation lines (tracebacks,
  embedded files, ps output) into the Hermes record that introduced
  them; skip the possibly mid-write final line of the active raw file
  (fixes truncated-duplicate fragments); render multi-line messages with
  indented continuations.
- Add best-effort level (debug/info/warn/error) to tx9.event.v1, parsed
  from python-logging prefixes, level= tokens, and native record fields;
  new --level filter keeps that level and above, counting unleveled
  events as info. Omitted when unknown, so the schema stays compatible.
- Reject sub-2001 numeric timestamps so loadavg-like fragments fall back
  to file mtime instead of 1970 (they were invisible to every --since).
- Drop native-history records with no extractable text (~80% of codex
  events were empty).

Timestamp-less warning lines grouped under one record still share the
mtime-based fallback timestamp; that is inherent to the source format.
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Logging now suppresses repeated reconcile and capture output, groups multiline records, parses severity levels, supports --level filtering for queries and exports, and documents and tests the updated behavior.

Logging behavior

Layer / File(s) Summary
Steady-state reconcile reporting
guest/hb, tests/regressions-hb-workload.sh
Executor status messages are persisted by key and repeated states are suppressed during reconcile cycles while transitions and interactive reports remain visible.
Capture and event normalization
guest/tx9-logs
Structured output deduplication, repeat summaries, multiline raw-record grouping, severity parsing, schema validation, and timestamp checks are implemented.
Level filtering and CLI propagation
guest/tx9-logs, internal/cli/cmd_logs.go, internal/cli/cmd_logs_test.go
Queries and exports accept validated --level filters, apply unlabeled-event handling, display levels, and propagate the option to the helper.
Regression coverage
tests/regressions-logs.sh
Regression cases cover bounded records, deduplication, continuation grouping, severity parsing, filtering, exports, rendering, and incomplete records.
CLI behavior documentation
README.md, docs/tx9-cli-design.md
Documentation describes level parsing, filtering, export metadata, deduplication, byte-faithful raw logs, multiline grouping, and portable exports.

Possibly related PRs

  • davis7dotsh/tx9#22: Introduces the tx9 logs and tx9-logs query/export stack extended by this change.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main changes: reducing log noise and adding severity-based searching.
Description check ✅ Passed The description matches the changeset and covers the log-noise reduction and severity-filtering work.

Comment @coderabbitai help to get the list of available commands.

macroscopeapp[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

greptile-apps[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

macroscopeapp[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

@bmdavis419
bmdavis419 merged commit 4d1f99b into main Jul 13, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant