080
LVL 03 — MID DEVELOPERSESSION 080DAY 80

STRUCTURED LOGGING

🎫 PIXELCRAFT-067
🐛Fix / ✨ Feature | 🟡 Medium | Priority: 🟠 High

The codebase is littered with console.log('here') and console.log(data). When something breaks in production, it's impossible to trace. Replace all console.log with a structured logger. Every log must have context.
CONCEPTS.UNLOCKED
📋
Structured Logging
Logs as JSON, not strings. Instead of "User 42 uploaded image" → { level: "info", userId: 42, action: "upload", timestamp: "..." }. Machine-readable, searchable, parseable.
🚦
Log Levels
DEBUG → INFO → WARN → ERROR → FATAL. In development, show everything. In production, show WARN and above. Filter by severity to find what matters fast.
🔧
Winston & Pino
Production-grade Node.js loggers. Winston: flexible, widely used. Pino: blazing fast (5× faster), JSON-native. Both support transports: file, console, external services.
🔗
Request Tracing
correlationId ties logs to a single request. Generate a UUID per request, attach to every log within that request. When debugging, filter by correlationId to see the full request lifecycle.
🛡️
Centralized Error Handling
One place to catch all errors. Express error middleware catches everything. Log the error with full context (userId, route, requestId, stack trace), then send a clean response to the client.
👀
Log Viewer
Internal dashboard for browsing logs. Read JSON log files, parse them, filter by level/userId/action, search by keyword. Your own mini Datadog — built from scratch.
HANDS-ON.TASKS
01
Set Up Pino Logger
npm install pino pino-pretty // logger.js const pino = require('pino'); const logger = pino({ level: process.env.LOG_LEVEL || 'info', transport: process.env.NODE_ENV === 'development' ? { target: 'pino-pretty', options: { colorize: true, translateTime: 'HH:MM:ss', } } : undefined, // JSON in production }); module.exports = logger;
02
Request Tracing Middleware
const { randomUUID } = require('crypto'); const logger = require('./logger'); function requestLogger(req, res, next) { // Generate unique request ID req.requestId = randomUUID(); req.startTime = Date.now(); // Create child logger with context req.log = logger.child({ requestId: req.requestId, method: req.method, path: req.path, userId: req.userId || 'anonymous', }); req.log.info('Request started'); // Log when response finishes res.on('finish', () => { const duration = Date.now() - req.startTime; req.log.info({ statusCode: res.statusCode, duration: `${duration}ms`, }, 'Request completed'); }); next(); } app.use(requestLogger);
pino.child() creates a logger that automatically includes the parent's context in every log. Every log from this request will include the requestId, method, path, and userId.
03
Centralized Error Handler
// Must be LAST middleware app.use((err, req, res, next) => { const statusCode = err.statusCode || 500; // Log error with full context req.log.error({ error: err.message, stack: err.stack, statusCode, }, 'Unhandled error'); // Clean response to client res.status(statusCode).json({ error: statusCode === 500 ? 'Internal server error' : err.message, requestId: req.requestId, // ^ user can reference this // when contacting support }); });
04
Replace console.log Everywhere
// ❌ BEFORE (useless in production) console.log('upload started'); console.log(user); console.log('error:', err); // ✅ AFTER (searchable, traceable) req.log.info({ action: 'upload_started', fileSize: req.file.size, mimeType: req.file.mimetype, }, 'Image upload initiated'); req.log.error({ action: 'upload_failed', error: err.message, }, 'Image upload failed');
05
Build Internal Log Viewer

Build a React page at /admin/logs: read JSON log files from the server, parse each line, display in a table with columns: timestamp, level, userId, action, message. Add filters for log level and search by requestId.

06
Close the Ticket
git switch -c feature/PIXELCRAFT-067-structured-logging git add server/ src/ git commit -m "Replace console.log with Pino structured logging (PIXELCRAFT-067)" git push origin feature/PIXELCRAFT-067-structured-logging # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

Observability is how you understand production systems.

Logging is one of three "pillars of observability" — the tools that let you understand what your system is doing without stopping it.

// The three pillars:

Logs
  What happened (discrete events)
  "User 42 uploaded image at 14:32"

Metrics
  How much is happening (aggregates)
  "95th percentile latency: 120ms"

Traces
  How requests flow through services
  "API → Queue → Worker → DB → S3"

// Tools at scale:
ELK Stack (Elasticsearch + Logstash
  + Kibana) — self-hosted log search
Datadog  — managed observability
Grafana  — dashboards + alerts

// "If you can't measure it,
// you can't improve it." — Kelvin
"Observability Lab"
[A]Add log rotation: pipe Pino output to a file, rotate daily (pino-roll or logrotate). Keep last 7 days of logs. Compress old log files with gzip to save disk space.
[B]Add performance metrics: log response time for every request. Build an /admin/metrics endpoint that shows average, P50, P95, and P99 latency over the last hour.
[C]Research: what is distributed tracing? How does OpenTelemetry work? When your app becomes microservices, how do you trace a single request across 5 services? Write a brief analysis.
REF.MATERIAL
ARTICLE
Pino Team
Official Pino documentation: transports, child loggers, redaction, serializers, and benchmarks. 5× faster than Winston in production.
PINOOFFICIALESSENTIAL
VIDEO
Hussein Nasser
Why structured logging matters in production. JSON vs plaintext, log aggregation, and how companies debug at scale.
LOGGINGARCHITECTURE
ARTICLE
OpenTelemetry
The standard for distributed tracing: spans, traces, and context propagation. The future of observability across microservices.
TRACINGOFFICIAL
ARTICLE
Adam Wiggins
Treat logs as event streams. Don't write to files — write to stdout and let the platform handle routing. The cloud-native logging philosophy.
12-FACTORARCHITECTURE
VIDEO
Fireship
Quick tour of Node.js logging: Winston vs Pino, log levels, transports, and production patterns. Practical and concise.
LOGGINGQUICK
// LEAVE EXCITED BECAUSE
Every request is now traceable from start to finish. When something breaks, you filter by requestId and see exactly what happened. No more console.log('here') — you have production-grade observability.