Winston.js im Production-Einsatz: Wie ihr eure Node.js-Logs endlich in den Griff bekommt

Von Roland Golla
0 Kommentar
Surreales Bild: Entwickler vor digitalem Auge mit Log-Daten

„Schon wieder ist die Anwendung abgestürzt und niemand weiß warum!“ – Kennt ihr das? Als wir letzte Woche bei einem unserer Enterprise-Kunden einen kritischen Production-Bug fixen mussten, wurde uns wieder einmal bewusst: Ohne vernünftiges Logging ist Debugging wie Stochern im Nebel.

Warum Winston.js die erste Wahl für Node.js-Logging ist

In der Node.js-Welt gibt es viele Logging-Libraries, aber Winston hat sich als De-facto-Standard etabliert. Und das aus gutem Grund! Bei Never Code Alone setzen wir Winston in allen größeren Node.js-Projekten ein – von E-Commerce-Plattformen bis zu hochskalierbaren Microservices.

Die Stärken von Winston auf einen Blick:

  • Multiple Transports: Logs gleichzeitig in Dateien, Datenbanken und externe Services schreiben
  • Log-Level Management: Von debug bis error – alles sauber strukturiert
  • Performance: Auch bei tausenden Requests pro Sekunde keine Einbußen
  • Erweiterbarkeit: Custom Formatter und eigene Transports sind kein Problem

Setup: So startet ihr mit Winston durch

Schluss mit console.log()! Hier kommt euer Production-Ready Logging-Setup:

const winston = require('winston');
const { combine, timestamp, json, printf, colorize } = winston.format;

// Custom Format für lokale Entwicklung
const devFormat = printf(({ level, message, timestamp, ...metadata }) => {
  let msg = `${timestamp} [${level}] : ${message} `;
  if (Object.keys(metadata).length > 0) {
    msg += JSON.stringify(metadata);
  }
  return msg;
});

// Production Logger Konfiguration
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: combine(
    timestamp({
      format: 'YYYY-MM-DD HH:mm:ss'
    }),
    process.env.NODE_ENV === 'production' ? json() : devFormat
  ),
  transports: [
    // Console Output
    new winston.transports.Console({
      format: process.env.NODE_ENV !== 'production' 
        ? combine(colorize(), devFormat) 
        : json()
    }),
    // File Rotation für Production
    new winston.transports.File({ 
      filename: 'logs/error.log', 
      level: 'error',
      maxsize: 5242880, // 5MB
      maxFiles: 5
    }),
    new winston.transports.File({ 
      filename: 'logs/combined.log',
      maxsize: 5242880,
      maxFiles: 5
    })
  ]
});

// Graceful Shutdown
process.on('unhandledRejection', (ex) => {
  logger.error('Unhandled Rejection', ex);
  process.exit(1);
});

module.exports = logger;

Best Practices aus der Praxis

Nach vier Jahren intensiver Winston-Nutzung in Enterprise-Projekten haben wir bei Never Code Alone einige goldene Regeln entwickelt:

1. Strukturierte Logs sind Gold wert

Vergesst unstrukturierte String-Logs! Mit strukturierten Daten könnt ihr später in Elasticsearch oder Splunk richtig durchstarten:

// ❌ So nicht!
logger.info(`User ${userId} logged in from ${ip}`);

// ✅ So macht ihr es richtig!
logger.info('User login', {
  userId,
  ip,
  userAgent: req.headers['user-agent'],
  timestamp: new Date().toISOString()
});

2. Context ist King

Nutzt Child Logger für Request-spezifische Kontexte:

app.use((req, res, next) => {
  req.logger = logger.child({
    requestId: req.id,
    method: req.method,
    url: req.url
  });
  next();
});

// In euren Routes
app.get('/api/users/:id', (req, res) => {
  req.logger.info('Fetching user', { userId: req.params.id });
  // Der requestId wird automatisch mitgeloggt!
});

3. Performance Monitoring inklusive

