Monolog

By | 22 novembre 2013

Je travaille actuellement sur la refonte de mon framework (https://github.com/GuillaumeDievart/Wise). Arrivé au composant logger, j’ai remis en question sa raison d’être ayant connaissance de l’existance de Monolog. Monolog respecte le standard PSR-3, standard que je voulais mettre en place pour le mien. Je vais donc arrêter mes développements et aller jeter un oeil à cette fameuse librairie, et je vais en profiter pour vous partager mon experience.

Installation

Comme énormément de projets récents de PHP, il est possible d’installer cette librairie via composer, alors je ne vais pas me géner. Pour les malheureux qui ne connaissent pas en quoi consiste composer je vous renvoie vers le site officiel: http://getcomposer.org/.

{
    "require": {
        "monolog/monolog": "~1.5"
    }
}

Premier test

Alors pour ce premier test je n’ai pas cherché bien loin, j’ai copié collé l’exemple donné sur github:

<?php 
require __DIR__.'/vendor/autoload.php'; 

use Monolog\Logger; 
use Monolog\Handler\StreamHandler; 

$log = new Logger('TEST'); 
$log->pushHandler(new StreamHandler('/tmp/monolog.log', Logger::DEBUG));

$log->addDebug('Foo');

Et voici la sortie:

[2013-11-22 22:28:42] TEST.DEBUG: Foo [] []

La sortie ne me convient pas spécialement au niveau du format, et à quoi correspondent ces crochets en fin de ligne ?

Formatage de la sortie

Il est évidement possible de personnaliser le format de sortie de Monolog. Pour se faire, il suffit de faire appel aux classes Formatter, en fournissant le format de sortie souhaité. Une fois le format configuré il suffit d’injecter notre objet Formatter à notre Handler via la méthode setFormatter.

Notre code donne maintenant ceci:

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;

$dateFormat = "Y-m-d H:i:s";
$output = "[%datetime%] [%channel%] [%level_name%] %message% %context% %extra%\n";
$formatter = new LineFormatter($output, $dateFormat);

$streamHandler = new StreamHandler('/tmp/monolog.log', Logger::DEBUG);
$streamHandler->setFormatter($formatter);

$log = new Logger('TEST');
$log->pushHandler($streamHandler);                                                                    

$log->addDebug('message de la part de Guillaume');

Voici la sortie

[2013-11-22 22:56:54] [TEST] [DEBUG] message de la part de Guillaume [] []

Les champs utilisables sont:

%datetime%   => Correspond à la date d'ajout de log
%channel%    => Correspond au channel du log (TEST dans notre exemple)
%level_name% => Le niveau du log (DEBUG, ERROR ...)
%message%    => Le message
%context%    => Ah le contexte, la réponse à ces fameux crochets :p
%extra%      => Pour le moment aucune idée de son utilité.

Il existe des Formatter Html, JSon ….

A vous de choisir maintenant le format qui vous conviendra le mieux.

Contexte

Dans la paragraphe précédent, j’ai levé le mystère sur le premier couple de crochet vide, en annoçant qu’il s’agissait du contexte. Le contexte va vous permettre simplement d’ajouter des informations concernant l’état du script ou les valeurs de certaines variables par exemple.

Dans l’exemple ci-dessous, je vais récupérer le fichier qui a généré le log et la ligne:

$log->addDebug('message de Guillaume', array(__FILE__.' on line '.__LINE__));

La sortie

[2013-11-22 23:46:48] [TEST] [DEBUG] message de Guillaume ["/opt/hosting/monolog/index.php on line 26"] []

Extra

Les extra sont la réponse aux derniers crochets vide \o/.

Il est possible avec Monolog, de faire appel à des méthodes à chaque enregistrement afin d’ajouter des informations communes à tout nos enregistrements.

Dans l’exemple suivant, je vais ajouter pour chaque enregistrement le load average de la machine.

$log->pushProcessor(function ($record) {
    $record['extra']['load_avg'] = sys_getloadavg();

    return $record;
});
$log->addDebug('message de Guillaume', array(__FILE__.' on line '.__LINE__));

La sortie:

[2013-11-22 23:57:42] [TEST] [DEBUG] message de Guillaume ["/opt/hosting/monolog/index.php on line 25"] {"load_avg":[0.5,0.56,0.57]}

La différence entre le contexte et les informations extra sont que ces dernières vont être identiques pour n’importe quel type de log, et depuis n’importe quel contexte. Ce qui permet de faire appel à une même méthode pour l’ensemble des logs. Cette méthode pourra donc vous suivre dans l’ensemble de vos projets sans devoir ajouter ces infos au niveau de l’appel au logger.

Dans l’exemple ci-dessus j’ai utilisé une closure pour ajouter des informations à l’enregistrement. Mais j’aurai très bien pu faire appel à n’importe quelle classe ou fonction.

Voici un exemple

class CommonLog
{
    public static function addExtra($record)
    {
        $record['extra']['load_avg'] = sys_getloadavg();

        return $record;
    }
}
$log->pushProcessor(array('CommonLog', 'addExtra'));                                                  

$log->addDebug('message de Guillaume', array(__FILE__.' on line '.__LINE__));

Processor

Les processor, ou comment faciliter l’ajout d’informations extra à nos enregistrements :).

Juste au dessus, je vous ai présenter les extra, et comment ajouter des informations communes à tout vos enregistrements en faisant appel à une fonction. Mais Monolog à penser à tout (enfin presque loadAvg :p), et nous offre une panoplie de classes qui va nous mâcher le boulot.

