Skip to main content
Application logs are in public preview and can change before reaching general availability.
Requires TypeScript SDK v3.39.0 or later, and BRAINTRUST_API_KEY set to a Braintrust API key.
Use the TypeScript SDK to send application logs to Braintrust. Emit messages directly from a project logger, or forward the messages your code already writes with console.

Emit logs from the SDK

To add log statements to your code, initialize a project logger and call a severity method: trace(), debug(), info(), warn(), error(), or fatal(). The trace() method emits a message at the trace severity level. It doesn’t start a Braintrust trace.
Each call creates one row with span_attributes.type set to log: The body can be any JSON-serializable value, not just a string. Each method returns the row ID. With asyncFlush: false, it returns a promise that resolves to the row ID after the row is flushed.

Preserve message templates

Pass a template literal tagged with logger.fmt to group messages by a stable template while retaining each event’s values. Format values with JavaScript expressions inside the template:
The record stores the rendered message, the template with numbered placeholders, and the parameters:
Parameters keep their JSON value types, and undefined and BigInt values are stored as strings. Template metadata replaces keys of the same name in the metadata option. A template literal without logger.fmt is stored as a plain message without template metadata. Because the template stays the same across events, you can group by it to find your most frequent messages with SQL:
Most frequent message templates

Capture console output

wrapConsole() forwards calls to console.log(), console.trace(), console.debug(), console.info(), console.warn(), and console.error() to Braintrust without changing existing log calls. Console output still appears as before.
Each console method maps to the severity level of the same name, except console.log(), which maps to info. The record stores the formatted message in output and sets logger.name to console in metadata. When the first argument is a string followed by more arguments, the record also stores it in braintrust.template and each remaining argument in braintrust.template.parameter.<index>. String, number, boolean, and null arguments keep their values, and other arguments are stored as formatted text. Outside Node.js, such as in browsers, Cloudflare Workers, and edge runtimes, the message joins the arguments with spaces instead of applying format specifiers such as %s. wrapConsole() accepts two optional arguments:
  • target: the console object to wrap. Defaults to the global console.
  • options.logger: the logger that receives console messages. Defaults to the current logger at the time of each call. If no current logger exists, for example before you initialize one or when your logger uses setCurrent: false, console calls produce their normal output only.
It returns a cleanup function that stops forwarding for that wrap. Calling wrapConsole() again on the same console updates the destination without duplicating messages. Cleanup doesn’t disable a later wrap, and it doesn’t flush messages that were already captured. The SDK’s own diagnostic output isn’t forwarded, and a console call made from inside another console call, such as from a patched console method, is written to the console only.

Associate logs with a trace

Emit a message inside an active Braintrust span to associate it with that operation. For example, log a retry inside the payment operation that triggered it:
The message has its own row ID and shares the active span’s span and trace IDs. Logs can also correlate with active OpenTelemetry spans when you configure OpenTelemetry compatibility mode. Without an active span, each SDK logger instance groups its messages under a shared trace ID. Those messages can belong to different requests. Use an active span to associate messages with a specific request or operation.

Next steps