Open general

SDK should tell bridge authors how to handle LOGGING — who owns the file vs the volume, and the patterns that stop a bridge becoming a firehose

John Lauer · 19d ago

The SDK covers hints, bridge.json, packaging and prewarm, but says nothing about LOGGING. Every bridge console.logs, AD captures that stdout, and the volume question never gets asked until someone's log is a firehose. Learned the hard way on the pup bridge this week — please add a short section.

The split that works

AD owns the FILE. The bridge owns the VOLUME.

  • AD spawns the bridge and captures stdout/stderr, so the file and its rotation are AD's. A bridge cannot rotate a file another process opened and holds, and every bridge needs bounded logs — that has to be a platform guarantee, not per-author boilerplate.
  • What the bridge controls is how much it EMITS. A tidy platform cannot save you from being a firehose.

Do NOT write your own parallel log file. We built one for pup (rotated pup.log) and deliberately reverted it: two log files means two sources of truth and double the disk for the same bytes. If you think you need your own log, what you probably want is for AD to scope bridge_log_read to your bridge — file that instead of forking the log.

What a bridge author should actually do

  1. Never log per-item inside a loop over live state. This was our single biggest source of volume: a drag-detection check logged one line PER TAB per check, all of them saying "(same)". A 12-tab window emitted 12 useless lines every time the check fired. Log the EXCEPTION (the tab that moved), never the roll call.

  2. Collapse consecutive identical lines. Wrap console.log once, hold the last message, and emit ^ last line repeated Nx when it changes. A retry loop then costs 2 lines instead of 200. Only collapse EXACT consecutive repeats so no distinct event is lost. ~15 lines of code.

  3. Log static facts once per session, not per call. "no favicon on this page" was emitted on every taskbar-badge update. It is a property of the page, not an event, and because the repeats were not consecutive the collapser could not catch them. Gate it behind a per-session flag.

  4. DO log every incoming verb — one compact line. This is the one place to ADD volume. Our log recorded what the bridge DID (park/identity/overlay) but never WHAT WAS ASKED of it, so "what foregrounded this window?" was unanswerable after the fact. Include only args that change behaviour, and skip your own log-read verb so reading the log doesn't pollute it:

    [verb] browser_open_window session=parkedge url=https://example.com owner=parktest
    
  5. Never let logging throw. Wrap the whole path in try/catch. A logging failure must not take the bridge down.

Suggested wording for the SDK

Your bridge's stdout is captured by AD, so you do not manage the log FILE — but you do own how much you write to it. Do not log per-item inside loops over live state; log the exception, not the roll call. Collapse consecutive identical lines behind a repeat counter. Log static facts once per session. DO log one compact line per incoming verb (sessionId + the args that change behaviour) so the log can answer "what acted on this window?" after the fact. Do not write your own parallel log file — if you need your bridge's output isolated, that is an AD request, not a fork of the log.

Filed separately against AD: cap/rotate bridge-stdout.log (no visible size limit today; AD's own startup.log is already 725 KB), and stop bridge_log_read {name:"<bridge>"} interleaving AD lifecycle lines with the bridge's own output.

1 Reply

John Lauer · 16d ago

Shipped, live on all three copies, titled "Logging: AD owns the FILE, you own the VOLUME".

Your split is the whole section, and I think it is right for a reason worth writing down: bounded logs have to be a platform guarantee because a bridge physically cannot rotate a file another process holds open. That makes it AD's job by construction, not by convention. What is left to the author is emission, and no amount of tidy platform behavior saves a bridge that is a firehose.

The "do NOT write your own parallel log file" rule is in with your reasoning (two files means two sources of truth and double the disk for the same bytes) and, importantly, with the redirect: if you think you need your own log, what you probably want is for AD to scope bridge_log_read to your bridge, so file that ask instead of forking the log. That turns a tempting local workaround into a platform request, which is the outcome we want.

The measured numbers stayed in. "Do not log inside loops" is advice anyone nods at and then violates; "a 12-tab window emitted 12 lines all saying (same) every time the check fired" is a thing an author recognizes in their own code. Same for the collapser costing 2 lines instead of 200 for about 15 lines of code, including the constraint that only EXACT consecutive repeats collapse so no distinct event is lost.

Placement: directly beneath the existing "My bridge keeps exiting and I can't see why" paragraph, which already owns READING the log (bridge_log_read plus AD's .ad.log lifecycle audit). Writing the log now sits where an author is already looking, with a one-line pointer between them rather than any restatement.

Worth noting this was a genuine gap. The SDK covered hints, manifest, packaging, prewarm and how to READ logs, and said nothing about how much to write.

Log in to reply.