Salta al contenuto principale

6 - Monitoraggio e Alerting per Applicazioni Web

Come implementare monitoraggio, error tracking e alerting per applicazioni web: Sentry, uptime monitoring, log management e dashboard.

Cosa ne pensi di questo articolo?

6 - Monitoraggio e Alerting per Applicazioni Web
DevOps FrontendFoto:Egor KomarovPexels

In Breve

Come implementare monitoraggio, error tracking e alerting per applicazioni web: Sentry, uptime monitoring, log management e dashboard.

Introduzione: Il Deploy Non e la Fine, e l'Inizio

Una pipeline CI/CD perfetta non serve a nulla se non sappiamo cosa succede dopo il deploy. Gli utenti reali interagiscono con la nostra applicazione in modi imprevedibili, su dispositivi diversi, con connessioni variabili. Il monitoraggio e l'alerting chiudono il ciclo DevOps: ci permettono di rilevare problemi prima che gli utenti li segnalino, e di rispondere rapidamente quando qualcosa non funziona.

In questo ultimo articolo della serie DevOps per Sviluppatori Frontend, esploreremo le tre categorie del monitoraggio frontend: error tracking, performance monitoring e user behavior analysis. Configureremo Sentry per l'error tracking in Angular, implementeremo il Real User Monitoring con web-vitals, e definiremo strategie di alerting efficaci.

Cosa Imparerai in Questo Articolo

  • Le tre categorie del monitoraggio frontend (errori, performance, comportamento utente)
  • Come integrare Sentry in un'applicazione Angular
  • Come implementare il Real User Monitoring (RUM)
  • Come configurare il synthetic monitoring (uptime check)
  • Come monitorare le performance con la libreria web-vitals
  • Come creare un Error Handler personalizzato in Angular
  • Come caricare le source map per il debugging in produzione
  • Strategie di alerting efficaci
  • Come configurare dashboard di monitoraggio
  • Confronto tra gli strumenti di monitoraggio più diffusi

1. Le Tre Categorie del Monitoraggio Frontend

Il monitoraggio frontend si divide in tre aree complementari, ognuna con obiettivi e strumenti specifici:

Categorie di Monitoraggio

Categoria Cosa Monitora Strumenti Tipici Priorità
Error Tracking Eccezioni JavaScript, errori HTTP, crash dell'applicazione Sentry, Bugsnag, Rollbar Critica
Performance Monitoring Core Web Vitals, tempi di caricamento, metriche di rete web-vitals, SpeedCurve, Datadog RUM Alta
User Behavior Navigazione, interazioni, funnel di conversione Google Analytics, Mixpanel, Amplitude Media

2. Error Tracking con Sentry

Sentry e la piattaforma di error tracking più utilizzata nel mondo frontend. Cattura automaticamente le eccezioni JavaScript, le arricchisce con il contesto del browser e le raggruppa per tipologia, facilitando enormemente il debugging.

Installazione e Configurazione Base


# Installare il pacchetto Sentry per Angular
npm install @sentry/angular @sentry/browser

# Oppure con il wizard automatico
npx @sentry/wizard@latest -i angular
      

// main.ts - Inizializzazione di Sentry
import * as Sentry from '@sentry/angular';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';

// Inizializzare Sentry PRIMA del bootstrap dell'applicazione
Sentry.init({
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  environment: 'production',
  release: 'my-app@1.0.0',

  // Tracciamento delle performance
  tracesSampleRate: 0.2, // 20% delle transazioni

  // Replay delle sessioni con errori
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,

  integrations: [
    Sentry.browserTracingIntegration(),
    Sentry.replayIntegration(),
  ],

  // Filtrare errori non rilevanti
  ignoreErrors: [
    'ResizeObserver loop limit exceeded',
    'Non-Error exception captured',
    /Loading chunk [\d]+ failed/,
  ],

  // Informazioni aggiuntive per ogni evento
  beforeSend(event) {
    // Rimuovere dati sensibili
    if (event.request?.cookies) {
      delete event.request.cookies;
    }
    return event;
  },
});

bootstrapApplication(AppComponent, appConfig);
      

Configurazione Angular con Provider


// app.config.ts - Registrare i provider Sentry
import { ApplicationConfig, ErrorHandler } from '@angular/core';
import { provideRouter, Router } from '@angular/router';
import * as Sentry from '@sentry/angular';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),

    // Error Handler personalizzato che invia a Sentry
    {
      provide: ErrorHandler,
      useValue: Sentry.createErrorHandler({
        showDialog: false,  // Non mostrare il dialog di Sentry agli utenti
        logErrors: true,    // Loggare anche nella console
      }),
    },

    // Tracciamento delle navigazioni Angular
    {
      provide: Sentry.TraceService,
      deps: [Router],
    },
  ],
};
      

Attenzione al Sample Rate

Il tracesSampleRate determina quante transazioni vengono inviate a Sentry. Un valore di 1.0 (100%) e utile in sviluppo ma troppo costoso in produzione con molto traffico. Inizia con 0.1 o 0.2 e aumenta se necessario. Per il replaysOnErrorSampleRate, mantenere 1.0 per catturare sempre la sessione quando si verifica un errore.

3. Custom Error Handler in Angular

Per un controllo più granulare sugli errori, possiamo creare un Error Handler personalizzato che combina Sentry con logica aggiuntiva:


// custom-error-handler.ts
import { ErrorHandler, Injectable, inject } from '@angular/core';
import * as Sentry from '@sentry/angular';

@Injectable()
export class CustomErrorHandler implements ErrorHandler {

  handleError(error: unknown): void {
    // Classificare l'errore
    const errorInfo = this.classifyError(error);

    // Inviare a Sentry con contesto aggiuntivo
    Sentry.withScope((scope) => {
      scope.setTag('error.type', errorInfo.type);
      scope.setTag('error.severity', errorInfo.severity);
      scope.setLevel(errorInfo.severity as Sentry.SeverityLevel);

      if (errorInfo.type === 'http') {
        scope.setContext('http', {
          status: (error as any)?.status,
          url: (error as any)?.url,
        });
      }

      Sentry.captureException(error);
    });

    // Loggare nella console in development
    console.error('[CustomErrorHandler]', error);
  }

  private classifyError(error: unknown): { type: string; severity: string } {
    if (error instanceof TypeError) {
      return { type: 'type-error', severity: 'error' };
    }
    if ((error as any)?.status !== undefined) {
      const status = (error as any).status;
      if (status >= 500) return { type: 'http', severity: 'error' };
      if (status >= 400) return { type: 'http', severity: 'warning' };
    }
    if (error instanceof Error && error.message?.includes('ChunkLoadError')) {
      return { type: 'chunk-load', severity: 'warning' };
    }
    return { type: 'unknown', severity: 'error' };
  }
}
      

4. Real User Monitoring (RUM)

Il Real User Monitoring misura le performance come le percepiscono gli utenti reali, non in un ambiente simulato. La libreria web-vitals di Google e lo standard per raccogliere le Core Web Vitals direttamente dal browser dell'utente.


# Installare web-vitals
npm install web-vitals
      

// web-vitals.service.ts
import { Injectable } from '@angular/core';
import { onCLS, onFCP, onINP, onLCP, onTTFB } from 'web-vitals';
import type { Metric } from 'web-vitals';

@Injectable({ providedIn: 'root' })
export class WebVitalsService {

  initializeTracking(): void {
    // Core Web Vitals
    onCLS(this.reportMetric.bind(this));
    onINP(this.reportMetric.bind(this));
    onLCP(this.reportMetric.bind(this));

    // Metriche supplementari
    onFCP(this.reportMetric.bind(this));
    onTTFB(this.reportMetric.bind(this));
  }

  private reportMetric(metric: Metric): void {
    const data = {
      name: metric.name,
      value: metric.value,
      rating: metric.rating,  // 'good' | 'needs-improvement' | 'poor'
      delta: metric.delta,
      id: metric.id,
      navigationType: metric.navigationType,
    };

    // Inviare a un endpoint di analisi
    this.sendToAnalytics(data);

    // Inviare a Sentry come breadcrumb
    this.sendToSentry(data);

    // Log in console per debugging
    console.log(`[Web Vital] ${metric.name}: ${metric.value} (${metric.rating})`);
  }

