Skip to main content

Command Palette

Search for a command to run...

Winston Logger Node.js: Production Logging Library

Published
6 min read
T

Welcome to TopperBlog! 👋

I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.

🎯 What I Write About: • AI/ML Engineering & LLMs • Web3 & Blockchain Development
• System Design & Architecture • Interview Preparation (FAANG) • Freelancing & Remote Work • Modern Tech Stacks (Next.js, React, Rust, TypeScript) • Performance Optimization & Best Practices

💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.

📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.

🌐 Let's connect and grow together in this amazing tech journey!

#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering

Winston Logger Node.js: Production Logging Library

The Bug We Couldn't Reproduce (Until We Fixed Our Logging)

Users complained about errors we couldn't see. Our logs were useless. Here's what changed everything.

Table of Contents

  • Modern Logging 2026
  • Architecture Design
  • 5 Logging Patterns
  • Error Tracking
  • Performance Impact
  • Security Considerations
  • Troubleshooting
  • FAQ
  • Production Setup

Modern Logging in 2026

Good logs are your production debugger.

Logging Levels

// Standard log levels
enum LogLevel {
  DEBUG = 0,   // Development details
  INFO = 1,    // General info
  WARN = 2,    // Warnings
  ERROR = 3,   // Errors
  FATAL = 4    // Critical failures
}

// Use appropriately
logger.debug('Variable value:', { value });
logger.info('User logged in', { userId });
logger.warn('API rate limit approaching', { usage: 90 });
logger.error('Payment failed', { error, orderId });
logger.fatal('Database connection lost', { error });

Structured vs Unstructured

// ❌ Bad: Unstructured
console.log('User John Doe logged in at ' + new Date());

// ✅ Good: Structured
logger.info('User logged in', {
  userId: 'user_123',
  username: 'johndoe',
  timestamp: new Date().toISOString(),
  ip: req.ip,
  userAgent: req.get('user-agent')
});

Business Impact

Fast debugging = less downtime = happy users.

Architecture Design

Building observable systems.

Centralized Logging

// Send logs to central system
import { createLogger, transports } from 'winston';

const logger = createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.json(),
  defaultMeta: {
    service: 'api',
    environment: process.env.NODE_ENV,
    version: process.env.APP_VERSION
  },
  transports: [
    // Console for local dev
    new transports.Console(),
    // File for production
    new transports.File({ filename: 'error.log', level: 'error' }),
    new transports.File({ filename: 'combined.log' }),
    // HTTP transport to log aggregator
    new transports.Http({
      host: 'logs.example.com',
      port: 443,
      path: '/logs'
    })
  ]
});

Correlation IDs

// Track requests across services
import { v4 as uuidv4 } from 'uuid';

app.use((req, res, next) => {
  req.id = req.get('x-request-id') || uuidv4();
  res.setHeader('x-request-id', req.id);

  // Add to all logs
  req.log = logger.child({ requestId: req.id });

  next();
});

// Usage
app.get('/api/users', (req, res) => {
  req.log.info('Fetching users');
  // All logs include requestId
});

Pattern 1: Structured Logging

JSON Format

// Log as JSON objects
interface LogEntry {
  timestamp: string;
  level: string;
  message: string;
  context: {
    requestId: string;
    userId?: string;
    [key: string]: any;
  };
  error?: {
    message: string;
    stack: string;
    code?: string;
  };
}

function log(entry: LogEntry) {
  console.log(JSON.stringify({
    ...entry,
    timestamp: new Date().toISOString()
  }));
}

// Usage
log({
  level: 'error',
  message: 'Payment processing failed',
  context: {
    requestId: req.id,
    userId: req.user.id,
    orderId: order.id,
    amount: order.total
  },
  error: {
    message: err.message,
    stack: err.stack,
    code: err.code
  }
});

Context Enrichment

// Add context automatically
class Logger {
  private context: Record<string, any> = {};

  child(context: Record<string, any>) {
    const childLogger = new Logger();
    childLogger.context = { ...this.context, ...context };
    return childLogger;
  }

  info(message: string, data?: any) {
    this.write('info', message, data);
  }

  private write(level: string, message: string, data?: any) {
    console.log(JSON.stringify({
      timestamp: new Date().toISOString(),
      level,
      message,
      ...this.context,
      ...data
    }));
  }
}

Pattern 2: Error Tracking

Sentry Integration

// Initialize Sentry
import * as Sentry from '@sentry/node';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 1.0
});

// Capture errors
app.use((err, req, res, next) => {
  Sentry.captureException(err, {
    user: {
      id: req.user?.id,
      email: req.user?.email
    },
    tags: {
      path: req.path,
      method: req.method
    },
    extra: {
      body: req.body,
      query: req.query
    }
  });

  res.status(500).json({ error: 'Internal server error' });
});

Custom Error Context

