Skip to main content

Transports

Transports decide where logs go. Sawdust splits them in two:

  • Core transportsconsole, pretty, consola. Configured under the transports object in configureLogger. Built into @cues/sawdust, no extra install.
  • Provider transports — Datadog server/browser logs. Shipped as factories in the separate @cues/sawdust-datadog package and wired through extraTransports. Core stays provider-agnostic; see Providers.

All of them are built on top of LogLayer.

Availability by runtime

TransportPackage / seamNodeBrowserWorkerPurpose
consolecore · transportsNative console / stdout / stderr.
prettycore · transportsHuman-friendly terminal / dev-console output.
consolacore · transportsConsola-backed output.
datadogTransport@cues/sawdust-datadog · extraTransportsDatadog server logs intake.
datadogBrowserTransport@cues/sawdust-datadog/browser · extraTransportsDatadog browser logs SDK.

Core transports

Enable any combination under transports in configureLogger.

console

console: {
enabled: true,
level: 'info',
appendObjectData: true, // print context object alongside the message
stringify: true, // emit JSON lines (great for log shippers)
messageField: 'msg', // JSON field names when stringify is on
dateField: 'ts',
levelField: 'level',
stream: 'stderr', // 'stdout' | 'stderr'
}
Structured JSON preset

For MCP servers and log shippers, the common shape is { stringify: true, messageField: 'msg', dateField: 'ts', levelField: 'level', stream: 'stderr' }. Centralize it so every service emits identical JSON.

pretty

pretty: {
enabled: process.env.NODE_ENV === 'development',
runtime: 'node', // 'node' | 'browser'
viewMode: 'inline', // 'inline' | 'expanded'
timestampFormat: 'mm:ss',
includeDataInBrowserConsole: true,
}

consola

consola: {
enabled: true,
createOptions: { level: 5 },
}

Provider transports (@cues/sawdust-datadog)

Datadog no longer lives inside the transports object. Install the provider package and pass its factory results into extraTransports (and, for APM correlation, plugins).

pnpm add @cues/sawdust-datadog

datadogTransport (server logs)

import { datadogTransport } from '@cues/sawdust-datadog'

extraTransports: [
datadogTransport({
service: 'orders-api',
logLevel: 'info',
apiKey: process.env.DD_API_KEY,
enableInDev: false,
options: { ddsource: 'nodejs', ddtags: 'env:prod' },
}),
]

Returns undefined when apiKey is missing; Sawdust skips falsy extraTransports entries.

Pairs with APM trace injection (a plugin, not a transport) so logs correlate to traces:

import ddTrace from 'dd-trace'
import { datadogTraceInjectorPlugin } from '@cues/sawdust-datadog'

plugins: [
datadogTraceInjectorPlugin({
apiKey: process.env.DD_API_KEY,
tracer: ddTrace.init(),
service: 'orders-api',
environment: 'production',
}),
] // Node only; returns undefined without both apiKey and tracer

datadogBrowserTransport (browser logs)

Requires the optional peer dep @datadog/browser-logs.

import { datadogBrowserTransport } from '@cues/sawdust-datadog/browser'

extraTransports: [
datadogBrowserTransport({
service: 'my-web-app',
environment: 'prod',
version: '1.0.0',
logLevel: 'info',
enabled: !!window.__DATADOG_CLIENT_TOKEN__,
init: {
clientToken: window.__DATADOG_CLIENT_TOKEN__,
forwardErrorsToLogs: true,
sessionSampleRate: 100,
},
}),
]

Returns undefined when init.clientToken is missing.

RUM

RUM is not a log transport — it is a separate browser subsystem reached through @cues/sawdust-datadog/rum. See the RUM guide. It requires the optional peer dep @datadog/browser-rum. Error-to-RUM forwarding is opt-in via the datadogRumErrorPlugin() plugin.

Merging overrides

Provide a subset of any core transport's options and Sawdust merges them over the defaults — you only specify what differs from the environment's baseline.