Skip to content

perf(cli): stop session logs growing by tens of MB a day - #1745

Open
chphch wants to merge 1 commit into
slopus:mainfrom
chphch:perf/cli-log-volume
Open

perf(cli): stop session logs growing by tens of MB a day#1745
chphch wants to merge 1 commit into
slopus:mainfrom
chphch:perf/cli-log-volume

Conversation

@chphch

@chphch chphch commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Two writers dominate a Happy CLI session log, and neither carries anything a reader uses. logToFile inspects every non-string argument at depth: 5, so a single logger.debug('...', err) on an error that transitively holds a Node socket — which every failed HTTP or socket.io call produces — expands into hundreds of lines of TLS cipher lists and internal symbols. Separately, sessionScanner writes the path it is about to read plus a scan summary on every 3-second poll, for the entire life of a session, which is ~2400 identical lines an hour whether or not anything changed. On my machine ~/.happy/logs had reached 1.3 GB, with individual week-old sessions holding 80–90 MB each; measured across those files, the object dumps were 55% of all bytes written and the scanner's repeated lines another 32%.

This does not add a log level or a config knob — it stops writing bytes nobody reads:

  • Cap one inspected argument at 4000 chars. That keeps an error's own top-level fields, which is the part anyone actually reads, and drops the object graph hanging off them. Small objects are untouched.
  • Log scanner state changes, not polls. An absent transcript is reported once per absence rather than once per tick, and the scan summary is written when the poll actually moved a message, otherwise at most once a minute so "the scanner is alive and reading N entries" stays observable.

Proof

No UI surface, so this is measured rather than filmed.

Steady-state growth, two idle sessions on the same machine over the same 180 s window — one still on the pre-patch binary, one on the patched build:

session log growth per day
pre-patch 356 KB/h 8.3 MB
patched 16 KB/h 0.4 MB

The patched session's log shows the summary line at 60-second spacing (08:06:34, 08:07:34, 08:08:35) where the pre-patch one still writes a Reading session file: plus a summary every 3 s.

Truncation, against a real object of the kind that filled the logs (a live https response, which holds a TLS socket):

depth-5 inspect : 24,457 chars, 697 lines
after the cap   :  4,030 chars, 139 lines   + "[truncated 20457 more chars]"
small object    :     40 chars — unchanged, no marker

packages/happy-cli tsc --noEmit is clean and the sessionScanner + startFileWatcher suites pass. I have been running this on my own daily-driver instance since building it, which is where the 180 s comparison above was taken.

The two halves are independent and I am happy to split them into separate PRs if you would rather review them apart — they are together here because they were found by the same measurement of the same file.

Two writers dominated ~1.3 GB of session logs on one machine, neither of
them carrying information anyone reads.

`logToFile` inspects every non-string argument at depth 5. Any error thrown
by a failed HTTP or socket.io call transitively holds a Node socket, so a
single `logger.debug('...', err)` expanded into hundreds of lines of TLS
cipher lists and internal symbols — 55% of all bytes written. Cap one
inspected argument at 4000 chars, which keeps the error's own top-level
fields and drops the object graph behind them.

`sessionScanner` logged the file it was about to read and a scan summary on
every 3s poll, for the whole life of a session — ~2400 identical lines an
hour, 32% of all bytes. Log state changes instead: report an absent
transcript once per absence rather than once per tick, and write the scan
summary when the poll actually moved something, otherwise at most once a
minute so liveness stays observable.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
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