// Add breadcrumbs
Sentry.addBreadcrumb({
  category: 'payment',
  message: 'Processing payment',
  level: 'info',
  data: {
    amount: 99.99,
    currency: 'USD'
  }
});

try {
  await processPayment();
} catch (error) {
  Sentry.captureException(error);
  throw error;
}

Pattern 3: Performance Logging

Request Timing

// Log request duration
app.use((req, res, next) => {
  const start = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - start;

    logger.info('Request completed', {
      method: req.method,
      path: req.path,
      status: res.statusCode,
      duration,
      requestId: req.id
    });

    // Alert on slow requests
    if (duration > 5000) {
      logger.warn('Slow request detected', {
        method: req.method,
        path: req.path,
        duration,
        requestId: req.id
      });
    }
  });

  next();
});

Database Query Logging

// Log slow queries
const db = knex({
  // ...config
  log: {
    warn(message) {
      logger.warn('Database warning', { message });
    },
    error(message) {
      logger.error('Database error', { message });
    },
    debug(message) {
      // Only in development
      if (process.env.NODE_ENV === 'development') {
        logger.debug('Database query', { message });
      }
    }
  }
});

Pattern 4: Log Aggregation

Query Logs

// Search logs by fields
const errors = await logService.query({
  level: 'error',
  timeRange: {
    start: new Date(Date.now() - 24 * 60 * 60 * 1000),
    end: new Date()
  },
  filter: {
    'context.userId': 'user_123'
  }
});

// Aggregate metrics
const errorCount = await logService.aggregate({
  field: 'level',
  value: 'error',
  groupBy: 'context.service',
  timeRange: 'last_hour'
});

Alerts

// Alert on error spike
const alertRule = {
  name: 'High Error Rate',
  condition: {
    metric: 'error_count',
    threshold: 100,
    window: '5m'
  },
  actions: [
    { type: 'email', to: 'team@example.com' },
    { type: 'slack', channel: '#alerts' },
    { type: 'pagerduty', severity: 'high' }
  ]
};

Pattern 5: Sensitive Data

Redaction

// Redact sensitive fields
const sensitiveFields = ['password', 'token', 'ssn', 'credit_card'];

function redact(obj: any): any {
  if (typeof obj !== 'object' || obj === null) return obj;

  const result = Array.isArray(obj) ? [] : {};

  for (const [key, value] of Object.entries(obj)) {
    if (sensitiveFields.includes(key.toLowerCase())) {
      result[key] = '[REDACTED]';
    } else if (typeof value === 'object') {
      result[key] = redact(value);
    } else {
      result[key] = value;
    }
  }

  return result;
}

// Usage
logger.info('User updated', redact(userData));

GDPR Compliance

// Delete user logs
async function deleteUserLogs(userId: string) {
  await logService.delete({
    'context.userId': userId
  });
}

Performance Impact

MethodOverheadThroughput
console.log~5ms20K/s
Async file~1ms100K/s
Buffered~0.1ms1M/s
Async HTTP~2ms50K/s

Security Considerations

Don't Log Secrets

// ❌ Never log these
- Passwords
- API keys
- Tokens
- Credit cards
- SSNs
- Private keys

// ✅ Safe to log
- User IDs
- Request IDs
- Timestamps
- Status codes
- Public data

Log Rotation

// Rotate logs daily
import { createLogger, transports } from 'winston';
import 'winston-daily-rotate-file';

const logger = createLogger({
  transports: [
    new transports.DailyRotateFile({
      filename: 'app-%DATE%.log',
      datePattern: 'YYYY-MM-DD',
      maxSize: '20m',
      maxFiles: '14d'
    })
  ]
});

FAQ

Q1: How much to log?

Info+ in production, debug in development.

Q2: Where to store logs?

Centralized system (ELK, Loki, BetterStack).

Q3: Log retention period?

30-90 days typical, longer for compliance.

Q4: Performance impact?

Async logging has minimal impact (<1ms).

Q5: Cost at scale?

Plan for $100-1000/month for medium app.

Production Setup

Checklist

  • [ ] Structured JSON logging
  • [ ] Log levels configured
  • [ ] Sensitive data redacted
  • [ ] Correlation IDs added
  • [ ] Error tracking setup
  • [ ] Alerts configured
  • [ ] Log rotation enabled
  • [ ] Retention policy set

Conclusion

Great logging enables great debugging.

Key takeaways:

  • Structure all logs
  • Add context
  • Track errors
  • Monitor performance
  • Protect sensitive data

Log smartly for fast debugging.

Resources:

  • Logging Best Practices
  • Error Tracking Guide
  • Performance Monitoring
  • Security Guidelines

Next Steps:

  1. Add structured logging
  2. Set up error tracking
  3. Configure alerts
  4. Test log queries
  5. Monitor production

Improve observability today.