Avant même de vous présenter l’ensemble des Processor disponibles, je veux vous montrer la simplicité d’utilisation:

$log->pushProcessor(new \Monolog\Processor\WebProcessor());

$log->addDebug('message de la part de Guillaume', array(__FILE__.' on line '.__LINE__));

Bon là vous allez me dire, ok mais ça fait quoi ??!!

\Monolog\Processor\IntrospectionProcessor()

Introspection permet d’obtenir le fichier, la méthode et la ligne ayant généré l’enregistrement. Alors par contre celui-ci je vous le déconseille fortement en production. Tout simplement car il utilise la fonction debug_backtrace qui risque d’avoir un coût énorme sur les perfomances de votre application si vous en abusez.

\Monolog\Processor\MemoryUsageProcessor()

Retourne la quantité de mémoire alloué au moment de l’appel.

\Monolog\Processor\MemoryPeakUsageProcessor()

Retourne la quantité de mémoire maximum alloué à votre script.

\Monolog\Processor\PsrLogMessageProcessor()

Permet d’injecter des variables dans les messages, qui seront remplacées par les valeurs du contexte.

Un petit exemple pour celui ci:

$log->pushProcessor(new \Monolog\Processor\PsrLogMessageProcessor());

$log->addDebug('message de la part de {prenom}', array('prenom' => 'Guillaume'));

\Monolog\Processor\UidProcessor()

Crée un id unique pour l’instance du Logger.

\Monolog\Processor\ProcessIdProcessor()

Retourne le PID.

\Monolog\Processor\GitProcessor()

Alors je trouve celui-ci assez original, il va vous retourner l’identifiant du commit et la branche ayant généré l’enregistrement.

\Monolog\Processor\WebProcessor()

Et le dernier, pour les interfaces web. Celui va retourner l’url, la méthode http utilisé, l’adresse IP … Enfin les informations les plus importantes provenant de $_SERVER.

Handler

Nous n’avons pas encore parlé des Handler, ou alors très briévement. Les Handler correspondent à la manière dont seront stockés vos enregistrements. En régle générale, les logs sont stockés dans des fichiers, qui sont très fastidieux à analyser lorsque qu’ils commencent à contenir plusieurs centaines de milliers de lignes, voir millions (!hadoo:p) !

Les logs peuvent aussi très bien servir à faire autre chose que donner des informations sur l’état de notre application. On peut très bien imaginer les exploiter pour obtenir des statistiques sur les visites de notre site, ou suivre le comportement d’un utilisateur.

Bon tout ça pour dire qu’avec Monolog il est très simple de stocker nos enregistrement sur des serveurs tel que ElasticSearch, Redis ou encore Syslog. Au delà du stockage, il est aussi possible de se servir d’un Handler comme système d’alerting pour envoyer des mails, ou des sms (pour le coup ça vous coutera un peu de développement).

Exemple d’utilisation de NativeMailerHandler:

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\NativeMailerHandler;
use Monolog\Formatter\LineFormatter;

$dateFormat = "Y-m-d H:i:s";
$output = "[%datetime%] [%channel%] [%level_name%] %message% %context% %extra%\n";
$formatter = new LineFormatter($output, $dateFormat);

$streamHandler = new StreamHandler('/tmp/monolog.log', Logger::DEBUG);
$streamHandler->setFormatter($formatter);                                                             

$mailerHandler = new NativeMailerHandler('to@nsa.gov', 'ALERTE', 'from@prism.gov', Logger::ERROR);
$mailerHandler->setFormatter($formatter);

$log = new Logger('TEST');
$log->pushHandler($streamHandler);
$log->pushHandler($mailerHandler);

$log->addError('PRISM');

 Conclusion

Je pense avoir fait le tour des principales fonctionnalités que propose Monolog, en tout cas celles qui m’intéressent :)

Pour ma part il est adopté.

La partie que j’ai le moins couvert dans cet article est celle des Handler. Mais sachez qu’il est possible d’interfacer Monolog avec pas mal de système, tel que Chrome, FirePHP, MongoDB, DynamoDB, CouchDB … … …

Lien: https://github.com/Seldaek/monolog

Guillaume Dievart

Leader technique chez BoostMyShop
Passionné par le développement web, et plus particulièrement par le langage PHP et les systèmes de gestion de données SQL / NoSQL.

Les derniers articles par Guillaume Dievart (tout voir)

Category: Php

3 thoughts on “Monolog

  1. Christophe Coevoet

    Ce post explique très bien comment utiliser Monolog.

    Je ferais juste une amélioration: dans la contrainte composer, utiliser « dev-master » est une mauvaise idée:

    - cela interdit à composer d’utiliser les versions stables de Monolog
    - cela va forcer les utilisateurs de votre framework à autoriser les versions dev de Monolog dans leur projet (sinon, ils recevront une erreur de la part de composer). Seul le projet lui-même peut autoriser Composer à installer des versions non-stable.
    - cela interdit de faire des releases (propres) de votre propre framework, car une release dirait qu’elle est compatible avec Monolog dev-master, y compris si Monolog dev-master est réécrit entièrement dans une future version majeure. Une dépendance sur « dev-master » est en réalité une dépendance sur une version +Inf du paquet.

    Une bien meilleure contrainte serait par exemple « ~1.5″ (qui autorise toutes les versions 1.x de Monolog à partir de 1.5)

    Reply
  2. Pingback: Guzzle | Guillaume Dievart - Développeur php

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *