hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses Logging and Observability with @hectoday/http

Why Logging

  • console.log Is Not Enough
  • What to Log
  • Project Setup

Structured Logging

  • JSON Logs
  • Log Levels
  • Building a Logger

Request Logging

  • Request IDs
  • Request-Response Logging
  • Error Logging

Context and Correlation

  • Log Context
  • Child Loggers
  • Correlating Across Services

What to Observe

  • Business Event Logging
  • Performance Logging
  • Health and Metrics

Production

  • Log Output and Transport
  • Sensitive Data
  • Checklist and Capstone

Correlating Across Services

Beyond a single request

A request to POST /v2/orders does more than return a response. It might:

  1. Create the order (database INSERT)
  2. Charge the payment (external API call to Stripe)
  3. Enqueue a confirmation email (background job)
  4. Notify inventory (HTTP call to another service)

Each of these operations produces logs in different systems. Without correlation, you have four separate log streams that cannot be connected.

The correlation ID

The request ID becomes a correlation ID when it is passed beyond the original request — to background jobs, external API calls, and downstream services. Same ID, same purpose: trace a chain of operations back to the original trigger.

Passing to background jobs

// In the route handler
const log = c.locals.log;

log.info("enqueueing confirmation email");
enqueueJob("send_order_confirmation", {
  orderId: order.id,
  correlationId: c.locals.requestId, // Pass the request ID to the job
});

The job handler uses the correlation ID in its own logger:

// In the job handler
export async function handleSendConfirmation(payload: any) {
  const jobLog = logger.child({
    job: "send_order_confirmation",
    correlationId: payload.correlationId,
    orderId: payload.orderId,
  });

  jobLog.info("processing job");
  await sendEmail(payload.to, "Order Confirmed", body);
  jobLog.info("job completed");
}

Now the job’s logs include correlationId: "req_a1b2c3" — the same ID as the original HTTP request. Search for req_a1b2c3 and you see: the request, the order creation, the job enqueue, and the email send.

[!NOTE] The Background Jobs course stored job payloads as JSON. Adding correlationId to the payload is a simple extension that enables cross-system tracing.

Passing to external API calls

When calling external services, pass the correlation ID as a header:

async function callInventoryService(
  orderId: string,
  items: any[],
  correlationId: string,
  log: Logger,
) {
  log.info("notifying inventory service", { orderId, itemCount: items.length });

  const response = await fetch("https://inventory.internal/v1/reserve", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Request-Id": correlationId, // Pass correlation ID
    },
    body: JSON.stringify({ orderId, items }),
  });

  log.info("inventory service responded", { status: response.status });
  return response;
}

The inventory service receives X-Request-Id: req_a1b2c3, uses it in its own logs, and the entire chain is traceable.

Passing to downstream services

If your API calls another internal API, forward the request ID:

async function fetchUserProfile(userId: string, correlationId: string): Promise<UserProfile> {
  const response = await fetch(`https://users.internal/v1/users/${userId}`, {
    headers: { "X-Request-Id": correlationId },
  });
  return response.json();
}

The user service logs the same correlation ID. A single search finds logs across all services involved in the original request.

The full trace

Search for req_a1b2c3:

{"service":"book-catalog","requestId":"req_a1b2c3","message":"request started","method":"POST","path":"/v2/orders"}
{"service":"book-catalog","requestId":"req_a1b2c3","message":"order created","orderId":"order-99"}
{"service":"book-catalog","requestId":"req_a1b2c3","message":"enqueueing confirmation email","orderId":"order-99"}
{"service":"book-catalog","requestId":"req_a1b2c3","message":"request completed","status":201,"duration":145}
{"service":"email-worker","correlationId":"req_a1b2c3","message":"processing job","job":"send_order_confirmation"}
{"service":"email-worker","correlationId":"req_a1b2c3","message":"email sent","to":"[email protected]"}
{"service":"inventory","requestId":"req_a1b2c3","message":"stock reserved","orderId":"order-99","items":3}

Three services, one correlation ID, a complete picture of what happened.

Exercises

Exercise 1: Pass the request ID to a background job as correlationId. Log the job processing with the same ID. Search for the ID and find both the request and job logs.

Exercise 2: Pass X-Request-Id to an external service call. Verify the header is sent.

Exercise 3: Simulate a three-service trace. Log in each service with the same correlation ID. Search and verify the complete chain.

Why pass the request ID to background jobs as a correlation ID?

← Child Loggers Business Event Logging →

© 2026 hectoday. All rights reserved.