Laravel Pulse: monitoraggio applicativo nativo senza strumenti esterni
A ottobre 2025 gestivo tre applicazioni Laravel in produzione per altrettanti clienti del settore servizi: un portale B2B con 800 utenti attivi e 15.000 richieste al giorno, un gestionale interno con 60 utenti e 200 job in coda ogni ora, e un e-commerce con picchi di 2.000 sessioni simultanee durante le campagne promozionali. Tutte e tre giravano su VPS Hetzner con stack LEMP, e nessuna aveva un sistema di monitoraggio applicativo. L'unico feedback sulle prestazioni arrivava dai clienti che chiamavano per dire "il sito è lento" o dal cronjob che controllava l'uptime con un ping HTTP ogni 5 minuti. Sapevo che qualcosa era lento, ma non sapevo cosa fosse lento, quando fosse lento e perché fosse lento. La differenza tra queste due situazioni è la differenza tra fare il pompiere (reagire quando brucia) e fare l'ingegnere (prevenire prima che bruci).
La soluzione tradizionale è uno stack di monitoring esterno: Prometheus per raccogliere le metriche, Grafana per visualizzarle, un exporter PHP custom per esporre i dati dell'applicazione, e un AlertManager per le notifiche. L'ho fatto molte volte, funziona, ma richiede infrastruttura aggiuntiva (un server dedicato al monitoring o almeno un container), configurazione non banale, e manutenzione continua. Per tre applicazioni di media complessità gestite da team PHP senza esperienza DevOps, era troppo. Laravel Pulse ha risolto il problema in venti minuti per applicazione: un pacchetto Composer, una migration, una riga di configurazione, e avevo una dashboard di monitoring nativa accessibile dal browser con metriche su richieste lente, query lente, job falliti, eccezioni ricorrenti, utilizzo cache e code asincrone. Non sostituisce un setup Prometheus/Grafana per infrastrutture complesse - ma copre l'80% delle domande operative quotidiane senza aggiungere un solo componente infrastrutturale.
Cosa ti dice davvero Laravel Pulse che i log non ti dicono?
I log applicativi ti dicono cosa è successo - ma non ti dicono quanto spesso succede, quanto è peggiorato rispetto alla settimana scorsa, e qual è l'impatto aggregato sul sistema. Pulse raccoglie metriche aggregate in tempo reale e le presenta in una dashboard che risponde a sei domande fondamentali per chi gestisce un'applicazione in produzione: quali sono le richieste HTTP più lente? Quali query SQL consumano più tempo? Quali job in coda falliscono più spesso? Quali eccezioni si ripetono? Quanta RAM e CPU consumano i server? Chi sta usando l'applicazione in questo momento?
Nel portale B2B, la dashboard Pulse mi ha rivelato nelle prime 24 ore di funzionamento tre problemi che erano completamente invisibili nei log: una query Eloquent nella pagina catalogo che impiegava 1.200 ms ma veniva chiamata solo 30 volte al giorno (abbastanza rara da non generare lamentele, abbastanza lenta da degradare l'esperienza per chi la eseguiva), un job di sincronizzazione magazzino che falliva silenziosamente al 3% delle esecuzioni (il catch generico nel job handler loggava l'errore ma non notificava nessuno), e un endpoint API che veniva chiamato 4.000 volte al giorno da un client esterno che il team non sapeva esistesse. Senza Pulse, questi tre problemi sarebbero rimasti invisibili per mesi - fino al giorno in cui la query da 1.200 ms sarebbe diventata una query da 12.000 ms sotto carico, il job avrebbe iniziato a fallire al 30% durante un picco, e il client esterno sconosciuto avrebbe saturato il rate limit inesistente. Nel mio profilo professionale trovi l'approccio alla consulenza che applico in questi scenari: identificare le criticità prima che diventino emergenze, con strumenti proporzionati alla complessità del sistema.
Installazione e configurazione: da zero a dashboard in 20 minuti
L'installazione di Pulse su un'applicazione Laravel 10+ è un singolo comando Composer più una migration:
# Installazione del pacchetto
composer require laravel/pulse
# Pubblica la migration e la configurazione
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
# Esegui la migration (crea la tabella pulse_*)
php artisan migrate
# Pubblica le viste per personalizzare la dashboard (opzionale)
php artisan vendor:publish --tag=pulse-dashboardLa dashboard è accessibile a /pulse e di default è protetta dal gate viewPulse che autorizza solo gli utenti in ambiente locale. In produzione, devi configurare il gate nel tuo AuthServiceProvider o nel file pulse.php:
// config/pulse.php oppure in AppServiceProvider
use Laravel\Pulse\Facades\Pulse;
Pulse::auth(function ($request) {
// Autorizza solo gli admin
return $request->user()?->is_admin === true;
});La configurazione di default è già sensata per la maggior parte dei casi d'uso: Pulse registra automaticamente le richieste HTTP con tempo superiore a 1000 ms, le query SQL sopra i 1000 ms, le eccezioni, i job in coda (dispatch, completamento, fallimento), l'utilizzo cache, e l'uso di DB::listen per tracciare tutte le query. I dati vengono salvati nella stessa istanza MySQL dell'applicazione (con retention di 7 giorni di default) e la dashboard si aggiorna in tempo reale via polling ogni 10 secondi. Su nessuna delle tre applicazioni ho notato un impatto misurabile sulle prestazioni dopo l'attivazione di Pulse: l'overhead è nell'ordine di 1-2 millisecondi per richiesta, trascurabile rispetto al tempo totale di elaborazione.
I recorder personalizzati: monitorare ciò che conta per il tuo business
La vera potenza di Pulse emerge quando vai oltre i recorder di default e inizi a monitorare metriche specifiche del tuo dominio di business. Pulse permette di creare recorder custom che tracciano qualsiasi metrica applicativa e la rendono visibile nella dashboard.
Nel gestionale interno, ho aggiunto un recorder custom per tracciare il tempo di generazione dei PDF di fattura - un'operazione che usava dompdf e che il team sospettava essere lenta, ma nessuno aveva mai misurato:
// app/Pulse/Recorders/PdfGenerationRecorder.php
namespace App\Pulse\Recorders;
use Laravel\Pulse\Facades\Pulse;
class PdfGenerationRecorder
{
public static function record(string $tipo, float $durata, int $pagine): void
{
Pulse::record(
type: 'pdf_generation',
key: $tipo,
value: $durata,
)->avg()->onlyBuckets();
}
}
// Utilizzo nel service di generazione PDF
$start = microtime(true);
$pdf = Pdf::loadView('fatture.template', $dati);
$pdf->save($path);
$durata = (microtime(true) - $start) * 1000;
PdfGenerationRecorder::record(
tipo: 'fattura',
durata: $durata,
pagine: $dati['righe']->count()
);Il risultato nella dashboard Pulse: un grafico che mostra il tempo medio di generazione PDF per tipo di documento, con trend settimanale. La scoperta è stata che le fatture con più di 50 righe impiegavano oltre 8 secondi - un tempo inaccettabile per l'utente che cliccava "Genera PDF" e restava in attesa. La soluzione è stata spostare la generazione in un job asincrono con notifica al completamento, riducendo il tempo percepito dall'utente a zero (il PDF viene generato in background e l'utente riceve un link per il download). Senza la metrica di Pulse, quel problema sarebbe rimasto una sensazione ("i PDF sono lenti") senza dati per quantificarlo e giustificare l'intervento.
Pulse vs Telescope vs Prometheus/Grafana: quando usare cosa
La domanda che mi fanno tutti i team è: "Ma allora non serve più Telescope? E Prometheus?" La risposta è che sono strumenti diversi per problemi diversi, e nel mio stack li uso in combinazione:
- Pulse è per il monitoring operativo quotidiano: dashboard always-on che mostra lo stato dell'applicazione, trend delle metriche, e alerting su anomalie. È lo strumento che il team guarda ogni mattina per capire se qualcosa è peggiorato rispetto a ieri. I dati sono aggregati e compressi (retention di 7 giorni, metriche aggregate per bucket temporale)
- Telescope è per il debugging in development e staging: traccia ogni singola richiesta, query, log, event, mail, notification. Produce una quantità enorme di dati (non adatto a produzione ad alto traffico) ed è lo strumento che usi quando devi capire perché una specifica richiesta ha impiegato 3 secondi. In produzione lo abilito solo temporaneamente, su richiesta, per debug mirati
- Prometheus/Grafana è per il monitoring infrastrutturale e cross-applicazione: metriche di CPU, RAM, disco, rete, stato dei servizi, custom metrics da più sorgenti. È lo strumento giusto quando gestisci molti server e hai bisogno di correlazione tra metriche applicative e metriche infrastrutturali. Lo installo quando l'infrastruttura cresce oltre il singolo VPS
Per le tre applicazioni dei miei clienti, Pulse da solo copre tutte le esigenze di monitoring applicativo. Telescope è installato solo in staging. Prometheus è riservato al cliente con l'e-commerce, che ha tre VPS e ha bisogno di vedere le metriche aggregate dell'infrastruttura. Ho documentato un approccio simile alla osservabilità minima per applicazioni PHP legacy con logging, metriche e alerting - un articolo che copre lo stesso principio applicato a codebase che non usano Laravel e non possono beneficiare di Pulse.
Alerting: dalla dashboard alla notifica
Pulse non include un sistema di alerting nativo nella versione base - la dashboard è pensata per essere consultata, non per inviare notifiche push. Per colmare questa lacuna, ho implementato un approccio semplice: un comando Artisan schedulato che ogni 5 minuti interroga le tabelle di Pulse e invia notifiche quando le metriche superano le soglie definite:
// Comando di alerting basato sui dati di Pulse
// Eseguito ogni 5 minuti dal task scheduler di Laravel
$slowRequests = DB::table('pulse_aggregates')
->where('type', 'slow_request')
->where('period', 60)
->where('aggregate', 'count')
->where('value', '>', 10)
->count();
if ($slowRequests > 0) {
Notification::route('telegram', config('pulse.alert_chat_id'))
->notify(new SlowRequestAlert($slowRequests));
}Sul portale B2B, le alert arrivano su un canale Telegram dedicato: "Attenzione: 14 richieste lente nell'ultima ora (soglia: 10)." Il team riceve la notifica, apre la dashboard Pulse, identifica l'endpoint problematico, e interviene prima che i clienti se ne accorgano. È un sistema minimale - non ha le correlazioni di PagerDuty né le escalation di OpsGenie - ma per una PMI con un team di tre sviluppatori che gestisce tre applicazioni, è esattamente il livello di sofisticazione necessario e sufficiente.
Ottimizzazione della retention e del database
Un aspetto che ho dovuto affrontare sull'e-commerce ad alto traffico riguarda l'impatto di Pulse sul database. Con 2.000 sessioni simultanee durante le campagne promozionali, Pulse genera un volume significativo di scritture sulle tabelle pulse_aggregates, pulse_entries e pulse_values. La retention di default di 7 giorni è ragionevole per applicazioni con traffico medio, ma sull'e-commerce produceva circa 2 GB di dati Pulse dopo una settimana - dati che vivono nella stessa istanza MySQL dell'applicazione e che competono per le risorse di I/O con le query di produzione.
La soluzione che ho adottato è duplice. Primo, ho ridotto la retention a 3 giorni per i dati ad alto volume (richieste HTTP e query SQL) mantenendo 7 giorni per i dati a basso volume (eccezioni e job):
// config/pulse.php - retention differenziata
'ingest' => [
'trim' => [
'lottery' => [1, 100], // 1% di probabilità di trim a ogni richiesta
],
],
// Comando di pulizia custom nel task scheduler
// Eseguito ogni notte alle 3:00
$schedule->command('pulse:purge --type=slow_request --hours=72')->dailyAt('03:00');
$schedule->command('pulse:purge --type=slow_query --hours=72')->dailyAt('03:05');Secondo, ho spostato le tabelle Pulse su una connessione database separata - un secondo database MySQL sullo stesso server, dedicato esclusivamente ai dati di monitoring. Questo isola completamente il carico I/O di Pulse dal database di produzione, eliminando ogni rischio di contention durante i picchi di traffico. La configurazione richiede una sola modifica nel file pulse.php:
// config/pulse.php - database dedicato
'storage' => [
'driver' => 'database',
'connection' => 'pulse', // connessione separata in config/database.php
],Dopo queste due ottimizzazioni, l'impatto di Pulse sulle prestazioni dell'e-commerce è diventato completamente trascurabile: le metriche di latenza delle query di produzione non mostrano alcuna differenza misurabile tra Pulse attivo e Pulse disattivato, nemmeno durante i picchi promozionali con 2.000 sessioni simultanee. Ho applicato lo stesso pattern di isolamento del database di monitoring anche su un'applicazione legacy di cui ho descritto il processo di audit tecnico nei primi 30 giorni, dove separare i dati operativi dai dati di business è una delle prime azioni di sanificazione architetturale.
L'investimento totale per l'attivazione di Pulse sulle tre applicazioni è stato di un'ora di lavoro effettivo: venti minuti per ciascuna (installazione, configurazione gate di accesso, verifica dashboard). Il ritorno è stato immediato: nella prima settimana ho identificato e risolto sei problemi di performance che il team non sapeva di avere, riducendo il tempo medio di risposta del portale B2B da 320 ms a 180 ms e dimezzando il tasso di fallimento dei job nel gestionale interno. Se gestisci applicazioni Laravel in produzione senza un sistema di monitoring applicativo, Pulse è il punto di partenza con il migliore rapporto costo/beneficio sul mercato - ed è gratuito. Se hai bisogno di aiuto per configurarlo correttamente, integrarlo con alerting personalizzato e costruire recorder custom per le metriche del tuo business, contattami per una sessione di setup: in due ore configuriamo Pulse, definiamo le soglie di alerting e costruiamo i recorder che ti servono per avere visibilità completa sulla tua applicazione.