Topic 18 of 55
Structured Logging with Pino
Overview
Structured logging (JSON format) enables log aggregation, searching, and alerting at scale. Pino is the fastest Node.js logger — it outputs structured JSON that integrates with ELK, Datadog, and CloudWatch.
Syntax
javascript
import pino from "pino";
const logger = pino({
level: process.env.LOG_LEVEL ?? "info",
transport: process.env.NODE_ENV !== "production"
? { target: "pino-pretty" }
: undefined,
});
// Log levels
logger.info({ userId, action: "login" }, "User logged in");
logger.warn({ requestId }, "Slow response detected");
logger.error({ err, userId }, "Payment failed");
logger.fatal("Database unreachable");
// Child logger with context
const requestLogger = logger.child({ requestId: "abc-123" });
requestLogger.info("Processing order");
// Express request logging middleware
app.use((req, res, next) => {
req.log = logger.child({ requestId: crypto.randomUUID() });
req.log.info({ method: req.method, url: req.url }, "Request received");
next();
});Common Pitfalls
- Never log passwords, tokens, credit card numbers, or PII — add a redact config to Pino to automatically remove sensitive fields.
- console.log in production is slow and unstructured — always use a structured logger in production Node.js apps.
- Interview tip: Structured JSON logs enable powerful queries: filter by userId, correlate by requestId, and alert on error rates.
Real-World Example
Structured logging for an order processing service
example
javascript
const logger = pino({ name: "order-service" });
async function processOrder(orderId, userId) {
const log = logger.child({ orderId, userId });
log.info("Starting order processing");
try {
const order = await db.orders.findById(orderId);
log.debug({ items: order.items.length }, "Order retrieved");
const startTime = Date.now();
const payment = await paymentGateway.charge({ amount: order.total });
log.info(
{ paymentId: payment.id, durationMs: Date.now() - startTime },
"Payment successful"
);
await db.orders.update(orderId, { status: "paid" });
log.info("Order processing complete");
return { success: true };
} catch (err) {
log.error({ err }, "Order processing failed");
await db.orders.update(orderId, { status: "failed" });
throw err;
}
}