  private sendToAnalytics(data: Record<string, unknown>): void {
    // Inviare a Google Analytics
    if (typeof gtag !== 'undefined') {
      gtag('event', data['name'] as string, {
        value: Math.round(data['value'] as number),
        event_category: 'Web Vitals',
        event_label: data['rating'] as string,
        non_interaction: true,
      });
    }
  }

  private sendToSentry(data: Record<string, unknown>): void {
    import('@sentry/angular').then((Sentry) => {
      Sentry.addBreadcrumb({
        category: 'web-vital',
        message: `${data['name']}: ${data['value']} (${data['rating']})`,
        level: data['rating'] === 'poor' ? 'warning' : 'info',
        data: data,
      });
    });
  }
}
      

Inizializzare il Tracking nel Componente Root


// app.component.ts
import { Component, OnInit, inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { WebVitalsService } from './services/web-vitals.service';

@Component({
  selector: 'app-root',
  template: '<router-outlet />',
})
export class AppComponent implements OnInit {
  private webVitals = inject(WebVitalsService);
  private platformId = inject(PLATFORM_ID);

  ngOnInit(): void {
    // Web Vitals funziona solo nel browser, non in SSR
    if (isPlatformBrowser(this.platformId)) {
      this.webVitals.initializeTracking();
    }
  }
}
      

5. Synthetic Monitoring (Uptime Check)

Il synthetic monitoring esegue controlli automatici a intervalli regolari per verificare che il sito sia raggiungibile e funzionante. A differenza del RUM, non dipende dal traffico degli utenti: rileva i problemi anche alle 3 di notte quando nessuno visita il sito.


# GitHub Actions per uptime monitoring
name: Uptime Check

on:
  schedule:
    - cron: '*/15 * * * *'  # Ogni 15 minuti

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Check site availability
        run: |
          STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://example.com)
          RESPONSE_TIME=$(curl -s -o /dev/null -w "%{time_total}" https://example.com)

          echo "Status: $STATUS"
          echo "Response time: ${RESPONSE_TIME}s"

          if [ "$STATUS" != "200" ]; then
            echo "ALERT: Site returned status $STATUS"
            exit 1
          fi

          # Controllare il tempo di risposta
          THRESHOLD="3.0"
          if (( $(echo "$RESPONSE_TIME > $THRESHOLD" | bc -l) )); then
            echo "WARNING: Response time ${RESPONSE_TIME}s exceeds threshold ${THRESHOLD}s"
          fi
      

Servizi di Synthetic Monitoring

  • UptimeRobot: Piano gratuito con 50 monitor, controlli ogni 5 minuti
  • Pingdom: Monitoring avanzato con analisi RUM integrata
  • Checkly: Browser check con Playwright, ideale per flussi complessi
  • Better Uptime: Incident management con pagine di stato pubbliche
  • Google Cloud Monitoring: Uptime check integrato con l'ecosistema GCP

6. Source Map Upload per il Debugging

In produzione, il codice JavaScript e minificato e illeggibile. Le source map permettono a Sentry di mostrare lo stack trace originale con nomi di variabili e numeri di riga del codice sorgente. E fondamentale caricarle su Sentry ma non servirle pubblicamente.


# Upload delle source map nella pipeline CI/CD
  - name: Build with source maps
    run: npx ng build --configuration=production --source-map

  - name: Upload source maps to Sentry
    uses: getsentry/action-release@v1
    env:
      SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
      SENTRY_ORG: my-organization
      SENTRY_PROJECT: my-angular-app
    with:
      environment: production
      version: ${{ github.sha }}
      sourcemaps: './dist/my-app/browser'
      url_prefix: '~/'

  - name: Remove source maps before deploy
    run: find dist/my-app/browser -name "*.map" -delete
      

Mai Servire le Source Map in Produzione

Le source map contengono il codice sorgente completo dell'applicazione. Servirle pubblicamente permetterebbe a chiunque di leggere la logica di business, individuare vulnerabilità, e comprendere l'architettura interna. Caricale su Sentry e poi eliminale dalla directory di deploy.

7. Strategie di Alerting

Un sistema di monitoraggio senza alerting e come un allarme antincendio senza sirena. Ma troppi alert portano alla "alert fatigue", dove il team ignora le notifiche. La chiave e definire soglie intelligenti:

Livelli di Alert Consigliati

Livello Trigger Canale Risposta
Critico (P0) Sito completamente down, errori > 50% SMS + Slack + PagerDuty Immediata (entro 15 min)
Alto (P1) Funzionalità critica rotta, error rate > 10% Slack + Email Entro 1 ora
Medio (P2) Performance degradate, errori non critici > 5% Slack Entro 4 ore
Basso (P3) Warning, metriche in trend negativo Report settimanale Prossimo sprint

Configurazione Alert in Sentry


Configurazione Alert Sentry:

Alert 1: Spike di Errori
  Condizione: Quando il numero di eventi > 100 in 1 ora
  Azione: Notifica Slack #alerts + email team lead
  Frequenza: Max 1 notifica ogni 30 minuti

Alert 2: Nuovo Errore in Produzione
  Condizione: Prima occorrenza di un nuovo issue
  Azione: Notifica Slack #errors
  Frequenza: Ogni nuovo issue

Alert 3: Regressione
  Condizione: Issue precedentemente risolto riappare
  Azione: Notifica Slack #alerts + assegna all'ultimo resolver
  Frequenza: Ogni regressione

Alert 4: Performance Budget
  Condizione: LCP p95 > 4 secondi
  Azione: Notifica Slack #performance
  Frequenza: Max 1 notifica ogni 2 ore
      

8. Dashboard di Monitoraggio

Una dashboard efficace fornisce una visione d'insieme immediata della salute dell'applicazione. Ecco le metriche essenziali da includere:

Dashboard Essenziale


+----------------------------------------------------------+
|                    Application Health                      |
+----------------------------------------------------------+
|                                                            |
|  Uptime: 99.97%    Error Rate: 0.3%    Avg LCP: 1.8s     |
|                                                            |
+----------------------------------------------------------+
|  Core Web Vitals (p75)                                     |
|  +--------+  +--------+  +--------+                        |
|  | LCP    |  | INP    |  | CLS    |                        |
|  | 1.8s   |  | 120ms  |  | 0.05   |                        |
|  | (good) |  | (good) |  | (good) |                        |
|  +--------+  +--------+  +--------+                        |
+----------------------------------------------------------+
|  Errori Ultimi 7 Giorni            | Top Errori            |
|  +-----------------------------+   | 1. TypeError: null    |
|  | Mon Tue Wed Thu Fri Sat Sun |   | 2. ChunkLoadError     |
|  |   5   3   8   2  12   1   0 |   | 3. HttpError 500      |
|  +-----------------------------+   | 4. NetworkError       |
+----------------------------------------------------------+
      

9. Confronto Strumenti di Monitoraggio

Strumenti di Monitoraggio a Confronto

Strumento Tipo Piano Gratuito Punto di Forza Ideale Per
Sentry Error tracking + Performance 5K eventi/mese Error grouping, source maps Tutti i progetti
Datadog RUM Full-stack monitoring Limitato Correlazione frontend-backend Enterprise
LogRocket Session replay + Error tracking 1K sessioni/mese Video replay delle sessioni UX debugging
New Relic Browser RUM + Error tracking 100 GB/mese Dashboard personalizzabili Team operativi
Bugsnag Error tracking 7.5K eventi/mese Stabilità release tracking Mobile + Web

10. Implementazione Completa: Checklist

Ecco una checklist completa per implementare il monitoraggio in un progetto Angular:


// monitoring-setup.ts - Configurazione centralizzata
import { Injectable, inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Injectable({ providedIn: 'root' })
export class MonitoringService {
  private platformId = inject(PLATFORM_ID);

  initialize(): void {
    if (!isPlatformBrowser(this.platformId)) return;

    // 1. Error Tracking (Sentry già configurato nel main.ts)

    // 2. Web Vitals
    this.setupWebVitals();

    // 3. Custom error boundary per errori non gestiti
    this.setupGlobalErrorHandlers();

    // 4. Network monitoring
    this.setupNetworkMonitoring();
  }

  private async setupWebVitals(): Promise<void> {
    const { onCLS, onINP, onLCP, onFCP, onTTFB } = await import('web-vitals');
    const report = (metric: any) => {
      console.log(`[Vital] ${metric.name}: ${metric.value}`);
    };
    onCLS(report);
    onINP(report);
    onLCP(report);
    onFCP(report);
    onTTFB(report);
  }

  private setupGlobalErrorHandlers(): void {
    // Catturare promise non gestite
    window.addEventListener('unhandledrejection', (event) => {
      console.error('[Unhandled Rejection]', event.reason);
    });

    // Catturare errori di caricamento risorse
    window.addEventListener('error', (event) => {
      if (event.target instanceof HTMLScriptElement) {
        console.error('[Script Load Error]', event);
      }
    }, true);
  }

  private setupNetworkMonitoring(): void {
    // Monitorare i cambiamenti di connessione
    const connection = (navigator as any).connection;
    if (connection) {
      connection.addEventListener('change', () => {
        console.log('[Network]', {
          effectiveType: connection.effectiveType,
          downlink: connection.downlink,
          rtt: connection.rtt,
        });
      });
    }
  }
}
      

Checklist di Monitoraggio per il Go-Live

  • Sentry configurato e funzionante con error handler personalizzato
  • Source map caricate su Sentry (e rimosse dal deploy)
  • Web Vitals tracking attivo con invio a Google Analytics
  • Uptime monitoring configurato (controlli ogni 5 minuti)
  • Alert configurati per errori critici (Slack, email)
  • Dashboard creata con le metriche essenziali
  • Piano di rotazione del token Sentry configurato
  • Procedura di risposta agli incident documentata
  • SSR compatibility verificata (isPlatformBrowser)
  • Sample rate ottimizzato per il volume di traffico

Conclusione

Con questo articolo concludiamo la serie DevOps per Sviluppatori Frontend. Abbiamo completato il ciclo: dalla creazione di pipeline CI/CD con GitHub Actions, alla containerizzazione con Docker, al deploy automatico con Firebase, alla gestione dei segreti, al monitoraggio delle performance con Lighthouse CI, fino al monitoraggio in produzione con Sentry e web-vitals.

Il monitoraggio in produzione chiude il loop del DevOps: i dati raccolti dagli utenti reali alimentano le decisioni di sviluppo, le priorità di bugfix, e le ottimizzazioni di performance. Non e un componente opzionale, e la base per costruire applicazioni affidabili e per migliorare continuamente l'esperienza utente.

La chiave del successo nel DevOps frontend e l'iterazione progressiva: non implementare tutto in una volta, ma iniziare con le basi (CI/CD + error tracking) e aggiungere progressivamente le componenti avanzate (performance budgets, synthetic monitoring, alerting strutturato) man mano che il progetto e il team maturano.

Domande frequenti

Qual è l'obiettivo principale del monitoraggio frontend?

L'obiettivo principale del monitoraggio frontend è rilevare problemi prima che gli utenti li segnalino e rispondere rapidamente quando qualcosa non funziona.

Come si divide il monitoraggio frontend in tre aree complementari?

Il monitoraggio frontend si divide in tre aree complementari: error tracking, performance monitoring e user behavior analysis.

Qual è la differenza tra Real User Monitoring (RUM) e Synthetic Monitoring (Uptime Check)?

La RUM misura le prestazioni come le percepiscono gli utenti reali, mentre il Synthetic Monitoring esegue controlli automatici a intervalli regolari per verificare che il sito sia raggiungibile e funzionante.

Perché è importante caricare le source map su Sentry ma non servirle pubblicamente?

Le source map contengono il codice sorgente completo dell'applicazione, quindi servirle pubblicamente potrebbe permettere a chiunque di leggere la logica di business e comprendere l'architettura interna.

Fonti e approfondimenti

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti