> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withboundary.com/llms.txt
> Use this file to discover all available pages before exploring further.

# createBoundaryLogger

> Full options reference for the SDK logger factory

```typescript theme={null}
import { createBoundaryLogger } from "@withboundary/sdk";
```

## Signature

```typescript theme={null}
function createBoundaryLogger<T = unknown>(
  options?: BoundaryLoggerOptions,
): BoundaryLogger<T> | null;
```

### Returns

* A `BoundaryLogger<T>` when either `apiKey` (or `BOUNDARY_API_KEY` env var) or `write` is configured.
* `null` otherwise — the dev-safe fallback. Passing `null` to `defineContract({ logger })` is a no-op.

## `BoundaryLoggerOptions`

| Option               | Type                                         | Default                          | Description                                                                                                                                                                                                                                                   |
| -------------------- | -------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `apiKey`             | `string`                                     | `process.env.BOUNDARY_API_KEY`   | Boundary ingest credential                                                                                                                                                                                                                                    |
| `environment`        | `"production" \| "staging" \| "development"` | —                                | Bucket events on the dashboard                                                                                                                                                                                                                                |
| `endpoint`           | `string`                                     | `"https://api.withboundary.com"` | Override the ingest endpoint (self-host, proxy)                                                                                                                                                                                                               |
| `model`              | `string`                                     | —                                | Default LLM label stamped onto every event; overridable per-call                                                                                                                                                                                              |
| `capture`            | `Partial<CapturePolicy>`                     | see below                        | Which buckets of data to ship                                                                                                                                                                                                                                 |
| `redact`             | `RedactionOptions`                           | —                                | `fields` / `patterns` / `custom` scrubbing                                                                                                                                                                                                                    |
| `batch.size`         | `number`                                     | `20`                             | Flush when queue hits this length. Small enough that low-traffic apps don't wait long for the first flush; large enough that bursts coalesce into one POST.                                                                                                   |
| `batch.intervalMs`   | `number`                                     | `5000`                           | Periodic flush cadence. Caps the worst-case time between an event firing and showing up on the dashboard. Set to `0` to disable the timer (recommended on Cloudflare Workers / Vercel Edge — the isolate freezes between requests, so timers are unreliable). |
| `batch.maxQueueSize` | `number`                                     | `1000`                           | Drop-oldest overflow threshold. Bounds memory during a backend outage. Tighten to \~100 on serverless/edge runtimes that recycle frequently.                                                                                                                  |
| `beforeSend`         | `(event) => BoundaryLogEvent \| null`        | —                                | Last-chance transform or drop                                                                                                                                                                                                                                 |
| `write`              | `(events) => void \| Promise<void>`          | —                                | Custom sink; fires alongside `apiKey`                                                                                                                                                                                                                         |
| `flushOnExit`        | `boolean`                                    | `true`                           | Attach runtime lifecycle drain hooks                                                                                                                                                                                                                          |
| `onError`            | `(err) => void`                              | one-time `console.warn`          | Permanent drop callback                                                                                                                                                                                                                                       |
| `fetch`              | `typeof fetch`                               | `globalThis.fetch`               | Injected fetch (tests, polyfills)                                                                                                                                                                                                                             |

### Default capture policy

```typescript theme={null}
{
  inputs: false,
  outputs: false,
  repairs: true,
}
```

Three optional buckets. Raw LLM inputs and outputs are off by default; repair messages are on so the dashboard can show how the model recovered. Failure attribution (`category`, `issues`, `ruleFailures`) and run metadata are always sent — they aren't gated. See [Capture policy](/sdk/capture-policy).

### Tuning by platform

| Runtime                          | Recommended overrides                                                                                      | Why                                                                                                                   |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Long-running Node                | Defaults                                                                                                   | The 5s timer drains continuously; 1000-event queue absorbs short outages.                                             |
| AWS Lambda / Vercel Functions    | `batch.maxQueueSize: 100`, optionally tighter `batch.size`                                                 | Containers freeze between invocations; flushes happen via `flush()` in your handler's `finally` block, not the timer. |
| Cloudflare Workers / Vercel Edge | `batch.intervalMs: 0`, `batch.maxQueueSize: 100`, drain via `ctx.waitUntil(logger.flush())`                | Timers are unreliable when the isolate freezes; explicit drain on the request lifecycle is the only reliable path.    |
| Browser                          | `batch.size: 10`, `batch.intervalMs: 2000`, `batch.maxQueueSize: 100`, `write` to your authenticated proxy | Browsers die more often than servers; smaller, faster batches, never an `apiKey` in the bundle.                       |

See [Node](/sdk/platforms/node), [Vercel & Lambda](/sdk/platforms/vercel-aws-lambda), [Cloudflare Workers & Edge](/sdk/platforms/cloudflare-workers-edge), and [Browser](/sdk/platforms/browser) for full per-runtime patterns.

## `BoundaryLogger<T>`

The returned object:

```typescript theme={null}
type BoundaryLogger<T = unknown> = ContractLogger<T> & {
  flush: (timeoutMs?: number) => Promise<void>;
  shutdown: (timeoutMs?: number) => Promise<void>;
};
```

* Implements every `ContractLogger<T>` hook (assign it directly to `defineContract({ logger })`).
* `flush(timeoutMs?)` — drain the queue; logger stays active.
* `shutdown(timeoutMs?)` — drain, stop the timer, disable further sends. Idempotent.

See [Shutdown](/sdk/shutdown) for timeout semantics and per-platform recipes.

## Minimal example

```typescript theme={null}
import { createBoundaryLogger } from "@withboundary/sdk";
import { defineContract } from "@withboundary/contract";

const logger = createBoundaryLogger({
  environment: "production",
});

const contract = defineContract({
  name: "lead-scoring",
  schema,
  rules,
  logger,
});
```

## Full example

```typescript theme={null}
const logger = createBoundaryLogger({
  apiKey: process.env.BOUNDARY_API_KEY,
  environment: "production",
  model: "gpt-4.1",
  capture: {
    inputs: false,
    outputs: false,
    repairs: true,
  },
  redact: {
    fields: ["ssn", "email"],
    patterns: [/\b\d{3}-\d{2}-\d{4}\b/],
    custom: (value, path) =>
      path.at(-1) === "customerId" && typeof value === "string"
        ? `cust_${hash(value).slice(0, 10)}`
        : value,
  },
  batch: {
    size: 50,
    intervalMs: 2000,
    maxQueueSize: 2000,
  },
  beforeSend(event) {
    return event.contractName === "health-check" ? null : event;
  },
  write(events) {
    for (const e of events) console.log(JSON.stringify(e));
  },
  onError(err) {
    metrics.increment("boundary.drop");
  },
});
```

## Related types

* [`BoundaryLogEvent`](/sdk/boundary-log-event) — the wire format
* [`CapturePolicy`](/sdk/capture-policy) — the three buckets, plus the always-on fields
* [`RedactionOptions`](/sdk/redaction) — `fields` / `patterns` / `custom`
* Full SDK types: [Types reference](/api-reference/types)

## See also

<CardGroup cols={2}>
  <Card title="SDK Quickstart" icon="rocket" href="/sdk/quickstart">
    Install and wire the logger
  </Card>

  <Card title="Shutdown" icon="power-off" href="/sdk/shutdown">
    flush + shutdown + signal handling
  </Card>
</CardGroup>
