Logging methods

Log levels, structured data, context, and capturing errors by hand.

Log levels

LevelUse it for
traceVery detailed steps you only want while chasing a problem.
debugDetail that helps while developing.
infoNormal events: a sign-up, an order placed.
warnSomething unexpected that the app recovered from.
errorSomething failed. Becomes part of an issue.
fatalSomething failed badly enough to stop the app. Becomes part of an issue.

Logs below minLogLevel (default info) are dropped in the SDK. See Passing a log level.

Logging methods

MethodArguments
trace, debug, info, warn(message: string, data?: object)
error, fatal(message: string, error?: Error, data?: object)
captureException(error: Error, context?: object)
captureMessage(message: string, level?: LogLevel, context?: object)
addBreadcrumb(message: string, category?: string, data?: object)

Every method returns straight away. The log waits in memory and goes out with the next batch.

Attaching data

Pass an object as the last argument. It is stored with the log, and you can see it in the log’s details:

TypeScript
logger.info('Order placed', {
  orderId: 'order-123',
  items: 3,
  total: 42.5,
});

The data goes through redaction first, so a field called email or password is masked.

Logging errors

error() and fatal() take an Error as their second argument. The SDK sends its name, message and stack trace, and Apperio groups it into an issue by those.

TypeScript
try {
  await chargeCard();
} catch (err) {
  const error = err instanceof Error ? err : new Error(String(err));
  logger.error('Payment failed', error, { orderId: 'order-123' });
}

In TypeScript a caught value has type unknown, so turn it into an Error as above before passing it.

captureException and captureMessage

  • captureException logs the error at error level with the message Exception captured. The issue is still titled from the error itself.
  • captureMessage logs a message at the level you choose (default info).
  • The optional context is added to the logger’s context for this one log only.
TypeScript
import { LogLevel } from 'apperio';

logger.captureException(new Error('Stock check failed'), { sku: 'sku-42' });
logger.captureMessage('Payment provider slow', LogLevel.WARN);

Breadcrumbs are sent at debug level

addBreadcrumb sends a separate log at debug level, so with the default minLogLevel of info it is dropped.

It also isn’t added to the trail of recent clicks, requests and page views that the SDK attaches to errors it captures by itself. To leave a trail you will see, log the step at info instead.

Context

Context is data added to every log from then on, such as the signed-in user’s ID:

TypeScript
logger.setContext({ userId: 'user-42', plan: 'pro' }); // merged into what is there
logger.getContext();                                   // a copy of the current context
logger.clearContext();                                 // removes everything

When it starts, the SDK puts some context there for you: the user agent, page address and referrer in a browser, or the platform and Node.js version on a server. clearContext() removes those too.

Sending now

await logger.flush() sends everything waiting in memory without waiting for the next batch. Use it before a short script exits or a serverless function returns. See Shutdown.

What a log looks like

A logger.error() call from the example above arrives roughly like this (the context is shortened):

JSON
{
  "projectId": "your-project-id",
  "timestamp": "2026-01-15T12:00:00.000Z",
  "level": "error",
  "message": "Payment failed",
  "data": { "orderId": "order-123" },
  "error": {
    "name": "Error",
    "message": "Card declined",
    "stack": "Error: Card declined\n    at chargeCard (https://example.com/assets/app.js:1:2345)"
  },
  "service": "web",
  "environment": "production",
  "release": "2.4.1",
  "context": { "url": "https://example.com/checkout" },
  "sessionId": "00000000-0000-4000-8000-000000000000",
  "url": "https://example.com/checkout",
  "referrer": "",
  "userAgent": "Mozilla/5.0 (example)"
}