Sentry Node Logger
Introduction
Section titled “Introduction”Use Sentry Node when you want Xeno.JS logging to report warnings and errors to Sentry while keeping the application code independent from the Sentry SDK.
Xeno.JS integrates @sentry/node as a logging provider. You configure it through AppBuilder.addLogger(), while application code continues to use the Xeno.JS ILogger abstraction.
Prerequisites
Section titled “Prerequisites”Install the Xeno.JS packages used by your application and the Sentry Node SDK:
npm install @xeno-js/core @xeno-js/shared @sentry/nodeXeno.JS loads the Sentry integration when Sentry logging is configured.
You do not need to call Sentry.init() yourself. Xeno.JS initializes Sentry from the configuration passed to addLogger().
Configure Sentry
Section titled “Configure Sentry”Configure Sentry through AppBuilder.addLogger():
import { AppBuilder } from '@xeno-js/core'
const app = new AppBuilder()
app.addLogger((options, config) => { options.sentry.config = { dsn: config.getOrThrow('SENTRY_DSN'), environment: config.get('NODE_ENV', 'development'), }})The Sentry configuration exposes:
| Option | Required | Description |
|---|---|---|
dsn |
Yes | Sentry Data Source Name used to send events to the configured Sentry project. |
environment |
No | Environment associated with the Sentry events. |
If environment is not provided, Xeno.JS uses process.env.NODE_ENV. If NODE_ENV is also undefined, it uses development.
A DSN is required when Sentry logging is enabled.
Configure the log level
Section titled “Configure the log level”The application log level is configured through the same LoggerConfig used by the other Xeno.JS providers:
import { AppBuilder, LOG_LEVEL } from '@xeno-js/core'
const app = new AppBuilder()
app.addLogger((options, config) => { options.level = LOG_LEVEL.INFO
options.sentry.config = { dsn: config.getOrThrow('SENTRY_DSN'), environment: config.get('NODE_ENV', 'development'), }})Xeno.JS supports these log levels:
DEBUGINFOWARNERROR
The configured level is the minimum level accepted by the application logger.
Sentry has an additional provider-specific rule: only WARN and ERROR messages are sent to Sentry. DEBUG and INFO messages are not converted into Sentry events.
For example, with:
options.level = LOG_LEVEL.ERRORonly errors can reach Sentry.
With:
options.level = LOG_LEVEL.INFOthe application logger accepts INFO, WARN, and ERROR, but Sentry still receives only WARN and ERROR.
Build the application
Section titled “Build the application”The logger is registered during application bootstrap:
const container = await app.build()Configure Sentry before calling build().
A complete minimal setup is:
import { AppBuilder } from '@xeno-js/core'
const app = new AppBuilder()
app.addLogger((options, config) => { options.sentry.config = { dsn: config.getOrThrow('SENTRY_DSN'), environment: config.get('NODE_ENV', 'development'), }})
const container = await app.build()Resolve the application logger
Section titled “Resolve the application logger”Xeno.JS does not expose a separate DI token for the Sentry logger.
Resolve the application logger through TOKENS.LOGGER:
import { TOKENS } from '@xeno-js/core'
const logger = container.resolve(TOKENS.LOGGER)The resolved object is the Xeno.JS ILogger abstraction.
This is important when multiple providers are configured. For example:
ILogger ├── Console ├── Pino ├── Sentry └── Custom loggerYour application calls the same logger regardless of which providers are enabled.
Write logs
Section titled “Write logs”Use the Xeno.JS logger methods:
logger.debug('Debug information')logger.info('User created')logger.warn('User profile is incomplete')logger.error('Failed to create user', error)The public error signature accepts the message first and the error as the second argument:
logger.error('Failed to create user', error)You do not need to import or call the Sentry SDK from application code.
Example in an application service
Section titled “Example in an application service”import { TOKENS } from '@xeno-js/core'import type { ILogger } from '@xeno-js/core'
export class UserService { constructor(private readonly logger: ILogger) {}
async createUser(): Promise<void> { try { // Application work... this.logger.info('User created') } catch (error) { this.logger.error('Failed to create user', error) //Sentry recieves only the error's log throw error } }}When Sentry is enabled, the same logger.error() call is forwarded to Sentry according to the Xeno.JS Sentry provider rules.
Warning behavior
Section titled “Warning behavior”Sentry receives WARN messages.
When a warning does not include an error object:
logger.warn('User profile is incomplete')Xeno.JS calls Sentry’s message capture mechanism with warning severity.
Conceptually, the resulting Sentry event is a warning message rather than an exception.
If a warning is associated with an error object, Xeno.JS captures the error as an exception:
logger.warn('User profile validation failed', error)The provider therefore distinguishes between:
WARN + no error ↓Sentry message with warning severity
WARN + error ↓Sentry exceptionError behavior
Section titled “Error behavior”Errors are sent to Sentry only when the configured application log level allows them.
When an error includes an error object:
logger.error('Database operation failed', error)Xeno.JS uses Sentry’s exception capture mechanism.
When no error object is supplied:
logger.error('Database operation failed')the Sentry provider uses Sentry’s message capture path with warning severity.
Therefore, when reporting an actual exception, pass the Error object:
try { await repository.save(user)} catch (error) { logger.error('Failed to save user', error) throw error}This preserves the exception for Sentry rather than reducing the event to a message.
Zod errors
Section titled “Zod errors”Xeno.JS filters out events whose original exception is a ZodError.
This is done before the event is sent to Sentry.
As a result, validation failures represented by ZodError are not reported as Sentry events through this integration.
This behavior is specific to the Xeno.JS Sentry integration and should not be interpreted as a general Sentry SDK behavior.
Safe context
Section titled “Safe context”Xeno.JS does not send the complete request context to logging providers.
The application logger builds a restricted safe context for observability. This context is intended to provide useful diagnostic information without automatically serializing arbitrary request/application state.
The safe context can include information such as:
userIdtenantIdrequestId- request
path - request
origin - masked client IP
correlationIdspanIdstartTimeparentSpanId- applicable messaging context
The full request context can contain additional information that is not appropriate for automatic logging, such as authentication details, cookies, CSRF values, user profile information, or transport-specific objects.
For Sentry, Xeno.JS attaches the available safe context to the Sentry scope as event extras.
Safe context is not a security boundary for application messages
Section titled “Safe context is not a security boundary for application messages”Safe context does not prevent application code from explicitly sending sensitive information to a logger.
Avoid code such as:
logger.info(`User password: ${password}`)or:
logger.error('Authentication failed', { token, authorizationHeader,})Do not explicitly log:
- passwords;
- access tokens;
- refresh tokens;
- API keys;
- authorization credentials;
- session cookies;
- secrets;
- sensitive personal data that is not required for diagnostics.
The safe-context mechanism limits automatically propagated context. It cannot remove sensitive information that application code explicitly places in a log message or error.
What reaches Sentry?
Section titled “What reaches Sentry?”For a typical Xeno.JS log:
Application code │ ▼ ILogger │ ├── minimum log-level check │ ▼ Sentry provider │ ├── DEBUG → ignored ├── INFO → ignored ├── WARN → Sentry message or exception └── ERROR → Sentry exception/messageThe Sentry provider adds the available safe context as event extras.
Sentry itself then stores and presents the resulting event according to the Sentry project configuration.
Configure Sentry together with other providers
Section titled “Configure Sentry together with other providers”Sentry does not have to be the only logging provider.
For example, you can enable Console, Pino, and Sentry together:
import { AppBuilder } from '@xeno-js/core'
const app = new AppBuilder()
app.addLogger((options, config) => { options.sentry.config = { dsn: config.getOrThrow('SENTRY_DSN'), environment: config.get('NODE_ENV', 'development'), }
options.pino.config = { destination: 'stdout', }})
await app.build()The same application log can then be processed by the configured providers.
For example:
logger.error('Failed to process payment', error) │ ├── Console ├── Pino └── SentryEach provider handles the log according to its own integration rules.
Sentry initialization performed by Xeno.JS
Section titled “Sentry initialization performed by Xeno.JS”When Sentry is configured, Xeno.JS initializes @sentry/node with:
- the configured DSN;
- the configured environment;
- HTTP integration;
- a traces sample rate of
1.0outside production; - a traces sample rate of
0.1in production.
Xeno.JS also installs an event filter that discards events whose original exception is a ZodError.
These settings are part of the Xeno.JS integration. You should therefore configure the integration through addLogger() rather than separately initializing Sentry for the same application logger.
Complete example
Section titled “Complete example”A typical application can configure Sentry and keep application logging independent from Sentry:
import { AppBuilder, LOG_LEVEL, TOKENS } from '@xeno-js/core'
async function bootstrap() { const app = new AppBuilder()
app.addLogger((options, config) => { options.level = LOG_LEVEL.INFO
options.sentry.config = { dsn: config.getOrThrow('SENTRY_DSN'), environment: config.get('NODE_ENV', 'development'), } })
const container = await app.build()
const logger = container.resolve(TOKENS.LOGGER)
logger.info('Application started')
try { throw new Error('Example failure') } catch (error) { logger.error('Application operation failed', error) }}
await bootstrap()The application knows only about ILogger. It does not need to depend on the Sentry API to report the error.
Troubleshooting
Section titled “Troubleshooting”Sentry does not receive any events
Section titled “Sentry does not receive any events”Check the following:
app.addLogger()is called.options.sentry.configis configured.- A valid
dsnis provided. app.build()is called after the logger configuration.- The log level allows the event.
- The event is
WARNorERROR. - The exception is not a
ZodError.
For example:
app.addLogger((options, config) => { options.level = LOG_LEVEL.WARN
options.sentry.config = { dsn: config.getOrThrow('SENTRY_DSN'), }})Bootstrap fails with a Sentry configuration error
Section titled “Bootstrap fails with a Sentry configuration error”If Sentry logging is enabled without a Sentry configuration, Xeno.JS throws:
Sentry configuration is required when Sentry logging is enabled.If the configuration exists but has no DSN, Xeno.JS throws:
Sentry DSN is required when Sentry logging is enabled.Check:
options.sentry.config = { dsn: process.env.SENTRY_DSN,}and verify that SENTRY_DSN is actually defined.
INFO logs do not appear in Sentry
Section titled “INFO logs do not appear in Sentry”This is expected.
The Sentry provider only forwards WARN and ERROR levels.
For example:
logger.info('User created')is not sent to Sentry.
Use an appropriate warning or error when the event should be reported to Sentry:
logger.warn('User profile requires attention')or:
logger.error('User creation failed', error)An error appears as a message instead of an exception
Section titled “An error appears as a message instead of an exception”Pass the actual error object to logger.error():
logger.error('Failed to process request', error)instead of only logging a string:
logger.error('Failed to process request')The Sentry integration uses captureException() when an error object is provided.
Sensitive information appears in Sentry
Section titled “Sensitive information appears in Sentry”The safe context only controls automatically propagated contextual information.
Inspect the message, error, and application data passed to the logger directly.
Remove secrets, credentials, tokens, passwords, and unnecessary sensitive information from application-level log calls.
Related documentation
Section titled “Related documentation”- Observability Overview
- Pino Logger
- Dependency Injection — Resolution
- Fundamentals — Context & Scopes
- Application — Pipelines
Support Us
Section titled “Support Us”Xeno.JS is an MIT-licensed open source project. It can grow thanks to the support of these awesome people. If you’d like to join them, please read more at support section
