Skip to content

Logger Middleware

The Logger Middleware is built-in middleware that gives you capability to add observability to your application by logging:

  • the intent (query, command or event) before and once handled (or errored)
  • the result(s) of the handler(s)
  • the Stamps

How to use it

As for any Middleware, you can use it by adding it to the bus instance.

const queryBus = createQueryBus<QueryHandlerRegistry>();
queryBus.useLoggerMiddleware({logger, adapter});

Remember built-in middlewares are intent aware, therefore you can customize the behavior per intent using the key intents.

Of course, the key is in the adapter that you can provide to the createLoggerMiddleware function. This adapter must respect the LoggerAdapter interface.

Logger must respect LoggerInterface which is log(...args: unknow[]): void and error(...args: unknow[]): void

Here is an example:

export const adapter: LoggerAdapter = {
processing: (identity, message, results, stamps) =>
logger.log(
`[Envelope<${identity?.body?.id}>](Processing)`,
JSON.stringify({
message,
results,
stamps,
}),
),
processed: (identity, message, results, stamps) => {
const timings = stamps.filter((stamp) => stamp.type === 'missive:timings')?.[0] as TimingsStamp | undefined;
logger.log(
`[Envelope<${identity?.body?.id}>](Processed${timings?.body?.total ? ` in ${(timings.body.total / 1000000).toFixed(4)} ms` : ''})`,
JSON.stringify({
message,
results,
stamps,
}),
);
},
error: (identity, message, results, stamps) => {
const timings = stamps.filter((stamp) => stamp.type === 'missive:timings')?.[0] as TimingsStamp | undefined;
logger.error(
`[Envelope<${identity?.body?.id}>](Errored${timings?.body?.total ? ` in ${(timings.body.total / 1000000).toFixed(4)} ms` : ''}`,
JSON.stringify({
message,
results,
stamps,
}),
)
}
};

Explanation

Internally, the Logger Middleware is going to call that adapter 2 times:

  • processing: when the message is received
  • processed: when the message is handled
  • error: when the message is in error

The parameters for each of those functions are:

  • identity: A Stamp that contains the id of the Envelope
  • message: The intent that has been dispatched
  • results: The result(s) of the handler(s)
  • stamps: The rest of the Stamps. (yes Identify and Results are extracted stamps from the Envelope)

Added Stamps

The Logger Middleware is going to add:

  • type TimingsStamp = Stamp<{ total: number }, 'missive:timings'>

    When the message is handled or errored with the total time elapsed in nanoseconds.

Going further

Look at the code of the Logger Middleware


Missive.js. MIT License.
Powered by Astro Starlight.
Inspired by Symfony Messenger