Logging Pipeline
Introduction
Section titled “Introduction”The Logging Pipeline logs the execution of commands and queries handled through the Xeno.JS CQRS pipeline.
It records:
- when a request starts;
- when a request completes successfully;
- when a request returns a failed
Result; - when request execution throws an exception.
The pipeline uses the application logger, so you can configure where logs are written without changing your commands or handlers.
Before you start
Section titled “Before you start”You need:
- an
AppBuilderinstance; - a command or query handled through the Xeno.JS mediator;
- the CQRS pipeline enabled with
addPipeline().
You do not need to register the LoggingPipeline manually.
Enable logging
Section titled “Enable logging”Enable the CQRS pipeline through AppBuilder:
import { AppBuilder } from 'xeno-js'
const app = new AppBuilder() .addPipeline() .build()When addPipeline() is configured, Xeno.JS adds the logging pipeline together with the other common CQRS pipelines.
Both commands and queries use the logging pipeline.
Configure the logger
Section titled “Configure the logger”Use addLogger() to configure the logger used by the application.
For example, to configure the minimum log level:
import { AppBuilder } from 'xeno-js'import { LOG_LEVEL } from '@xeno-js/shared'
const app = new AppBuilder() .addLogger((config) => { config.level = LOG_LEVEL.INFO }) .addPipeline() .build()The available configuration includes:
| Option | Description |
|---|---|
level |
Minimum log level forwarded to the configured logging clients. |
console |
Enables or disables console logging. |
sentry.config |
Configures Sentry logging. |
pino.config |
Configures Pino logging. |
customLoggers |
Registers custom logging clients. |
The default AppBuilder configuration uses console logging and the DEBUG level.
Choose the log level
Section titled “Choose the log level”Xeno.JS filters messages below the configured minimum level.
For example:
import { AppBuilder } from 'xeno-js'import { LOG_LEVEL } from '@xeno-js/shared'
const app = new AppBuilder() .addLogger((config) => { config.level = LOG_LEVEL.WARN }) .addPipeline() .build()With LOG_LEVEL.WARN, informational messages from the Logging Pipeline are filtered out, while warnings and errors remain available to logging clients.
The Logging Pipeline itself uses:
infofor request start;infofor successful completion;errorfor failedResultvalues;errorfor thrown exceptions.
Configure console logging
Section titled “Configure console logging”Console logging is enabled by default.
You can configure it explicitly:
const app = new AppBuilder() .addLogger((config) => { config.console = true }) .addPipeline() .build()To disable the console logger:
const app = new AppBuilder() .addLogger((config) => { config.console = false }) .addPipeline() .build()Disabling the console logger does not disable the Logging Pipeline itself. The pipeline can still send logs to other configured logging clients.
Configure Pino
Section titled “Configure Pino”Xeno.JS can use Pino as a logging client.
Configure it through pino.config:
const app = new AppBuilder() .addLogger((config) => { config.pino.config = { destination: 'stdout', prettyPrint: true, env: 'development', } }) .addPipeline() .build()Write Pino logs to a file
Section titled “Write Pino logs to a file”Set destination to file and provide a file path:
const app = new AppBuilder() .addLogger((config) => { config.pino.config = { destination: 'file', filePath: 'logs/app.log', prettyPrint: false, env: 'production', } }) .addPipeline() .build()Supported destinations are:
stdout;file.
If filePath is omitted when using the file destination, Xeno.JS uses logs/app.log.
Pino logging also applies its own redaction for sensitive fields such as:
password;token;secret;authorization;headers.authorization.
Configure Sentry
Section titled “Configure Sentry”Xeno.JS can also send warning and error logs to Sentry.
Configure the Sentry DSN:
const app = new AppBuilder() .addLogger((config) => { config.sentry.config = { dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV, } }) .addPipeline() .build()The Sentry DSN is required when Sentry logging is enabled.
If environment is not provided, Xeno.JS uses NODE_ENV and falls back to development.
Sentry logging is primarily used for warnings and errors. Informational and debug messages are not sent as Sentry events.
Use multiple logging clients
Section titled “Use multiple logging clients”You can configure more than one logging destination.
For example, console and Pino can be enabled together:
const app = new AppBuilder() .addLogger((config) => { config.console = true
config.pino.config = { destination: 'stdout', prettyPrint: true, env: 'development', } }) .addPipeline() .build()The application logger forwards each accepted message to the configured logging clients.
Add a custom logger
Section titled “Add a custom logger”Use customLoggers when you need to integrate a logging destination that is not provided by Xeno.JS.
A custom logger must implement the ILoggerClient contract from @xeno-js/shared.
For example:
import type { ILoggerClient } from '@xeno-js/shared'
const customLogger = (): ILoggerClient => ({ track(level, message, context, error) { console.log({ level, message, context, error, }) },})Register it with addLogger():
const app = new AppBuilder() .addLogger((config) => { config.customLoggers = [ () => customLogger(), ] }) .addPipeline() .build()Custom logger factories receive the application service scope, so they can resolve application services when required.
What does the Logging Pipeline log?
Section titled “What does the Logging Pipeline log?”For a request such as:
{ type: 'COMMAND', intent: 'CREATE_USER'}the pipeline logs the start of the request:
Handling COMMAND CREATE_USERWhen the request succeeds:
Successfully handled COMMAND CREATE_USERWhen the handler returns a failed Result:
Failed to handle COMMAND CREATE_USER: User already existsWhen request execution throws:
Exception while handling COMMAND CREATE_USERThe failed Result is returned unchanged.
Thrown exceptions are logged and then rethrown.
Logging failed Results
Section titled “Logging failed Results”A command or query can complete without throwing but still return a failed Result.
For example:
const result = await mediator.send(command)
if (!result.isOk()) { // The Logging Pipeline has already logged the failure.}The Logging Pipeline does not replace or transform the Result.
It logs the error and returns the same result to the caller.
Logging thrown exceptions
Section titled “Logging thrown exceptions”If execution throws an exception:
try { await mediator.send(command)} catch (error) { // The Logging Pipeline has already logged the exception.}The pipeline logs the exception and rethrows it.
This means the Logging Pipeline does not act as the application’s exception handler. Use the Exception Pipeline when you need to configure exception handling behavior.
Complete example
Section titled “Complete example”A typical application can configure logging and the CQRS pipeline together:
import { AppBuilder } from 'xeno-js'import { LOG_LEVEL } from '@xeno-js/shared'
const app = new AppBuilder() .addLogger((config) => { config.level = LOG_LEVEL.INFO config.console = true
config.pino.config = { destination: 'stdout', prettyPrint: true, env: 'development', } }) .addPipeline() .build()After the application is built, commands and queries executed through the configured mediator are logged automatically.
Logging is not running
Section titled “Logging is not running”If commands or queries are not producing logs, check:
addPipeline()is called;- the command or query is executed through the Xeno.JS mediator;
- console logging has not been disabled with
config.console = false; - the configured minimum
config.leveldoes not filter the messages you expect; - at least one logging client is configured.
For example, if you configure:
config.level = LOG_LEVEL.WARNthe normal info messages generated by the Logging Pipeline will not be forwarded.
Pino logs are not written to the expected destination
Section titled “Pino logs are not written to the expected destination”Check the Pino configuration:
config.pino.config = { destination: 'file', filePath: 'logs/app.log',}For a file destination:
destinationmust befile;filePathshould point to the desired output file.
For standard output:
config.pino.config = { destination: 'stdout',}Sentry logging is failing during startup
Section titled “Sentry logging is failing during startup”If Sentry logging is enabled, verify that the configuration contains a DSN:
config.sentry.config = { dsn: process.env.SENTRY_DSN,}If the Sentry configuration is enabled without a DSN, application initialization fails with:
Sentry DSN is required when Sentry logging is enabled.Sentry Node Logger Documentation
See Loggers Documentation
Section titled “See Loggers Documentation”Related docs
Section titled “Related docs”- Application overview
- Exception Pipeline
- Performance Pipeline
- Creating Commands
- Creating Queries
- Creating Handlers
- Loggers Documentation
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
