Console

Console patch (captureConsole)link

Forward console.warn and console.error into BugWatch without touching your application code. Exported from the @newinstance/bugwatch/console subpath.

What it is for

Use the console patch when you want log capture in minutes and cannot (or do not want to) introduce a logging library:

  • Legacy services where errors are reported with bare console.error(...) calls.
  • Scripts, cron jobs, and small workers with no logging setup.
  • A first-day proof that events reach the dashboard before you invest in a real logger.

It is deliberately minimal. The original console output is preserved, so nothing you already see in your terminal disappears.

Wiring it up

import { createClient } from "@newinstance/bugwatch";
import { captureConsole } from "@newinstance/bugwatch/console";

const client = createClient({ projectKey: process.env.BUGWATCH_KEY! });

// Patches console.warn and console.error (the default).
const restore = captureConsole(client);

Only warn and error can be patched, and both are on by default. Narrow the set with the levels option:

// Only forward console.error; leave console.warn untouched.
const restore = captureConsole(client, { levels: ["error"] });

Levels map to BugWatch severities directly: warn becomes 40, error becomes 50.

How arguments become an event

Every argument is stringified and joined with a single space to form the event message. Strings pass through as is, an Error contributes its message, and everything else is JSON.stringifyd with a String(value) fallback. If any argument is an Error instance, the first one found is attached as the event's error, so you get a stack trace on the dashboard:

console.error("payment failed", new Error("card declined"));
// message: "payment failed card declined", error: Error("card declined")

Re-entrancy guard and skipped SDK lines

Two protections keep the patch from feeding itself:

  • A re-entrancy flag. While the patch is inside captureLog, any nested console.warn/console.error triggered by that call is passed to the original console only and is not captured again.
  • Any call whose first argument is a string starting with [bugwatch] is skipped entirely. That is the prefix the SDK uses for its own debug output, so turning on debug mode never creates a capture loop.

The whole capture path is wrapped in a try/catch that swallows errors. A console patch never throws into your application.

No tags

The console patch sends level, message, and an optional error. It does not send tags. There is no place in a console.error(...) call to express structured fields, so nothing is inferred from the arguments. Events still pick up whatever the scope already carries (user, global tags, release, trace context), but per-call structured metadata is not available.

If you need per-call tags, use the native logger or a library adapter instead.

Restoring the original console

captureConsole returns an uninstall function. Calling it puts the original console.warn and console.error back for exactly the levels that were patched:

const restore = captureConsole(client);
// ... later, in a test teardown or on shutdown
restore();

Call restore() in test teardown so a patched console does not leak between test files.

When to prefer a real logger

Move off the console patch once any of these become true:

  • You want structured fields per log line. Use createLogger from the core package, or the Pino, Winston, Bunyan, or log4js adapters.
  • You want levels below warn. The patch cannot capture info or debug.
  • You want the SDK's local pretty printing. Only the native logger echoes locally; the console patch just leaves your existing output alone.

The patch and a real logger can coexist. A common setup is createLogger for new code plus captureConsole(client, { levels: ["error"] }) as a safety net over legacy paths.

Complete example

import { createClient } from "@newinstance/bugwatch";
import { captureConsole } from "@newinstance/bugwatch/console";

const client = createClient({
	projectKey: process.env.BUGWATCH_KEY!,
	environment: process.env.NODE_ENV ?? "development",
	release: process.env.GIT_SHA,
});

const restore = captureConsole(client, { levels: ["warn", "error"] });

client.setTag("service", "checkout");

console.warn("retrying charge", { orderId: "ord_1" });
console.error("payment failed", new Error("card declined"));

process.on("SIGTERM", async () => {
	restore();
	await client.flush();
	process.exit(0);
});

Troubleshooting

Nothing arrives in the dashboard. Confirm the client was created with a valid projectKey and that the process flushed before exiting. Short-lived scripts must await client.flush().

console.info and console.log are not captured. By design. Only warn and error can be patched.

Messages look like [object Object] or raw JSON. Non-string arguments are JSON.stringifyd. Pass a readable string first and keep objects for the structured logger.

My own [bugwatch]-prefixed logs are ignored. That prefix is reserved for the SDK's debug output and is filtered to prevent recursion. Rename the prefix in your own messages.

The patch is still active in tests. You did not call the returned restore(). Store it and call it in teardown.

Double capture. Calling captureConsole twice stacks patches, so each call is forwarded twice. Call it once at startup, and restore() before re-installing.