Overview
Use logger from @/lib/logger for all server-side logging. Never use console.log. The logger provides structured output, automatic redaction, and log levels.
What it is
A Pino-based structured logger with automatic sensitive data redaction and environment-aware log levels.
Why we use it
Structured logs are searchable, redaction prevents leaking secrets, and log levels reduce noise in production.
When to use
Errors, authentication events, external API calls, database errors. Avoid logging success paths or in tight loops.
Key Features
- Structured JSON logging for parsing
- Automatic redaction of passwords, tokens, keys
- Log levels (error, warn, info, debug)
- Child loggers with inherited context
Quick Start
Basic Logging
Using the logger in an API route.
// Using logger in an API route
import { logger } from '@/lib/logger';
export async function POST(request: Request) {
logger.info('Processing request', { endpoint: '/api/users' });
try {
const user = await createUser(data);
logger.info('User created', { userId: user.id });
return NextResponse.json({ ok: true, data: user });
} catch (error) {
logger.error('Failed to create user', { error, data });
return handleError(error);
}
}Patterns
Log Levels
When to use each log level.
// Log levels and when to use them
import { logger } from '@/lib/logger';
// ERROR: Application errors that need attention
logger.error('Database connection failed', { host, error });
// WARN: Unexpected but recoverable situations
logger.warn('Rate limit approaching', { userId, remaining: 5 });
// INFO: Notable events in normal operation
logger.info('User logged in', { userId, method: 'oauth' });
// DEBUG: Detailed info for troubleshooting (dev only)
logger.debug('Query executed', { sql, duration: 45 });Adding Context
Including structured data in logs.
// Adding structured context
logger.info('Payment processed', {
userId: user.id,
amount: 99.99,
currency: 'USD',
paymentMethod: 'stripe',
transactionId: 'tx_123',
});
// Output (JSON in production):
{
"level": "info",
"message": "Payment processed",
"userId": "usr_abc",
"amount": 99.99,
"currency": "USD",
...
}Child Loggers
Creating loggers with inherited context.
// Child loggers inherit context
const requestLogger = logger.child({
requestId: crypto.randomUUID(),
userId: auth.user.id,
});
// All logs from this logger include requestId and userId
requestLogger.info('Starting operation');
requestLogger.info('Step 1 complete');
requestLogger.info('Operation finished');Watch Out
Using console.log instead of logger
Don't
// Using console.log
console.log('User created:', user);
console.error('Something failed');Do
// Using structured logger
logger.info('User created', { userId: user.id });
logger.error('Operation failed', { error, context });Logging passwords, tokens, or personal data
Don't
// Logging sensitive data
logger.info('User login', {
email: user.email,
password: password, // NEVER!
token: authToken, // NEVER!
});Do
// Let automatic redaction work
logger.info('User login', {
userId: user.id,
method: 'password',
});
// Passwords, tokens, keys are auto-redacted- Logging every successful request
- Logging inside loops or hot paths