Making Wooster Talk: A Deep Dive into Structured Logging
PinoNodeJSBackendLoggingTypeScriptExpressWooster
The Problem with console.log
Let's be honest - we've all been there. Your Express backend starts misbehaving, so you do what any self-respecting developer would do:
Three months later, you're staring at production logs filled with "HERE1!!!" and wondering what past-you was thinking. It's like leaving passive-aggressive post-it notes for yourself, except less helpful.
Enter Pino: Structured Logging for Grown-ups
After the third time searching through logs for a "HERE!!!" that could have been anywhere, I decided it was time to be a responsible developer and implement proper logging. Enter Pino, because even Wooster needs to keep track of what he's been up to.
Environment-Aware Logging
First revelation: different environments need different logging approaches. Here's how I set up Wooster's logger:
Let's break this down:
Test Environment: Only log errors. Jest output is noisy enough without debug logs.
Development: Pretty, colorized logs that are actually readable by humans.
Production: Raw JSON logs - perfect for log aggregation tools.
Configurable Level: Can be overridden with LOG_LEVEL environment variable.
The Art of Log Levels
Pino gives us proper log levels, and choosing the right one is more art than science:
Request Context: The Missing Piece
The real power of structured logging comes with context. Here's the middleware I use to track requests:
Practical Examples: Real Logs from Wooster
Here's what my logs actually look like in different scenarios:
Development (using pino-pretty):
Production (JSON format):
Real-World Example: Database Service Logging
When I started building Wooster's destination service, I realized logging needs to tell a story. Here's how I approached it:
This creates a breadcrumb trail through the application:
Key patterns here:
Log at service boundaries (entering/exiting key functions)
Include relevant business context (location, destination data)
Use consistent message formats
Track operation progress through the stack
The result? When something goes wrong (and it will), you can trace the exact path through your application. Trust me, at 3 AM when your app is failing, you'll thank yourself for these breadcrumbs.
Logging Best Practices I've Learned
Be Consistent with Context
Always include requestId
Add userId when available
Include relevant business context (tripId, destinationId, etc.)
Log Level Selection
Error: Something is broken and needs immediate attention
Warn: Something unexpected that might need investigation
Info: Normal business events you want to track
Debug: Detailed information for development/troubleshooting
Security Considerations
Never log sensitive data (passwords, tokens, etc.)
Be careful with PII (personally identifiable information)
Consider GDPR implications of what you're logging
Performance Impact
Use appropriate log levels to control output
Consider log rotation in production
Be mindful of log size (especially with stack traces)
What I Actually Learned
Structured logging isn't just about pretty output - it's about being able to trace issues across requests
Environment-specific configuration is crucial
Context is king - the more relevant context you add, the easier debugging becomes
Good logging practices save hours of debugging time
Your future self will thank you (mine already has)
What's Next?
Now that I can properly track what's happening in my application, it's time to handle what happens when things go wrong. But that's a story for another post about error handling...
And once we've got error handling sorted, we'll look at how to get these lovely structured logs into a proper monitoring system - because as nice as JSON logs are, they're even better when you can query and visualize them. But one thing at a time...