Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Log delle informazioni in PHP

Descrizione del modulo Zend_Log: la soluzione dello Zend Framework per monitorare le azioni dei nostri script PHP
Descrizione del modulo Zend_Log: la soluzione dello Zend Framework per monitorare le azioni dei nostri script PHP
Link copiato negli appunti

Più è complessa un'applicazione più risulta necessario poter conoscere per filo e per segno le operazioni che hanno portato all'esecuzione di determinati errori. Più è grande la necessità di ottenere informazioni su chi ha svolto determinate azioni più risulta necessario studiare un sistema che archivi ogni singola operazione dell'utente da controllare. Per entrambi questi motivi e per ogni altra situazione in cui risulti necessario archiviare un grosso quantitativo di informazioni relative al flusso logico ed alle operazioni svolte dal proprio sistema sono state sviluppate delle librerie specifiche. 

L'operazione in questione è chiamata logging ed in PHP può essere effettuata in diversi modi, appoggiandosi a librerie più o meno complete o sviluppando dei semplici sistemi personali. In questa sede analizzeremo il modulo Zend_Log dello Zend Framework che si propone come ottima libreria per il logging con PHP fornendo il supporto per un'ampia gamma di soluzioni.

Il modulo Zend_Log

Il modulo Zend_Log espone una classe principale con lo stesso nome del modulo tramite la quale è possibile effettuare il logging relativo alle informazioni di esecuzione della propria applicazione. La classe Zend_Log espone solamente una serie di metodi statici che operano su degli adapter che si occupano di salvare i messaggi su diverse tipologie di destinazioni. Gli adapter sono delle classi che implementano l'interfaccia Zend_Log_Adapter_Interface; di default lo Zend Framework espone i seguenti adapter:

  • Zend_Log_Adapter_Console: esegue il log dei messaggi scrivendo direttamente sullo standard output impostato per PHP. Viene utilizzato direttamente il costrutto echo ed è possibile impostare come opzione format, che definisce il formato (format) del messaggio di log che si intende utilizzare;
  • Zend_Log_Adapter_Db: esegue il log dei messaggi scrivendo all'interno di un database. Accetta come opzioni il nome del campo che conterrà il livello del messaggio di log (fieldLevel) ed il nome del campo che conterrà il messaggio stesso (fieldMessage). In caso non siano specificati vengono utilizzati level e message rispettivamente;
  • Zend_Log_Adapter_File: esegue il log dei messaggi scrivendo su un file. Tramite le opzioni è possibile impostare la proprietà buffer per decidere se utilizzare il buffering o meno, specificare il numero di linee da mantenere in memoria prima di effettuare il flush (bufferLines) il formato del messaggio (format) e decidere se mantenere aperto il file tra le singole operazioni di scrittura o no (keepOpen);
  • Zend_Log_Adapter_Null: non esegue alcun log;

Inizializzare ed utilizzare la libreria Zend_Log è molto semplice:

<?php

require_once 'Zend/Log.php';

/*   È bene ricordarsi di includere tutti gli altri adapter che ci serviranno
*    per effettuare il logging corretto della nostra applicazione.
*/

require_once 'Zend/Log/Adapter/Console.php';

$console = new Zend_Log_Adapter_Console;
$console->setOption('format', "[%logname% - %level%] %message%");

// Registriamo l'adapter

Zend_Log::registerLogger($console);

Zend_Log::log('Un messaggio di log a cui sarà impostato un livello di default (Zend_Log::LEVEL_DEFAULT)');
Zend_Log::log('Un messaggio di log a cui impostiamo manualmente il livello', Zend_Log::LEVEL_SEVER);
?>

Una volta incluso il modulo e tutti gli adapter necessari, si procede con l'inizializzazione dei singoli adapter e l'eventuale impostazione di opzioni. Successivamente si registra l'adapter ed è possibile procedere con il logging dei messaggi all'interno del nostro flusso applicativo. Sarà la classe Zend_Log ad occuparsi di inviare correttamente i messaggi a tutti gli adapter registrati. Il logging di un messaggio viene effettuato attraverso la funzione Zend_Log::log che accetta, oltre al messaggio da loggare, anche il livello del messaggio, un array di valori utilizzabili all'interno del formato del messaggio ed il nome del log. Gli ultimi due parametri sono molto utili nel caso si desideri scrivere qualche funzione di logging personalizzata:

<?php

require_once 'Zend/Log.php';
require_once 'Zend/Log/Adapter/Console.php';

class CustomAdapter extends Zend_Log_Adapter_Console
{
    public function write($fields)
    {
        $fields['time'] = date('%d%m%y %h%i%s');
        return parent::write($fields);
    }
}

function custom_log()
{
    $args = func_get_args();
    $callback = array('Zend_Log', 'log');

    // Sostituiamo il nome del log
    switch(func_num_args())
    {
        case 1:
            $args[] = Zend_Log::LEVEL_DEBUG;
            
        case 2:
            $args[] = 'Custom Logger';
        break;
        
        case 3:
        case 4:
            $args[count($args) - 1] = 'Custom Logger';
        break;
    }
    return call_user_func_array($callback, $args);
}

