wasm-bindgen icon indicating copy to clipboard operation
wasm-bindgen copied to clipboard

Capture console output from custom workers

Open drewcrawford opened this issue 1 month ago • 1 comments

Capture console output from custom workers

Summary

This PR adds wasm_bindgen_test::forward_console_to_test_runner(), a public API that allows console output from user-spawned dedicated workers to be captured by the test harness.

Problem

Currently, wasm-bindgen-test-runner only captures console output from:

  • The main browser thread
  • The test runner's own worker (when using run_in_dedicated_worker, etc.)

Console output from custom workers spawned by user code is not captured - it goes directly to browser DevTools and is invisible to the test harness. This means they do not appear as seen from the command-line output of cargo test, wasm-bindgen-test-runner etc as they would be on other runners and other platforms.

Solution

This PR

Add a public API that users should call from their custom worker (or from their crate that spawns workers):

// In a custom worker's entry point:
wasm_bindgen_test::forward_console_to_test_runner();

// Now console.log, console.error, etc. will be captured
console_log!("This will appear in test output");

The function wraps console.debug/log/info/warn/error to send messages via postMessage in the format ["__wbgtest_<method>", [args...]], which the test runner already listens for.

Pros:

  • Simple, explicit, no magic
  • Less risk of breaking existing code
  • a PR in the hand is worth two in the bush

Cons:

  • Requires user (or ecosystem crates) to remember to call the function to get these logs
  • Only works for custom dedicated workers at present

Testing

Two new tests verify the functionality:

  1. forward_console_to_test_runner_sends_correct_message - Verifies console.log frmo dedicated worker sends correct postMessage format
  2. forward_console_all_methods - Verifies all five console methods are forwarded

Alternative idea: Monkeypatching

I think the best way to evaluate this is by comparison to the main alternative design.

I suspect we could automatically capture console output from all spawned workers without requiring new API or user code changes. That architecture would instead be along the lines of:

  1. Monkey-patch Worker constructor in the test runner's HTML to add a query parameter:

    const OriginalWorker = Worker;
    Worker = function(url, options) {
        const absoluteUrl = new URL(url, location.href).href;
        const patchedUrl = absoluteUrl + (absoluteUrl.includes('?') ? '&' : '?') + '__wbgtest_worker=1';
        return new OriginalWorker(patchedUrl, options);
    };
    
  2. Server-side script prepending - when the test server sees __wbgtest_worker=1, prepend console forwarding code:

    // In server.rs
    if request.url().contains("__wbgtest_worker=1") {
        let content = read_file(path);
        let wrapped = format!("{}\n{}", CONSOLE_FORWARD_BOOTSTRAP, content);
        return Response::from_data("application/javascript", wrapped);
    }
    
  3. Bootstrap code prepended to worker scripts:

    (function() {
        const methods = ['debug', 'log', 'info', 'warn', 'error'];
        for (const method of methods) {
            const original = console[method].bind(console);
            console[method] = function(...args) {
                self.postMessage(["__wbgtest_" + method, args]);
                original(...args);
            };
        }
    })();
    // ... original worker script follows ...
    

Pros:

  • Fully transparent - works without any user code changes
  • Captures output from all workers, including those in dependencies

Cons:

  • More likely to break user code without any code changes
  • Much more complex implementation, touches many wasm-bindgen components
  • Modifies scripts in flight
  • Many more edge cases than I've explained here (blob URLs, modules, relative imports...)

Comparison

It seemed simpler to start with the simple approach and get feedback.

drewcrawford avatar Dec 11 '25 13:12 drewcrawford

CodSpeed Performance Report

Merging #4856 will not alter performance

Comparing drewcrawford:capture-worker-logs (4123bc0) with main (ce43616)

Summary

✅ 4 untouched

codspeed-hq[bot] avatar Dec 11 '25 13:12 codspeed-hq[bot]

I like the solution, but would like to hear what others think about this as well.

In comparison to (3) that you present it seems to mainly save extra typing. But I'm all for that personally.

guybedford avatar Dec 15 '25 11:12 guybedford

mainly save extra typing

IMO the bigger tradeoff is that

  • Incomplete logs are dangerous; they send folks down some very wild debugging goose chases. Once one realizes that one is using custom workers at all, and that custom workers aren't being logged, and figures out there's an API that will escape log jail, and figures out where is the right place to call it, then this is a great solution to the problem. But getting to this point assumes a certain level of sophistication with wasm's logging quirks.
  • But we're already missing stdio logs anyway, so a certain awareness of wasm's logging quirks should be assumed. It would take a lot of implementation complexity to actually collect worker logs by default without breaking anything. Atomics users are more sophisticated anyway, so it's not unreasonable to ask them to learn 1 API.

I think they're both good arguments. I'm kind of persuaded by the idea that we could start with the simple thing now and see how that goes.

I'd be interested to hear from any users of custom workers and what their experience has been with missing logs.

drewcrawford avatar Dec 15 '25 22:12 drewcrawford