Skip to content

Custom transports

Subclass LoggerTransport or CfLoggerTransport, register before build(), and tune levels in flare.json.

AI generated, pending review Updated 10 days ago · Flare 0.3

Add a custom transport when you need logs in a sink beyond the default console output. Register every transport before the first host.build(); post-build registration does not update the live logger.

RuntimeBase classImport
Node.jsLoggerTransport@flare-ts/core
Cloudflare WorkersCfLoggerTransport@flare-ts/core

A transport is a class with:

  • static transportName: must match the key in log.transports when you override per-transport level
  • static deps: pinned empty; transports take no service dependencies, and calling inject() throws. Construction-time needs come from static config plus this.config() and from clients you open in onStart()
  • static config (optional): config tokens available via this.config() in lifecycle hooks
  • write(record: LogRecord): void: required; receives filtered records only
  • onStart?() / onStop?() (optional): lifecycle hooks in registration order (shutdown in reverse)

Example for Workers:

import { CfLoggerTransport, LOG_CONFIG } from "@flare-ts/core";
import type { LogRecord } from "@flare-ts/core";
class MetricsTransport extends CfLoggerTransport {
public static override readonly transportName = "metrics";
public static override readonly config = [LOG_CONFIG];
public write(record: LogRecord): void {
if (record.level === "error" || record.level === "fatal") {
// forward to your sink
}
}
}

Node example: subclass LoggerTransport instead of CfLoggerTransport. The rest of the shape is the same.

import { FlareHost } from "@flare-ts/core";
import { node } from "@flare-ts/core/node";
const host = new FlareHost(node);
host.logging.transport(MetricsTransport);
const app = host.build();
app.run();

The runtime’s default console transport is wired in first. Transports you register follow in registration order.

Name transports in flare.json under log.transports. Keys must match each class’s transportName:

{
"log": {
"level": "info",
"transports": {
"console": { "level": "debug" },
"metrics": { "level": "error" }
}
}
}

Per-transport level overrides the global log.level for that transport only.

RuleDetail
Register before build()The live logger snapshots its transport list during build()
No this.inject() in transportsOpen clients in onStart() and read settings with this.config(); calling inject() throws
Node async lifecycleonStart and onStop may return Promise<void>; the logger awaits them
Workers sync lifecycleCfLoggerTransport.onStart and onStop must not return a Promise