Logging methods
Log levels, structured data, context, and capturing errors by hand.
Log levels
| Level | Use it for |
|---|---|
trace | Very detailed steps you only want while chasing a problem. |
debug | Detail that helps while developing. |
info | Normal events: a sign-up, an order placed. |
warn | Something unexpected that the app recovered from. |
error | Something failed. Becomes part of an issue. |
fatal | Something 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
| Method | Arguments |
|---|---|
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:
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.
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
captureExceptionlogs the error aterrorlevel with the messageException captured. The issue is still titled from the error itself.captureMessagelogs a message at the level you choose (defaultinfo).- The optional
contextis added to the logger’s context for this one log only.
import { LogLevel } from 'apperio';
logger.captureException(new Error('Stock check failed'), { sku: 'sku-42' });
logger.captureMessage('Payment provider slow', LogLevel.WARN);addBreadcrumb
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:
logger.setContext({ userId: 'user-42', plan: 'pro' }); // merged into what is there
logger.getContext(); // a copy of the current context
logger.clearContext(); // removes everythingWhen 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):
{
"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)"
}