Good logging enables debugging, monitoring, and auditing. This guide covers logging strategies for production applications.
Structured Logging#
Use JSON for machine-readable logs:
1import pino from 'pino';
2
3const logger = pino({
4 level: process.env.LOG_LEVEL || 'info',
5 base: {
6 service: 'api-server',
7 version: process.env.APP_VERSION,
8 },
9});
10
11// Usage
12logger.info({ userId: '123', action: 'login' }, 'User logged in');Log Levels#
1// FATAL: System is unusable
2logger.fatal({ error }, 'Database connection lost');
3
4// ERROR: Error conditions
5logger.error({ error, orderId }, 'Payment processing failed');
6
7// WARN: Warning conditions
8logger.warn({ queueSize }, 'Queue size exceeds threshold');
9
10// INFO: Normal operations
11logger.info({ userId }, 'User created account');
12
13// DEBUG: Detailed debugging
14logger.debug({ request }, 'Incoming request');
15
16// TRACE: Very detailed tracing
17logger.trace({ sql }, 'Executing query');Request Context#
1import { AsyncLocalStorage } from 'async_hooks';
2
3const requestContext = new AsyncLocalStorage<{ requestId: string }>();
4
5app.use((req, res, next) => {
6 const requestId = req.headers['x-request-id'] || crypto.randomUUID();
7 requestContext.run({ requestId }, next);
8});
9
10function getLogger() {
11 const context = requestContext.getStore();
12 return logger.child({ requestId: context?.requestId });
13}What to Log#
- Request/response metadata (not sensitive data)
- Error details with stack traces
- Business events (user actions, transactions)
- Performance metrics
- Security events (auth failures, permission denied)
What NOT to Log#
- Passwords and secrets
- Credit card numbers
- Personal health information
- Full request bodies with sensitive data
Log enough to debug issues, but protect user privacy.