Script Valley
Debugging: A Systematic Approach
Strategic Logging and ObservabilityLesson 4.2

Where to place logs to trace a bug through your system

log placement strategy, entry and exit logging, function boundaries, error path logging, correlation IDs

Log Placement Is a Design Decision

Random log placement creates noise. Strategic placement answers a specific question: given any bug report, can you trace the execution path through your system from the log output alone? If not, your logs are not useful.

The Entry-Exit Pattern

Log at the boundary of every significant operation: when it starts (with all relevant input context) and when it ends (with outcome -- success, error, or result summary). This gives you a complete trace of what happened and how long it took.

async function processOrder(orderId) {
  logger.info({ orderId }, 'processOrder: start');
  try {
    const order = await db.getOrder(orderId);
    logger.info({ orderId, itemCount: order.items.length }, 'processOrder: order fetched');
    const result = await chargeCard(order);
    logger.info({ orderId, chargeId: result.id }, 'processOrder: payment complete');
    return result;
  } catch (err) {
    logger.error({ orderId, err }, 'processOrder: failed');
    throw err;
  }
}

Correlation IDs

In systems with multiple services or parallel requests, logs from different operations interleave. A correlation ID -- a unique identifier assigned at the start of a request and passed through every log entry for that request -- lets you filter all logs for a single operation. Generate one UUID at the API entry point and thread it through every subsequent call. Without it, logs from concurrent users are indistinguishable.

Up next

How to use error tracking tools like Sentry to catch production bugs

Sign in to track progress