Messt die Performance kritischer Operationen direkt mit:

const profiler = logger.startTimer();
await heavyDatabaseOperation();
profiler.done({ message: 'Database query completed' });
// Loggt automatisch die Ausführungszeit!

Integration mit externen Services

In Production-Umgebungen reichen lokale Log-Dateien nicht aus. Hier unsere bewährte Stack-Kombination:

Elasticsearch + Kibana Setup

const ElasticsearchTransport = require('winston-elasticsearch');

const esTransportOpts = {
  level: 'info',
  clientOpts: { 
    node: process.env.ELASTICSEARCH_URL,
    auth: {
      username: process.env.ELASTIC_USER,
      password: process.env.ELASTIC_PASSWORD
    }
  },
  index: 'logs-app',
  pipeline: 'timestamp_pipeline'
};

logger.add(new ElasticsearchTransport(esTransportOpts));

Slack-Alerts für kritische Fehler

const SlackHook = require('winston-slack-webhook-transport');

logger.add(new SlackHook({
  webhookUrl: process.env.SLACK_WEBHOOK_URL,
  level: 'error',
  formatter: (info) => ({
    text: `🚨 *${info.level.toUpperCase()}* in ${process.env.APP_NAME}`,
    attachments: [{
      color: 'danger',
      fields: [{
        title: 'Error Message',
        value: info.message,
        short: false
      }, {
        title: 'Stack Trace',
        value: ````${info.stack || 'No stack trace'}````,
        short: false
      }]
    }]
  })
}));

Häufige Fallstricke und wie ihr sie vermeidet

Memory Leaks durch falsche Transport-Konfiguration

// ❌ Gefährlich: Unbegrenzte In-Memory Queue
const transport = new winston.transports.Console({
  stderrLevels: ['error']
});

// ✅ Sicher: Mit Limits arbeiten
const transport = new winston.transports.Console({
  stderrLevels: ['error'],
  handleExceptions: true,
  handleRejections: true
});

Sensible Daten in Logs

Implementiert einen Sanitizer für sensible Daten:

const sanitizer = winston.format((info) => {
  // Passwörter entfernen
  if (info.password) info.password = '***REDACTED***';
  // Kreditkarten maskieren
  if (info.creditCard) {
    info.creditCard = info.creditCard.replace(/d{12}/, '************');
  }
  return info;
});

logger.add(sanitizer());

Testing nicht vergessen!

Eure Logs sind Teil der Anwendung – testet sie auch:

describe('Logger Tests', () => {
  it('should log errors with correct format', () => {
    const mockTransport = new winston.transports.Stream({
      stream: new WritableStream()
    });

    testLogger.add(mockTransport);
    testLogger.error('Test error', { code: 'TEST_001' });

    expect(mockTransport.stream.lastWrite).toMatchObject({
      level: 'error',
      message: 'Test error',
      code: 'TEST_001'
    });
  });
});

Fazit: Logging als Investition in die Zukunft

Gutes Logging ist wie eine Versicherung – ihr braucht es erst, wenn es zu spät ist. Mit Winston.js habt ihr ein mächtiges Werkzeug an der Hand, das euch im Ernstfall den Arsch rettet.

Bei Never Code Alone haben wir durch konsequentes Logging die Mean Time To Resolution (MTTR) bei kritischen Bugs um über 60% reduziert. Das Investment in ein vernünftiges Logging-Setup zahlt sich schneller aus, als ihr denkt!

Ihr wollt Winston.js in eurem Projekt einführen?

Unsere Experten helfen euch bei der Implementierung einer Production-Ready Logging-Strategie. Von der Architektur bis zur Integration in eure bestehende Monitoring-Landschaft – wir haben die Erfahrung aus dutzenden Enterprise-Projekten.

0 Kommentar

Tutorials und Top Posts

Gib uns Feedback

Diese Seite benutzt Cookies. Ein Akzeptieren hilft uns die Seite zu verbessern. Ok Mehr dazu