Course contentsShow
← Node.js
Lesson 1403 of 1,98013. Error Handling and Resilient SystemsFree lesson

Configuring Logging Contextualization

You will inject request-specific IDs into error logs to allow for distributed tracing across asynchronous boundaries.

Configuring Logging Contextualization

In this lesson, you will learn how to inject unique request identifiers into your logs to maintain traceability as requests travel across asynchronous boundaries.

When your application handles multiple concurrent requests, standard logs can quickly become a jumbled mess. If an error occurs in an asynchronous function (like a database query or a microservice call), you often lose the context of which user triggered the action. To solve this, we use Logging Contextualization to "tag" every log line with a specific Request ID.

Using AsyncLocalStorage

The most effective way to manage this in Node.js is the node:async_hooks module, specifically AsyncLocalStorage. This allows you to store data that persists throughout the lifetime of a single asynchronous execution chain without manually passing a "correlation ID" argument to every single function.

Here is how you implement it:

const { AsyncLocalStorage } = require('node:async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();

// Middleware to set the ID per request
function requestContextMiddleware(req, res, next) {
  const requestId = req.headers['x-request-id'] || crypto.randomUUID();
  asyncLocalStorage.run({ requestId }, () => {
    next();
  });
}

// Logging helper that automatically pulls the ID
function logger(message) {
  const store = asyncLocalStorage.getStore();
  const id = store?.requestId || 'no-id';
  console.log(`[${id}] ${message}`);
}

By wrapping your request handler in asyncLocalStorage.run(), any code executed within that block—including nested callbacks, promises, or await statements—can access the current requestId via getStore().

When an error occurs, you simply call logger('Database connection failed'). Because the logger function implicitly knows the context, your logs will look like [abc-123] Database connection failed and [xyz-789] Database connection failed simultaneously. This allows you to filter your logs in systems like ELK or Datadog to see the exact sequence of events for a single failing request, even if hundreds of others are processing at the same time.

Key Takeaway: By using AsyncLocalStorage to inject request identifiers into your logs, you transform scattered error reports into cohesive, traceable execution paths.