# Console

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

```ts
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:

```ts
// 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.stringify`d 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:

```ts
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:

```ts
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

```ts
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.stringify`d. 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.
