Skip to content

Logpoints: log runtime values without modifying source code #2584

Description

@johan-gorter

Is your feature request related to a problem? Please describe.

When an agent needs to know what a page actually does at runtime — which branch
ran, what a value held, in what order events fired — its only real option today is to
edit the source, add console.log, rebuild, reload, read the output, and then remove
the calls again. That has four costs:

  1. It's a write to the user's codebase to answer a read-only question, and stray
    console.log calls get left behind and committed.
  2. Every probe added or adjusted costs a full rebuild + reload of the frontend
    bundle — slowest exactly when the agent is iterating fastest.
  3. It requires source access and a local build. Debugging a deployed environment
    (staging, a hosted app, anything the agent didn't build) can't be instrumented at
    all.
  4. The reload can destroy the state being observed — which matters a lot for
    lifecycle bugs.

@natorion already put this better than I can, in #567 while discussing initScript:

I think its related to having log points, which would be like a specific screw
driver, which is superior to a multitool, if you have to work with screws.

This is a proposal for that screwdriver.

Describe the solution you'd like

Logpoints, as in the DevTools Sources panel: log an expression every time a line
executes, without pausing and without touching the source.

  • set_logpointurl or urlRegex, lineNumber (1-based), optional
    columnNumber, and expression formatted like console.log arguments and
    evaluated in the scope of that line, so locals are accessible. url accepts either
    a script URL as served, or an original source file (e.g. src/app.ts, suffix
    match) resolved through the parsed scripts' source maps — and re-resolved
    automatically when a rebuilt bundle loads. Survives reloads and navigations until
    removed or the page closes.
  • list_logpoints — what's currently active.
  • remove_logpoint — one by id, or all on the page when id is omitted.
  • list_scripts — search script URLs and source-map sources, so the agent can
    find the file to target.

Output arrives as ordinary console messages via list_console_messages, so no new
output channel is needed.

Logpoints never pause the page. Implementation is CDP
Debugger.setBreakpointByUrl with a condition that is an IIFE calling console.log
and returning false — the same trick the Sources panel uses. Execution is never
suspended.

A concrete case from our own use. An agent was asked why a spinner span forever
after navigating away from our app and pressing Back. It read the code, formed a
hypothesis, then confirmed it by setting three logpoints on authored TypeScript
lines — a navigation module and two engine methods — resolved through source maps into
a webpack chunk, plus pagehide/pageshow with their persisted flags. The captured
sequence showed a bfcache teardown bug: teardown ran on beforeunload, the page was
frozen with persisted=true, the WebSocket was closed by bfcache, and on restore
there was no popstate and no afterNavigate — so the app never re-entered the
page. The decisive evidence was a line that never executed, which is exactly what
reading code cannot give you and what a logpoint shows cheaply. Three probes, no
rebuild, no source change, and one remove_logpoint call to clean up all of them.

Describe alternatives you've considered

  • initScript on navigate_page (feat: Add ability to inject script to run on page load #568) — closest existing tool, but the
    instrumentation has to be written up front, it only runs at document start so a
    probe can't be added mid-session, it needs a reload, and it can't target "line 42 of
    this .tsx".
  • evaluate_script — point-in-time only. It can't observe a specific line as it
    executes, and it has no access to local scope there.
  • Editing in console.log calls — the status quo, with the four costs above.
  • Interactive debugging (Interactive debugging support #407) — related but a different feature. Pausing and
    stepping is stateful and blocks the page, which is risky to hand an agent. Logpoints
    are non-blocking and need none of that machinery; they arguably deliver much of the
    practical value of Interactive debugging support #407 without the hang risk, and could be a first step toward it.

Additional context

I have this implemented and working:
feat/logpoints
(needs a rebase onto current main, which has since refactored DevtoolsUtils.ts).
Our team has been using it daily for about six weeks; it's changed how our agents
investigate runtime behaviour, and the non-local case in particular — debugging a
hosted environment we can't rebuild — isn't reachable any other way.

Known limitations I'd call out rather than hide: a logpoint placed on a line that runs
during initial script evaluation can miss executions that happen before the source map
is fetched, and a line with no executable code slides to the next valid location (same
as DevTools).

Happy to open a PR if the direction is welcome.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions