Introduzione
Quando un’applicazione PHP cresce, gestire gli errori con semplici echo o var_dump non basta più. In un ambiente reale, soprattutto su server Linux o in contesti con più servizi, è molto più utile inviare gli errori a un sistema di logging centralizzato come syslog. Questo approccio permette di raccogliere messaggi di errore in modo standard, consultarli facilmente e integrarli con gli strumenti di monitoraggio del sistema.
In questo tutorial vedremo come intercettare errori, eccezioni e problemi fatali in PHP e scriverli su syslog. Il vantaggio principale è la separazione tra log applicativi e output dell’applicazione: l’utente finale non vede dettagli tecnici, mentre lo sviluppatore ha comunque accesso a informazioni utili per il debug e la manutenzione.
Il sotto-argomento scelto è pratico perché syslog è molto usato in produzione, ma spesso trascurato da chi si limita al logging su file. Inoltre, imparare a usarlo aiuta a comprendere meglio il flusso di gestione degli errori in PHP.
Codice completo
<?php
declare(strict_types=1);
/**
* Esempio di gestione errori in PHP con logging su syslog.
* - intercetta errori PHP
* - intercetta eccezioni non gestite
* - intercetta errori fatali in chiusura
* - invia tutto a syslog con un formato leggibile
*/
// Configurazione base del logger
openlog(´mia_app_php´, LOG_PID | LOG_ODELAY, LOG_USER);
/**
* Converte un livello di errore PHP in una stringa leggibile.
*/
function errorLevelToString(int $severity): string
{
return match ($severity) {
E_ERROR => ´E_ERROR´,
E_WARNING => ´E_WARNING´,
E_PARSE => ´E_PARSE´,
E_NOTICE => ´E_NOTICE´,
E_CORE_ERROR => ´E_CORE_ERROR´,
E_CORE_WARNING => ´E_CORE_WARNING´,
E_COMPILE_ERROR => ´E_COMPILE_ERROR´,
E_COMPILE_WARNING => ´E_COMPILE_WARNING´,
E_USER_ERROR => ´E_USER_ERROR´,
E_USER_WARNING => ´E_USER_WARNING´,
E_USER_NOTICE => ´E_USER_NOTICE´,
E_STRICT => ´E_STRICT´,
E_RECOVERABLE_ERROR => ´E_RECOVERABLE_ERROR´,
E_DEPRECATED => ´E_DEPRECATED´,
E_USER_DEPRECATED => ´E_USER_DEPRECATED´,
default => ´UNKNOWN_ERROR´,
};
}
/**
* Scrive un messaggio su syslog con contesto utile.
*/
function logToSyslog(string $level, string $message, array $context = []): void
{
$contextString = $context ? json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : ´{}´;
$finalMessage = sprintf(´[%s] %s | context=%s´, $level, $message, $contextString);
// In un progetto reale potresti scegliere livelli diversi
syslog(LOG_ERR, $finalMessage);
}
/**
* Gestore degli errori PHP.
* Converte errori non fatali in log strutturati.
*/
set_error_handler(function (int $severity, string $message, string $file, int $line): bool {
$levelName = errorLevelToString($severity);
logToSyslog(´PHP_ERROR´, $message, [
´severity´ => $levelName,
´file´ => $file,
´line´ => $line,
´request_uri´ => $_SERVER[´REQUEST_URI´] ?? null,
´user_agent´ => $_SERVER[´HTTP_USER_AGENT´] ?? null,
]);
// Restituiamo true per indicare che l´errore è stato gestito
return true;
});
/**
* Gestore delle eccezioni non catturate.
*/
set_exception_handler(function (Throwable $exception): void {
logToSyslog(´UNCAUGHT_EXCEPTION´, $exception->getMessage(), [
´type´ => get_class($exception),
´file´ => $exception->getFile(),
´line´ => $exception->getLine(),
´trace´ => $exception->getTraceAsString(),
´request_uri´ => $_SERVER[´REQUEST_URI´] ?? null,
]);
http_response_code(500);
echo ´Si è verificato un errore interno.´;
});
/**
* Gestore degli errori fatali alla chiusura dello script.
*/
register_shutdown_function(function (): void {
$lastError = error_get_last();
if ($lastError !== null) {
$fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR];
if (in_array($lastError[´type´], $fatalTypes, true)) {
logToSyslog(´FATAL_ERROR´, $lastError[´message´], [
´type´ => errorLevelToString($lastError[´type´]),
´file´ => $lastError[´file´],
´line´ => $lastError[´line´],
]);
}
}
closelog();
});
/**
* Simulazione di un warning controllato.
*/
function divide(int $a, int $b): float
{
if ($b === 0) {
trigger_error(´Divisione per zero non consentita´, E_USER_WARNING);
return 0.0;
}
return $a / $b;
}
// Esempio di utilizzo
echo ´Risultato: ´ . divide(10, 0) . PHP_EOL;
// Esempio di eccezione non gestita
throw new RuntimeException(´Eccezione di prova non gestita´);
Spiegazione
Il codice mostrato implementa una strategia completa di gestione degli errori. Vediamo i pezzi principali uno per uno.
1. Apertura del canale syslog
La funzione openlog() inizializza il collegamento con il sistema di logging. Il primo parametro è il nome dell’applicazione, utile per riconoscere i messaggi nei log di sistema. Le opzioni LOG_PID e LOG_ODELAY aggiungono rispettivamente il PID del processo e un comportamento di apertura ritardata del log. LOG_USER indica la facility, cioè la categoria di sistema usata per i messaggi.
2. Conversione del livello di errore
La funzione errorLevelToString() trasforma i codici numerici di PHP in etichette leggibili. Questo è utile perché i numeri da soli sono poco chiari nei log. Ad esempio, invece di memorizzare solo E_WARNING come valore numerico, salvare la stringa rende più immediata l’analisi.
3. Funzione di logging centralizzata
logToSyslog() costruisce un messaggio uniforme. Inserisce il livello, il messaggio e un contesto in formato JSON. Questa è una buona pratica perché i log strutturati sono più facili da cercare e analizzare. Il contesto può contenere informazioni come file, linea, URL richiesta o user agent.
4. Gestione degli errori non fatali
Con set_error_handler() intercettiamo warning, notice e altri errori gestibili. La callback riceve severità, messaggio, file e linea. Nel nostro esempio trasformiamo tutto in un evento di log e restituiamo true, segnalando a PHP che l’errore è stato preso in carico.
Questo approccio è molto utile per evitare che warning ripetuti finiscano direttamente nell’output HTTP, cosa che in produzione è quasi sempre da evitare.
5. Gestione delle eccezioni non catturate
Con set_exception_handler() intercettiamo le eccezioni che non sono state gestite nel flusso normale. Qui scriviamo nel log il tipo dell’eccezione, il messaggio, il file, la linea e lo stack trace. Dopo il logging, restituiamo all’utente un messaggio generico, senza esporre dettagli sensibili.
6. Gestione dei fatal error
Gli errori fatali non possono essere gestiti come i warning, ma possiamo intercettarli in fase di shutdown con register_shutdown_function(). Dentro la funzione controlliamo error_get_last() e verifichiamo se l’ultimo errore appartiene a una categoria fatale. Se sì, lo inviamo a syslog prima di chiudere il processo.
Questo è importante perché alcuni errori, come un parse error o un fatal error, altrimenti rischierebbero di non lasciare traccia applicativa utile.
Best practice
- Non mostrare dettagli tecnici all’utente finale: i messaggi completi devono andare nel log, mentre all’utente va mostrato un errore generico.
- Usa log strutturati: includere contesto in JSON rende la ricerca e l’analisi molto più efficaci.
- Separa ambiente di sviluppo e produzione: in locale puoi mantenere error_reporting alto, ma in produzione meglio loggare senza esporre output diretto.
- Evita di loggare dati sensibili: mai inserire password, token o dati personali nei log.
- Gestisci anche gli errori fatali: non limitarti a warning ed eccezioni, perché i problemi più gravi spesso emergono solo in shutdown.
- Chiudi sempre il canale con closelog(): è una buona abitudine per liberare correttamente le risorse.
- Usa livelli coerenti: non tutto è un errore critico. Alcuni eventi possono essere warning, notice o informational in base al contesto.
Riepilogo
Il logging su syslog è una soluzione robusta per la gestione degli errori in PHP, soprattutto in ambienti server e applicazioni in produzione. Con poche funzioni native puoi intercettare errori, eccezioni e problemi fatali, centralizzando la diagnosi in un unico flusso di log.
Abbiamo visto come:
- inizializzare syslog con openlog();
- gestire gli errori PHP con set_error_handler();
- catturare le eccezioni con set_exception_handler();
- intercettare i fatal error con register_shutdown_function();
- scrivere messaggi coerenti e leggibili per il debug.
Se vuoi migliorare ulteriormente il sistema, puoi integrare questo approccio con un framework di logging più avanzato, oppure inviare i messaggi a un collector centralizzato. Ma come base, syslog resta una scelta solida, semplice e molto efficace.
Approfondisci con risorse ufficiali
- PHP Manual - error handling: documentazione ufficiale sulla gestione degli errori in PHP.
- PHP Manual - set_error_handler(): dettagli su come intercettare errori personalizzati.
- PHP Manual - set_exception_handler(): gestione delle eccezioni non catturate.
- PHP Manual - register_shutdown_function(): uso della funzione di shutdown per intercettare errori fatali.
- PHP Manual - syslog() e openlog(): funzioni native per l’invio dei log al sistema.
- Documentazione Linux syslog: utile per capire come i messaggi vengono raccolti dal sistema operativo.
