PHP

Analizzare i log di errore di Apache con le espressioni regolari in PHP

calendar_today personTeam EGSOFT schedule5 min di lettura
info

Disclaimer: le informazioni pubblicate in questa sezione hanno finalità di pura divulgazione tecnica. EG Software S.r.l. non si assume alcuna responsabilità per un utilizzo improprio dei contenuti, né per eventuali danni diretti o indiretti derivanti dalla loro applicazione, e non garantisce l'aggiornamento, l'accuratezza o la completezza delle informazioni riportate. Prima di utilizzare in produzione codice o procedure qui descritte, verificane sempre l'adeguatezza al proprio contesto.

Quando qualcosa va storto su un sito in produzione, il log di errore di Apache è spesso il primo posto in cui guardare — ma leggerlo riga per riga, su un file che può contenere migliaia di voci, è lento e porta a perdere pattern ricorrenti. Un piccolo script PHP che struttura le righe del log in dati interrogabili rende molto più semplice individuare, ad esempio, quale file genera più errori o in quale fascia oraria si concentrano.

Il formato di una riga di log Apache

Una riga tipica del log di errore di Apache ha una struttura riconoscibile: timestamp tra parentesi quadre, livello di log, opzionalmente il PID del processo e l'IP del client, seguiti dal messaggio vero e proprio. Un'unica espressione regolare con gruppi di cattura può isolare tutte queste parti in un colpo solo:

<?php

declare(strict_types=1);

function analizzaRigaLog(string $riga): ?array
{
    $pattern = '/\[([^\]]+)\] \[([^\]]+)\] (?:\[pid ([^\]]+)\] )?(?:\[client ([^\]]+)\] )?(.+)/';

    if (!preg_match($pattern, $riga, $m)) {
        return null; // riga che non rispetta il formato atteso: la si scarta
    }

    return [
        'timestamp' => $m[1],
        'livello'   => $m[2],
        'pid'       => $m[3] ?? '',
        'client_ip' => $m[4] ?? '',
        'messaggio' => $m[5],
    ];
}

I due gruppi opzionali per PID e IP client — marcati con (?:...)? — sono importanti: non tutte le righe di log li includono, e senza renderli facoltativi il pattern fallirebbe su una parte consistente del file.

Normalizzare il timestamp con un fallback

Il formato data di Apache (es. Wed Nov 19 07:47:48.164731 2025) include a volte i microsecondi, che DateTime di PHP non sempre digerisce allo stesso modo su tutte le versioni. Conviene rimuoverli con una piccola sostituzione prima del parsing, e prevedere comunque un fallback su strtotime() per non perdere la riga se il formato varia leggermente:

function normalizzaTimestamp(string $timestamp): ?DateTimeImmutable
{
    $pulito = preg_replace('/\.\d+/', '', $timestamp) ?? $timestamp;

    try {
        return new DateTimeImmutable($pulito);
    } catch (Exception) {
        $unix = strtotime($pulito);
        return $unix !== false ? (new DateTimeImmutable())->setTimestamp($unix) : null;
    }
}

Classificare gli errori per gravità

Non tutte le righe hanno lo stesso peso: un PHP Deprecated non merita la stessa attenzione di un PHP Fatal error. Una funzione di classificazione, basata su un controllo testuale sul livello di log e sul messaggio, permette di ordinare o filtrare rapidamente le voci più critiche:

function determinaGravita(string $livello, string $messaggio): string
{
    $messaggioMinuscolo = strtolower($messaggio);

    if (str_contains($livello, 'error') || str_contains($messaggioMinuscolo, 'fatal')) {
        return 'alta';
    }
    if (str_contains($livello, 'warn')) {
        return 'media';
    }
    return 'bassa';
}

Aggregare le statistiche mentre si legge il file

Per un file di log corposo conviene leggerlo riga per riga con un file handle (come nell'articolo sull'import CSV) invece di caricarlo tutto in memoria, aggiornando contatori aggregati mano a mano:

$statistiche = ['per_gravita' => [], 'per_file' => []];

$handle = fopen($percorsoLog, 'rb');
while (($riga = fgets($handle)) !== false) {
    $voce = analizzaRigaLog($riga);
    if ($voce === null) {
        continue;
    }

    $gravita = determinaGravita($voce['livello'], $voce['messaggio']);
    $statistiche['per_gravita'][$gravita] = ($statistiche['per_gravita'][$gravita] ?? 0) + 1;
}
fclose($handle);

Con questi pochi elementi — un pattern di parsing, una normalizzazione robusta della data e una classificazione semplice — si ottiene uno strumento diagnostico che trasforma un file di testo grezzo in un piccolo cruscotto di errori, utile prima ancora di collegare un sistema di log aggregation più strutturato.

Altri articoli su PHP grid_viewTutte le tematiche
Scrivici su WhatsApp