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.
