Watch out! You're reading a series of articles
- Logging Best Practices Series: What to log?
Ready to transform your logs from a wall of gibberish into your most valuable debugging ally?
- (currently reading) Logging Best Practices Series: Logging levels
The Good, The Bad, and The "Why Is This in Production?"
- Logging Best Practices Series: Logging formats
From Chaos to Structure
- Logging Best Practices Series: Contextual logging
Ever tried to debug an issue where a request passes through five different services, ten different methods, and you have no idea which logs belong to which request?
Logging Levels: The Good, The Bad, and The "Why Is This in Production?"
Production was down, and I was staring at logs that looked like this:
console.log("Starting process...")
console.log("Done!")
Somewhere between "Starting" and "Done", our payment processing system had failed, affecting thousands of users, and I had absolutely no idea why. That's when I learned the hard way about the importance of proper logging levels.
The Five Levels of Logging Enlightenment
Think of log levels like spice levels at a Thai restaurant - you need the right amount for the right situation. Let's break them down with some real TypeScript examples:
1. DEBUG: The "I'm Just Curious" Level
class PaymentProcessor {
async processPayment(payment: Payment) {
logger.debug(`Starting payment processing for ID: ${payment.id}`, {
amount: payment.amount,
currency: payment.currency,
timestamp: new Date().toISOString()
});
// Processing logic here...
logger.debug('Payment validation steps completed', {
validationResults: results,
processingTime: elapsed
});
}
}
Use DEBUG when you're in detective mode. It's like those behind-the-scenes DVD extras - interesting during development, but you don't need them in the final cut.
2. INFO: The "Everything's Fine" Level
class UserAuthService {
async login(username: string) {
logger.info('User login successful', {
user: username,
loginTime: new Date().toISOString(),
ipAddress: request.ip
});
}
}
INFO is for tracking normal operations. It's like those "Your package has been delivered" notifications - useful to know, but not something to wake you up at night for.
3. WARNING: The "Hmm, That's Weird" Level
class DatabaseConnection {
async query(sql: string) {
try {
const result = await this.execute(sql);
if (this.connectionPool.available < 3) {
logger.warn('Database connection pool running low', {
availableConnections: this.connectionPool.available,
totalConnections: this.connectionPool.total,
sql: sql.substring(0, 100) // Don't log entire queries!
});
}
return result;
} catch (error) {
// More severe error handling...
}
}
}
WARN is for those "Check engine" light moments - something's not quite right, but your car hasn't exploded... yet.
4. ERROR: The "Houston, We Have a Problem" Level
class PaymentGateway {
async processTransaction(transaction: Transaction) {
try {
return await this.gateway.charge(transaction);
} catch (error) {
logger.error('Payment processing failed', {
transactionId: transaction.id,
error: error.message,
errorCode: error.code,
stackTrace: error.stack,
// Never log full card details!
lastFourDigits: transaction.card.lastFour
});
throw error;
}
}
}
ERROR is for when things actually break. Like when your code tries to divide by zero, or when that "definitely tested" feature meets real users.
5. CRITICAL: The "Wake Everyone Up" Level
class SystemMonitor {
checkDiskSpace() {
const freeSpace = this.getDiskSpace();
if (freeSpace.percentage < 1) {
logger.critical('SYSTEM CRITICAL: Disk space nearly exhausted', {
freeSpacePercent: freeSpace.percentage,
freeSpaceBytes: freeSpace.bytes,
timestamp: new Date().toISOString(),
affectedServices: this.getAffectedServices()
});
// Initiate emergency cleanup or notification
}
}
}
CRITICAL is for those "wake up the CTO" moments. Like when your disk is full, your database is down, or someone deployed straight to production on a Friday afternoon.
Real Talk: When to Use What
Here's my practical guide after years of 3 AM debugging sessions:
DEBUG: Use it liberally in development, but sparingly in production. It's like developer commentary - fascinating for you, boring for everyone else.
INFO: Perfect for tracking business events and flow. Want to know how many users logged in today? INFO is your friend.
WARNING: Use for "soft" errors and potential issues. Like when that API you're calling is getting a bit slow, or when someone tries to log in with "password123".
ERROR: For all those "this shouldn't happen" moments that, inevitably, do happen. If you need to fix something, it should be an ERROR.
CRITICAL: Reserve this for the real disasters. If you're logging more than a few CRITICAL events per month, you're either being too dramatic or you have bigger problems to solve.
The Golden Rules of Log Levels
Be Consistent: If a failed payment is an ERROR, it should always be an ERROR. Don't play log level roulette.
Context is King: Don't just log "Error occurred" - include enough context to understand what happened without having to reproduce the issue.
Think About Scale: What's useful when debugging locally might be overwhelming in production. Use log levels to control the noise.
Remember that one time I mentioned the 2 AM debugging session? Well, after implementing proper log levels, a similar issue occurred, but this time our logs told us exactly what went wrong:
[2024-01-15T02:00:01.123Z] ERROR Payment processing failed {
"transactionId": "tx_123",
"errorCode": "GATEWAY_TIMEOUT",
"retryCount": 3,
"gatewayResponse": "Connection refused"
}
Five minutes later, we had identified the issue (a gateway timeout), fixed it (increased the timeout threshold), and I was back in bed. That's the power of proper log levels.
Conclusion
Logging levels aren't just about categorizing messages - they're about telling a story that helps you understand what your application is doing, especially when things go wrong. Use them wisely, and your future self (probably at 2 AM) will thank you.
Watch out! You're reading a series of articles
- Logging Best Practices Series: What to log?
Ready to transform your logs from a wall of gibberish into your most valuable debugging ally?
- (currently reading) Logging Best Practices Series: Logging levels
The Good, The Bad, and The "Why Is This in Production?"
- Logging Best Practices Series: Logging formats
From Chaos to Structure
- Logging Best Practices Series: Contextual logging
Ever tried to debug an issue where a request passes through five different services, ten different methods, and you have no idea which logs belong to which request?