Back

Bonnes pratiques pour la journalisation des erreurs en JavaScript

Bonnes pratiques pour la journalisation des erreurs en JavaScript

Les applications JavaScript en production échouent silencieusement chaque jour. Les utilisateurs rencontrent des erreurs que les développeurs ne voient jamais, entraînant de mauvaises expériences et des pertes de revenus. La différence entre les applications qui détectent ces problèmes et celles qui ne le font pas ? Une journalisation appropriée des erreurs.

Cet article couvre les pratiques essentielles pour implémenter une journalisation robuste des erreurs JavaScript dans les environnements frontend et backend. Vous apprendrez à aller au-delà de console.log, à implémenter une journalisation structurée avec des frameworks éprouvés, et à construire un système qui capture les erreurs critiques avant que les utilisateurs ne les signalent.

Points clés à retenir

  • La journalisation console manque de persistance, de centralisation et de structure nécessaires pour les environnements de production
  • La journalisation structurée avec des frameworks comme Winston ou Pino fournit des données analysables par machine
  • La gestion des erreurs frontend nécessite des gestionnaires globaux et des solutions spécifiques aux frameworks comme les Error Boundaries de React
  • La protection des données sensibles et l’inclusion d’informations contextuelles sont essentielles pour une journalisation efficace

Pourquoi la journalisation console est insuffisante en production

La plupart des développeurs commencent avec console.log() pour le débogage. Bien qu’adéquate pendant le développement, cette approche échoue en production :

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

Les méthodes console manquent de :

  • Persistance au-delà de la session en cours
  • Collection centralisée entre les utilisateurs
  • Données structurées pour l’analyse
  • Niveaux de gravité pour la priorisation
  • Protection des données sensibles

Les applications de production nécessitent une journalisation qui capture, structure et transmet les erreurs vers un emplacement central pour analyse.

Implémentation de la journalisation structurée avec des frameworks éprouvés

Choisir le bon framework

Pour la journalisation Node.js, deux frameworks dominent l’écosystème :

Winston offre flexibilité et options de transport étendues :

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 privilégie la performance avec une surcharge minimale :

const pino = require('pino');

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

Structurer vos logs

Remplacez les chaînes non structurées par des objets JSON que les machines peuvent analyser :

// 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']
});

Composants essentiels d’une journalisation efficace des erreurs JavaScript

1. Utiliser des niveaux de log appropriés

Implémentez des niveaux de gravité cohérents dans toute votre application :

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. Toujours inclure les traces de pile

Capturez le contexte complet de l’erreur pour le débogage :

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. Ajouter des informations contextuelles

Incluez les identifiants de requête, d’utilisateur et les données de session pour tracer les problèmes :

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

requestLogger.info('Processing payment request');

4. Protéger les données sensibles

Ne journalisez jamais les mots de passe, jetons ou informations personnelles :

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'
});

Stratégies de gestion des erreurs frontend

Gestionnaires d’erreurs globaux

Capturez toutes les erreurs non gérées dans les environnements navigateur :

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
  });
});

Error Boundaries React

Pour les applications React, implémentez des Error Boundaries pour capturer les erreurs de composants :

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;
  }
}

Centraliser les logs pour l’analyse

Dirigez tous les logs vers stdout et laissez votre infrastructure gérer le routage :

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

Cette approche permet à Docker, Kubernetes ou aux collecteurs de logs comme Fluentd de collecter et router les logs vers des systèmes centralisés pour analyse. Pour les applications côté client, implémentez un point de terminaison simple pour recevoir et transmettre les logs des navigateurs vers votre infrastructure de journalisation centralisée.

Conclusion

Une journalisation efficace des erreurs JavaScript nécessite plus que remplacer console.log par un framework. Elle exige des données structurées, des niveaux de gravité appropriés, un contexte d’erreur complet et une collecte centralisée. En implémentant ces pratiques avec des frameworks comme Winston ou Pino, en protégeant les données sensibles et en établissant des Error Boundaries appropriées dans le code frontend, vous créez un système qui détecte les problèmes avant qu’ils n’impactent les utilisateurs. Commencez par ces fondamentaux, puis développez en fonction des besoins spécifiques de surveillance de votre application.

FAQ

Pino a une surcharge minimale, ajoutant seulement 2 à 3 % de latence dans la plupart des cas. Winston est légèrement plus lourd mais reste négligeable pour la plupart des applications. Les deux sont prêts pour la production et utilisés par des applications à fort trafic dans le monde entier.

Utilisez les fonctionnalités de masquage intégrées dans votre framework de journalisation pour masquer automatiquement les champs sensibles. Définissez une liste de noms de champs à masquer comme les mots de passe, jetons et numéros de carte de crédit. Auditez toujours vos logs régulièrement pour détecter toute exposition accidentelle de données.

Journalisez les erreurs des deux côtés. La journalisation côté client capture les problèmes spécifiques au navigateur et les erreurs JavaScript qui n'atteignent jamais votre serveur. La journalisation côté serveur gère les erreurs d'API et les défaillances backend. Utilisez un système centralisé pour agréger les deux sources.

Configurez des alertes pour les niveaux error et fatal immédiatement. Les niveaux warning peuvent déclencher des résumés quotidiens. Les niveaux info et debug doivent être consultables mais ne pas déclencher d'alertes, sauf si vous enquêtez sur des problèmes spécifiques ou surveillez des événements métier critiques.

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