$custom_adapter = new CustomAdapter;
$custom_adapter->setOption('format', "[%logname% - %level% -%time%] %message%");

// Registriamo l'adapter
Zend_Log::registerLogger($custom_adapter);

custom_log('Primo');
sleep(5);
custom_log('Secondo');
Zend_Log::log('Terzo');

?>

Nell'esempio ho definito un adapter che aggiunge la data ed il tempo in cui viene prodotto il messaggio ai parametri del formato. Ho scritto anche una semplice funzione di log che sostituisce il nome del log con Custom Logger. I livelli di logging supportati nativamente dal sistema sono i seguenti, ordinati per gravità:

  • LEVEL_DEBUG (1);
  • LEVEL_INFO (2);
  • LEVEL_WARNING (4);
  • LEVEL_ERROR (8);
  • LEVEL_SEVERE (16);

Il codice di errore è sempre una potenza di due, quindi è possibile utilizzare le operazioni di bit per specificare quali errori il sistema può accettare.

LEVEL_ALL include in un colpo solo tutti gli errori:

<?php

// ...

Zend_Log::setLevel(Zend_Log::LEVEL_INFO);
Zend_Log::log('Il messaggio non viene loggato');

Zend_Log::setMask(Zend_Log::LEVEL_INFO | Zend_Log::LEVEL_DEBUG);
Zend_Log::log('Il messaggio viene loggato');
Zend_Log::log('Anche questo', Zend_Log::LEVEL_INFO);

?>

Logging verso destinazioni multiple

Come accennato precedentemente il sistema è in grado di supportare il logging simultaneo su diversi adapter. In questo modo è possibile, estendendo gli adapter o implementandone di nuovi, riempire tutte le esigenze necessarie per applicazioni specifiche. Per esempio si potrebbe sviluppare un adapter che effettui il logging solo di determinati errori su file, ed un altro che effettui il logging di altri errori su database; oppure un adapter che invii una mail all'amministratore in caso di errore severo riscontrato per la prima volta in una giornata. Vediamo un esempio:

<?php

require_once 'Zend/Log.php';
require_once 'Zend/Log/Adapter/File.php';
require_once 'Zend/Log/Adapter/Interface.php';

class AdapterDebug extends Zend_Log_Adapter_File
{
    function write($fields)
    {
        if($fields['level'] <= Zend_Log::LEVEL_WARNING)
            return parent::write($fields);
        return false;
    }
}

/*    CREATE TABLE severe_errors(
*        id UNSIGNED INT AUTO_INCREMENT,
*        level UNSIGNED INT NOT NULL,
*        message VARCHAR(255) NOT NULL,
*        creation_date DATETIME NOT NULL,
*
*        PRIMARY KEY(id)
*    );
*/

class SevereMailAdapter extends Zend_Log_Db_Adapter
{
    function write($fields)
    {
        if($fields['level'] != Zend_Log::LEVEL_SEVERE)
            return false;

        $sql = $this->_dbAdapter->quoteInto(
                "SELECT 1 FROM ? WHERE message = ? AND DATEDIFF(creation_date, CURDATE()) < 1",
                $this->_tableName,
                $fields['message']
                );
        $result = $this->_dbAdapter->query($sql);
        if($result->rowCount() == 0)
        {
            $this->_sendMail();
        }
        
        $fields['creation_date'] = date('%Y-%m-%d %h:%i:%s');
        return parent::write($fields);
    }

    private function _sendMail()
    {
        // ....
    }
}

Zend_Log::registerLogger(new AdapterDebug('/logs/test_log.txt'), 'File');
Zend_Log::registerLogger(new SevereMailAdapter(/* parametri di connessione ... */), 'Severe');
//Zend_Log::registerLogger(new Zend_Log_Adapter_Null);

?>

Quando vorremo disabilitare il logger basterà decommentare la registrazione dell'adapter Null e commentare le altre. Come potete vedere seguendo delle semplici regole implementative è possibile adattare il comportamento del modulo alle proprie esigenze permettendoci di raggiungere buoni livelli di produttività in breve tempo. Purtroppo il modulo funziona solamente integrato ad altre funzionalità rese disponibili dal framework, quindi risulta difficile utilizzarlo all'esterno dell'ambiente con cui viene distribuito.

Conclusione

Abbiamo visto la soluzione proposta dallo Zend Framework per eseguire il logging delle informazioni. Il modulo Zend_Log non è comunque l'unica soluzione attuabile, vi sono altre librerie che svolgono funzionalità simili e c'è sempre spazio per implementare qualcosa di più adatto alle proprie esigenze. Però il modulo Zend_Log è abbastanza versatile e modulare da risultare adatto a quasi tutte le situazioni lavorative ed implementative. Può essere utilizzato anche per effettuare il logging delle operazioni interne dello Zend Framework, quindi è interessante anche solamente dal punto di vista di utilità per lo studio del funzionamento del sistema.

Ti consigliamo anche