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.

Commenti
Caricamento commenti...
Accedi per commentare