Back

Best Practices für Error Logging in JavaScript

Best Practices für Error Logging in JavaScript

JavaScript-Anwendungen in der Produktivumgebung scheitern täglich still und leise. Benutzer stoßen auf Fehler, die Entwickler nie zu Gesicht bekommen, was zu schlechten Erfahrungen und Umsatzverlusten führt. Der Unterschied zwischen Anwendungen, die diese Probleme erfassen, und solchen, die es nicht tun? Ordnungsgemäßes Error Logging.

Dieser Artikel behandelt wesentliche Praktiken für die Implementierung robuster JavaScript-Fehlerprotokollierung in Frontend- und Backend-Umgebungen. Sie lernen, wie Sie über console.log hinausgehen, strukturiertes Logging mit bewährten Frameworks implementieren und ein System aufbauen, das kritische Fehler erfasst, bevor Benutzer sie melden.

Wichtigste Erkenntnisse

  • Console Logging fehlt die Persistenz, Zentralisierung und Struktur, die für Produktivumgebungen erforderlich sind
  • Strukturiertes Logging mit Frameworks wie Winston oder Pino liefert maschinenlesbare Daten zur Analyse
  • Frontend-Fehlerbehandlung erfordert globale Handler und Framework-spezifische Lösungen wie React Error Boundaries
  • Der Schutz sensibler Daten und die Einbeziehung kontextbezogener Informationen sind entscheidend für effektives Logging

Warum Console Logging in der Produktion nicht ausreicht

Die meisten Entwickler beginnen mit console.log() zum Debuggen. Während dieser Ansatz während der Entwicklung ausreichend ist, versagt er in der Produktion:

// This error disappears into the user's browser
try {
  processPayment(order);
} catch (error) {
  console.error(error); // Lost forever in production
}

Console-Methoden fehlt:

  • Persistenz über die aktuelle Sitzung hinaus
  • Zentralisierte Erfassung über alle Benutzer hinweg
  • Strukturierte Daten zur Analyse
  • Schweregrade zur Priorisierung
  • Schutz sensibler Daten

Produktionsanwendungen benötigen Logging, das Fehler erfasst, strukturiert und an einen zentralen Ort zur Analyse übermittelt.

Implementierung von strukturiertem Logging mit bewährten Frameworks

Auswahl des richtigen Frameworks

Für Node.js-Logging dominieren zwei Frameworks das Ökosystem:

Winston bietet Flexibilität und umfangreiche Transport-Optionen:

const winston = require('winston');

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'error.log', level: 'error' })
  ]
});

Pino priorisiert Performance mit minimalem Overhead:

const pino = require('pino');

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  timestamp: pino.stdTimeFunctions.isoTime,
  formatters: {
    level: (label) => ({ level: label })
  }
});

Strukturierung Ihrer Logs

Ersetzen Sie unstrukturierte Strings durch JSON-Objekte, die Maschinen parsen und analysieren können:

// Bad: Unstructured string
logger.info(`User ${userId} failed login attempt`);

// Good: Structured JSON
logger.info({
  event: 'login_failed',
  userId: userId,
  ip: request.ip,
  timestamp: new Date().toISOString(),
  userAgent: request.headers['user-agent']
});

Wesentliche Komponenten effektiver JavaScript-Fehlerprotokollierung

1. Verwenden Sie geeignete Log-Level

Implementieren Sie konsistente Schweregrade in Ihrer gesamten Anwendung:

logger.debug('Detailed debugging information');
logger.info('Normal application flow');
logger.warn('Warning: degraded performance detected');
logger.error('Error occurred but application continues');
logger.fatal('Critical failure, application shutting down');

2. Schließen Sie immer Stack Traces ein

Erfassen Sie den vollständigen Fehlerkontext zum Debuggen:

process.on('uncaughtException', (error) => {
  logger.fatal({
    message: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString()
  });
  process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  logger.error({
    message: 'Unhandled Promise Rejection',
    reason: reason,
    promise: promise
  });
});

3. Fügen Sie kontextbezogene Informationen hinzu

Beziehen Sie Request-IDs, User-IDs und Sitzungsdaten ein, um Probleme nachzuverfolgen:

const requestLogger = logger.child({
  requestId: generateRequestId(),
  sessionId: request.session.id
});

