Angular Error Handling : logging et observabilité

🏷️ Front-end 📅 14/04/2026 01:15:00 👤 Mezgani said
Angular Error Handling Logging Observability Monitoring
Angular Error Handling : logging et observabilité

Mettez en place une stratégie de gestion d'erreurs Angular : ErrorHandler global, logs contextualisés, tracing et monitoring en production.

ErrorHandler global Angular

Angular expose un token ErrorHandler que tu peux remplacer pour intercepter toutes les erreurs JavaScript non gérées — erreurs de composants, exceptions dans les services, rejections de promesses.

import { ErrorHandler, Injectable, inject } from '@angular/core';
import { Router } from '@angular/router';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
    private router = inject(Router);

    handleError(error: unknown): void {
        const message = error instanceof Error ? error.message : String(error);
        const stack = error instanceof Error ? error.stack : undefined;

        // Enrichir avec le contexte courant
        console.error({
            message,
            stack,
            url: this.router.url,
            timestamp: new Date().toISOString()
        });

        // Envoyer à un service de monitoring (Sentry, Datadog…)
        // this.monitoringService.captureException(error);
    }
}

Enregistrement dans app.config.ts :

import { ApplicationConfig } from '@angular/core';
import { ErrorHandler } from '@angular/core';
import { GlobalErrorHandler } from './global-error-handler';

export const appConfig: ApplicationConfig = {
    providers: [
        { provide: ErrorHandler, useClass: GlobalErrorHandler }
    ]
};
Attention: l'ErrorHandler global ne capture pas les erreurs dans les async/await non rattachés à la zone Angular. Utilise window.addEventListener('unhandledrejection', ...) pour ceux-là.

Gestion des erreurs HTTP

Les erreurs HTTP (4xx, 5xx, timeout, réseau) doivent être interceptées avant qu'elles atteignent les composants. Un intercepteur fonctionnel Angular 15+ est l'endroit idéal.

import { HttpInterceptorFn, HttpRequest, HttpHandlerFn, HttpErrorResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { catchError, throwError } from 'rxjs';
import { Router } from '@angular/router';

export const errorInterceptor: HttpInterceptorFn = (
    req: HttpRequest<unknown>,
    next: HttpHandlerFn
) => {
    const router = inject(Router);

    return next(req).pipe(
        catchError((error: HttpErrorResponse) => {
            switch (error.status) {
                case 401:
                    router.navigate(['/login']);
                    break;
                case 403:
                    router.navigate(['/acces-refuse']);
                    break;
                case 404:
                    router.navigate(['/404']);
                    break;
                case 0:
                    console.error('Erreur réseau — vérifier la connexion');
                    break;
                default:
                    console.error(`Erreur serveur ${error.status}:`, error.message);
            }
            return throwError(() => error);
        })
    );
};

Ne pas avaler les erreurs

  1. Toujours retourner throwError() après traitement — avaler l'erreur bloque le flux Observable et rend le débogage impossible.
  2. Pour les retries (réseau instable), utilise retry({ count: 2, delay: 1000 }) avant le catchError.

Logs structurés et niveaux

Un service de logging centralisé permet d'associer un niveau (debug, info, warn, error), une source et un contexte à chaque entrée de log.

export type LogLevel = 'debug' | 'info' | 'warn' | 'error';

export interface LogEntry {
    level: LogLevel;
    message: string;
    context?: Record<string, unknown>;
    timestamp: string;
    correlationId?: string;
}

@Injectable({ providedIn: 'root' })
export class LoggerService {
    private readonly isProd = !isDevMode();

    log(level: LogLevel, message: string, context?: Record<string, unknown>): void {
        if (this.isProd && level === 'debug') return; // muet en prod

        const entry: LogEntry = {
            level,
            message,
            context,
            timestamp: new Date().toISOString()
        };

        console[level](JSON.stringify(entry));
        // En prod, envoyer à ton backend de logs
    }

    debug(msg: string, ctx?: Record<string, unknown>): void { this.log('debug', msg, ctx); }
    info(msg: string, ctx?: Record<string, unknown>): void  { this.log('info',  msg, ctx); }
    warn(msg: string, ctx?: Record<string, unknown>): void  { this.log('warn',  msg, ctx); }
    error(msg: string, ctx?: Record<string, unknown>): void { this.log('error', msg, ctx); }
}

Tracing front-back avec correlation ID

Un correlation ID (UUID généré côté client) envoyé dans chaque requête HTTP permet de relier une erreur front à la trace serveur correspondante — indispensable pour les architectures microservices.

import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';

let correlationId = crypto.randomUUID(); // renouvelé par session

export const correlationInterceptor: HttpInterceptorFn = (req, next) => {
    const traced = req.clone({
        setHeaders: { 'X-Correlation-ID': correlationId }
    });
    return next(traced);
};
Côté serveur: lire le header X-Correlation-ID et l'inclure dans chaque ligne de log. Les outils comme Datadog, Grafana ou ELK permettent ensuite de retrouver toute la chaîne d'appels en filtrant sur cet ID.

Monitoring en production

Pour les applications Angular en production, Sentry est la solution la plus répandue : il capture automatiquement les erreurs JS, les breadcrumbs de navigation et les performances.

// main.ts
import * as Sentry from '@sentry/angular';

Sentry.init({
    dsn: 'https://votre-dsn@sentry.io/projet',
    environment: 'production',
    release: '1.0.0',
    tracesSampleRate: 0.1,   // 10% des transactions tracées
    integrations: [
        Sentry.browserTracingIntegration(),
        Sentry.replayIntegration({ maskAllText: true })
    ]
});

Les métriques clés à surveiller :

  • Error rate — pourcentage de sessions avec au moins une erreur JS.
  • Core Web Vitals — LCP, INP, CLS mesurés en conditions réelles.
  • API error rate — ratio 4xx/5xx par endpoint.
  • Apdex — satisfaction utilisateur basée sur les temps de réponse.

Checklist observabilité

  • Implémenter un ErrorHandler global qui capture et enrichit toutes les erreurs non gérées.
  • Créer un intercepteur HTTP qui catégorise les erreurs (réseau, 4xx, 5xx) et redirige si nécessaire.
  • Ajouter un X-Correlation-ID dans chaque requête pour le tracing distribué.
  • Logger en JSON structuré avec niveau, source et timestamp — jamais de console.infoo brut en prod.
  • Désactiver les logs debug en production avec isDevMode().
  • Configurer des alertes sur le taux d'erreurs et les Core Web Vitals dégradés.
  • Tester régulièrement les scénarios d'erreur (réseau coupé, token expiré, 500 API) en dev pour vérifier le comportement.
  • Organiser un post-mortem court après chaque incident majeur et documenter les actions correctives.