requestLogger.info('Processing payment request');

4. Schützen Sie sensible Daten

Protokollieren Sie niemals Passwörter, Tokens oder persönliche Informationen:

const logger = pino({
  redact: ['password', 'creditCard', 'ssn', 'authorization']
});

// These fields will be automatically redacted
logger.info({
  user: email,
  password: 'secret123', // Will show as [REDACTED]
  action: 'login_attempt'
});

Frontend-Fehlerbehandlungsstrategien

Globale Error Handler

Erfassen Sie alle unbehandelten Fehler in Browser-Umgebungen:

window.addEventListener('error', (event) => {
  logToServer({
    message: event.message,
    source: event.filename,
    line: event.lineno,
    column: event.colno,
    stack: event.error?.stack
  });
});

window.addEventListener('unhandledrejection', (event) => {
  logToServer({
    type: 'unhandledRejection',
    reason: event.reason,
    promise: event.promise
  });
});

React Error Boundaries

Implementieren Sie für React-Anwendungen Error Boundaries, um Komponentenfehler abzufangen:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    logger.error({
      message: error.toString(),
      componentStack: errorInfo.componentStack,
      timestamp: new Date().toISOString()
    });
  }

  render() {
    if (this.state.hasError) {
      return <h2>Something went wrong. Please refresh the page.</h2>;
    }
    return this.props.children;
  }
}

Zentralisierung von Logs zur Analyse

Leiten Sie alle Logs an stdout weiter und lassen Sie Ihre Infrastruktur das Routing übernehmen:

// Configure logger to output to stdout only
const logger = pino({
  transport: {
    target: 'pino-pretty',
    options: {
      destination: 1 // stdout
    }
  }
});

Dieser Ansatz ermöglicht es Docker, Kubernetes oder Log-Shippern wie Fluentd, Logs zu sammeln und an zentralisierte Systeme zur Analyse weiterzuleiten. Implementieren Sie für clientseitige Anwendungen einen einfachen Endpunkt, um Logs von Browsern zu empfangen und an Ihre zentralisierte Logging-Infrastruktur weiterzuleiten.

Fazit

Effektives JavaScript-Error-Logging erfordert mehr als nur den Ersatz von console.log durch ein Framework. Es erfordert strukturierte Daten, geeignete Schweregrade, umfassenden Fehlerkontext und zentralisierte Erfassung. Durch die Implementierung dieser Praktiken mit Frameworks wie Winston oder Pino, den Schutz sensibler Daten und die Einrichtung geeigneter Error Boundaries im Frontend-Code schaffen Sie ein System, das Probleme erfasst, bevor sie Benutzer beeinträchtigen. Beginnen Sie mit diesen Grundlagen und erweitern Sie dann basierend auf den spezifischen Monitoring-Anforderungen Ihrer Anwendung.

Häufig gestellte Fragen

Pino hat minimalen Overhead und fügt in den meisten Fällen nur 2-3% Latenz hinzu. Winston ist etwas schwerer, aber für die meisten Anwendungen immer noch vernachlässigbar. Beide sind produktionsreif und werden von Anwendungen mit hohem Traffic weltweit eingesetzt.

Verwenden Sie integrierte Redaction-Funktionen in Ihrem Logging-Framework, um sensible Felder automatisch zu maskieren. Definieren Sie eine Liste von Feldnamen, die geschwärzt werden sollen, wie Passwörter, Tokens und Kreditkartennummern. Überprüfen Sie Ihre Logs regelmäßig auf versehentliche Datenoffenlegung.

Protokollieren Sie Fehler auf beiden Seiten. Clientseitiges Logging erfasst browserspezifische Probleme und JavaScript-Fehler, die Ihren Server nie erreichen. Serverseitiges Logging behandelt API-Fehler und Backend-Ausfälle. Verwenden Sie ein zentralisiertes System, um beide Quellen zu aggregieren.

Konfigurieren Sie Alerts für Error- und Fatal-Level sofort. Warning-Level können tägliche Zusammenfassungen auslösen. Info- und Debug-Level sollten durchsuchbar sein, aber keine Alerts auslösen, es sei denn, Sie untersuchen spezifische Probleme oder überwachen kritische Geschäftsereignisse.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay