Author Archives: Guillaume Dievart

About Guillaume Dievart

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.

ELK Magento

elk

Dans cet article je vais vous montrer comment exploiter la pile ELK (elasticsearch, logstash et kibana) pour monitorer les performances de magento sans trop d’effort.

Je ne vais pas m’étendre sur les détails de ces 3 logiciels, mais simplement vous décrire en quelques mots leur principal rôle. Continue reading

Les normes de codage ça sert à rien

psrJe suis développeur depuis quelques années maintenant,et beaucoup des collègues avec qui j’ai pu travailler me considèrent comme quelqu’un de chiant,trop exigeant.D’après eux je perd mon temps sur des choses sans intérêt,comme le respect des normes de codage, la beauté d’un code.Pour eux l’essentiel pour un projet c’est qu’il fonctionne. Continue reading

Elasticsearch | river-jdbc incrémental

elastic-searchJe travaille depuis une semaine sur la mise en place d’Elasticsearch en tant que data warehouse, et son exploitation depuis Kibana.

J’ai donc besoin d’indexer un certain nombre de documents provenant de source différentes. Comme beaucoup, 90% de mes données sont actuellement stockées dans des tables MySQL. Certaines d’entre elles contiennent pas loin de 100 millions d’enregistrements, et sont très souvent actualisées. Continue reading

Elasticsearch en 10min

elastic-searchCette semaine, une mission qui m’a été confiée, était de trouver une solution pour catégoriser de manière automatique des articles. Je me suis donc naturellement tourné vers Elasticsearch, produit que je connais uniquement de nom car très populaire en ce moment.

Je pars donc à la découverte d’Elasticsearch.

Introduction

Pour expliquer de manière simple, Elasticsearch est un moteur de recherche open source.

Avant de partir tête baissée dans le code, il y a une chose à savoir. ES classe les documents par Index et par Type. Les Index peuvent être comparés à la base de données pour un SGDR, et le Type à une table.

Je ne vais pas rentrer dans les détails de ce qu’est un Index ou un Type. Premièrement car mes connaissances de ce serveur sont très limitées, et surtout le but de cet article est uniquement de montrer la simplicité de mise en place d’un tel moteur.

Installation d’Elasticsearch

Commençons par son installation (serveur Debian).

Avant tout chose, il faut que Java soit installé sur notre serveur pour faire fonctionner Elasticsearch.

$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java7-installer

Maintenant que Java est installé nous pouvons installer ES.

$ wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.7.deb
$ dpkg -i elasticsearch-0.90.7.deb
$ update-rc.d elasticsearch defaults
$ /etc/init.d/elasticsearch start

API PHP

Elasticsearch fournit un client PHP, et vu qu’ils ont bien fait les choses nous pouvons l’installer avec composer:

{
    "require": {
        "elasticsearch/elasticsearch": "~0.4"
    }
}

Indexation

Nous voilà donc prêt à jouer avec Elasticsearch. La première étape va être l’indexation de documents.

$params = array();

$client = new Elasticsearch\Client();

$article['index'] = 'blog';
$article['type']  = 'article';
$article['ttl']   = 10;
$article['body']  = array(
    'title' => 'Premier pas avec ES',
    'desc' => 'ES est un moteur de recherche simple à mettre en place !'
);

$result = $client->index($article);
Array
(
    [ok] => 1
    [_index] => blog
    [_type] => article
    [_id] => k_F51gpqQQ-MoRpiK0PdMw
    [_version] => 1
)

Comme nous l’avons vu précédemment, ES classe ses articles de cette manière /index/type/[id].

Pour reprendre l’exemple ci-dessus, nous avons donc indexer un document de type article dans l’index blog. Les Index et les Type vont être créés automatiquement si ils n’existent pas. Ces 2 champs sont obligatoires pour indexer un document.

Les champs contenus dans body sont le contenu de notre article, la structure est libre.

Comme vous avez pu le constater, ES a généré un id pour notre document (« k_F51gpqQQ-MoRpiK0PdMw »), mais nous aurions pu lui fournir notre propre id ($article[‘id’] = 1).

La dernière chose importante est le numéro de version de l’article, cette version sera incrémentée à chaque fois nous indexons un même article, en précisant donc l’id.

Recherche

Pour notre première recherche nous allons récupérer un article par son identifiant.

$params = array();
$params['index'] = 'blog';
$params['type']  = 'article';
$params['id']    = 1;

$article = $client->get($params);
print_r($article);
Array
(
    [_index] => blog
    [_type] => article
    [_id] => 1
    [_version] => 2
    [exists] => 1
    [_source] => Array
        (
            [title] => Ceci est mon titre
            [desc] => Ceci est la description de mon article
        )

)

Comme vous l’avez certainement remarqué, le contenu de notre article se trouve dans _source, pour le reste rien de nouveau. Si nous voulons uniquement récupérer le contenu de l’article sans les méta données nous pouvons appeler la méthode getSource($params).

Continuons par une recherche du nombre de documents indexés:

$params = array();
$params['index'] = 'blog';
$article['type'] = 'article';

$result = $client->count($params);
print_r($result);
Array
(
    [count] => 26
    [_shards] => Array
        (
            [total] => 5
            [successful] => 5
            [failed] => 0
        )

)

Une recherche textuelle sur tous les champs de notre document:

$params = array();
$params['index'] = 'blog';
$params['type']  = 'article';
$params['body']['query']['match']['_all'] = 'moteur';

$results = $client->search($params);
print_r($results);
Array
(
    [took] => 9
    [timed_out] =>
    [_shards] => Array
        (
            [total] => 5
            [successful] => 5
            [failed] => 0
        )
    [hits] => Array
        (
            [total] => 35
            [max_score] => 0.26516503
            [hits] => Array
                (
                    [0] => Array
                        (
                            [_index] => blog
                            [_type] => article
                            [_id] => VMl4KT5uR92g_H9s4RABuA
                            [_score] => 0.26516503
                            [_source] => Array
                                (
                                    [title] => Premier pas avec ES
                                    [desc] => ES est un moteur de recherche simple à mettre en place !
                                )
                        )
                )
        )
)

_all signifie que la recherche s’effectuera dans l’ensemble des champs de notre article. Si vous souhaitez limité la recherche à un champ il suffit de remplacer _all par le nom du champ.

WebServices Elasticsearch

Petit paragraphe pour vous dire que cette librairie PHP ne fait qu’appeler le webservices d’Elasticsearch qui écoute sur le port 9200. Ce qui signifie que si vous appeler l’IP de votre serveur sur le port 9200, vous devriez obtenir une réponse au format json.

{
  "ok" : true,
  "status" : 200,
  "name" : "Steel Raven",
  "version" : {
    "number" : "0.90.7",
    "build_hash" : "36897d07dadcb70886db7f149e645ed3d44eb5f2",
    "build_timestamp" : "2013-11-13T12:06:54Z",
    "build_snapshot" : false,
    "lucene_version" : "4.5.1"
  },
  "tagline" : "You Know, for Search"
}

Récupérer un article par son id

http://192.168.*.*:9200/blog/article/1

Récupérer le nombre d’article

http://192.168.*.*:9200/blog/article/_count

…..

Conclusion

Cet article ne présente qu’une infime partie de ce qu’on peut faire avec Elasticsearch. Pour ceux qui souhaiteraient aller plus loin je vous renvoi vers le site officiel (http://www.elasticsearch.org/).

Category: Php

Guzzle

Guzzle est un client HTTP développé en PHP. Je pense qu’aujourd’hui n’importe quel développeur s’est déjà retrouvé face à un web services. Les web services, ont beaucoup évolué, passant par des trams XML, SOAP et maintenant RESTful ! Guzzle permet donc d’attaquer facilement ce genre de web services, en utilisant le module cURL.

Installation

On ne change pas les bonnes habitudes concernant l’installation, Composer reste notre meilleur ami 🙂

{
   "require": {
      "guzzle/guzzle": "3.7.*"
   }
}

Composer n’est pas la seule façon de l’installer, vous pouvez passer par PEAR (petite pensé pour les sysAdmins):

$ pear channel-discover guzzlephp.org/pear
$ pear install guzzle/guzzle

Dans l’exemple ci-dessus, nous installons l’ensemble des paquets Guzzle, mais il est possible de n’installer que les paquets dont vous avez besoin. Par exemple si vous souhaitez installer uniquement le paquet http, il suffira de remplacer guzzle/guzzle par guzzle/http. Tout simplement 🙂

Premier test

Notre première requête:

define('ROOT_DIR', realpath(__DIR__).'/');
require ROOT_DIR.'vendor/autoload.php';

use Guzzle\Http\Client;

$client = new Client('http://google.com');

$request = $client->get('/');

$response = $request->send();
echo $response->getHeader('Date');

Reprenons l’exemple ci-dessus, nous pouvons distinguer 3 briques utilisées (le client, la requête et la réponse).

Client

Nous commençons par créer le client. L’URL passée en paramètre à l’instanciation sera utilisée par défaut par l’ensemble de nos requêtes. Cette URL est facultative, nous aurions pu la passer à la méthode $client->get(‘http://google.com/’);.

Le client va nous permettre de définir la configuration par défaut de nos requêtes (user_agent, basic_auth, timeout, proxy …). Les options de configuration peuvent aussi être défini et/ou redéfini lors de la préparation de la requête, nous reviendrons dessus.

La requête est créée par le client, toutes les médhodes HTTP peuvent être utilisées (get, post, put, delete, head …).

Requête

La requête est le résultat obtenu par le client. C’est depuis cette brique que la requête sera exécutée. Elle permet aussi de redéfinir les options par défaut du client avant l’exécution.

Réponse

La dernière brique, la réponse va nous permettre d’extraite l’ensemble des informations de la requête. L’entête, le corps, le code retour …

Configuration par défaut du client

Comme nous l’avons vu dans l’exemple précédent, il est possible de définir un ensemble d’options par défaut à l’instanciation du client.

URL

La première option, est l’URL. Si elle est définit lors de l’instanciation, elle sera utilisée pour toutes les requêtes faisant appel à un chemin relatif (/mon/chemin). Mais rien ne vous empêche d’appeler une autre URL depuis ce même client.

Exemple:

define('ROOT_DIR', realpath(__DIR__).'/');
require ROOT_DIR.'vendor/autoload.php';

use Guzzle\Http\Client;

$client = new Client('http://google.com');

$requests[] = $client->get('/');
$requests[] = $client->get('http://google.fr/');
$requests[] = $client->get('search/php');
$requests[] = $client->get('search/php/../test');
foreach($requests as $request) {
    echo $request->getUrl()."\n";   
}

# http://google.com/
# http://google.fr/
# http://google.com/search/php
# http://google.com/search/test

 Options Client

Il est possible de définir un comportement par défaut du client. Ce comportement sera déterminé par les options passées en arguments à l’instanciation. Il existe plusieurs type d’options configurable, les options de requêtes, les options cURL et les variables d’URL.

Options de requête

Dans l’exemple ci-dessous, 3 options ont été définies.

La première headers, est certainement la plus importante, car il s’agit l’entête HTTP qui sera envoyée au serveur. headers attend un tableau en valeur.

La seconde, query, va ajouter à toutes les requêtes une variable ts contenant un timestamp. Ce type d’option peut être très utile, par exemple pour activer dans un environ de développement le profiling de requête via la variable XDEBUG_PROFILE.

La troisième option parle d’elle même, il s’agit du timeout de réponse qui sera appliqué à toutes les requêtes.

$opts = array(
    'request.options' => array(
        'headers' => array('Foo' => 'Bar'),
        'query'   => array('ts' => time()),
        'timeout' => 1
    )
); 
$client = new Client('http://google.com', $opts);

$requests[] = $client->get('/');
foreach($requests as $request) {
    echo $request->getUrl()."\n";   
}

# http://google.com/?ts=138581351

Une autre option très intéressante est la gestion d’événements. Il est possible de mettre en place des écouteurs sur les différentes actions d’une requête (before_send, sent, clone).

$opts = array(
    'request.options' => array(
        'events' => array(
            'request.before_send' => function (\Guzzle\Common\Event $e) {
                echo "before\n";
            },
            'request.sent' => function (\Guzzle\Common\Event $e) {
                echo "sent\n";
            },
        )
    )
); 
$client = new Client('http://google.com', $opts);

La semaine dernière je vous ai présenté Monolog, nous pourrions ici l’utiliser afin de journaliser les URL « crawlées ».

Variable d’URL

Les variables d’URL permettent de définir des valeurs par défaut de chemin.

$opts = array(
    'date' => date('Y-m-d'),
    'request.options' => array(
        'query'   => array('ts' => time()),
        'timeout' => 1
    )
); 
$client = new Client('http://google.com', $opts);

$requests[] = $client->get('/{date}');
foreach($requests as $request) {
    echo $request->getUrl()."\n";   
}

# http://google.com/2013-11-30?ts=1385820766

Options cURL

Il est aussi possible de définir les options cURL à utiliser par défaut via curl.options.

$opts = array(
    'date' => date('Y-m-d'),
    'request.options' => array(
        'query'   => array('ts' => time()),
        'timeout' => 1
    ),
    'curl.options' => array(
        'CURLOPT_PROXY' => 'http://127.0.0.1'
    )
); 
$client = new Client('http://google.com', $opts);

Redéfinir les options par défaut

Jusqu’à présent nous avons vu les options par défaut que nous pouvons définir au niveau du client. Ces options peuvent être redéfinies lors de la création de la requête. A l’appel d’une méthode (get, post, put …), nous pouvons passer en second paramètre des informations d’entête HTTP et en troisième paramètre les options de request.options.

Dans l’exemple suivant, nous allons redéfinir les variables à passer à la requête (query).

$opts = array(
'request.options' => array(
'query'   => array('ts' => time()),
)
);
$client = new Client('http://google.com', $opts);

$requests[] = $client->get(
    '/',
    array(),
    array(
        'query' => array('ts' => date('YmdHis', time()))
    )
);
foreach($requests as $request) {
    echo $request->getUrl()."\n";
}
$response = $requests[0]->send();

# http://google.com/?ts=20131130160416

Paralléliser les requêtes

Guzzle permet de paralléliser les requêtes HTTP, via l’utilisation de curl_multi.

$responses = $client->send(array(
    $client->get('http://www.google.com/'),
    $client->get('http://www.google.fr/'),
    $client->get('http://www.google.es/'),
));

Son utilisation est extrêmement simple, il suffit de passer en arguments de la méthode Client::send() un tableau contenant un ensemble de requêtes à exécuter.

Le temps total de ces 3 requêtes devrait être le temps de la requête la plus lente.

google.com ---->    |
google.fr  -------->|
google.es  ------>  |
           .........! La réponse est renvoyée quand google.fr a répondu.

Traitement de la réponse

Lors de l’exécution d’une requête, le client Guzzle retourne une instance de Guzzle\Http\Message\Response. Cet objet va nous permettre d’obtenir facilement toutes les informations de la requête.

Récupérer les informations de statut de la réponse.

$response = $client->get('/')->send();

echo $response->getStatusCode()."\n";
echo $response->getReasonPhrase()."\n";
echo $response->getProtocol()."\n";
echo $response->getProtocolVersion()."\n";

$response->isSuccessful();
$response->isInformational();
$response->isRedirect();
$response->isClientError();
$response->isServerError();

Pour récupérer les informations provenant de l’entête HTTP, il nous suffit de faire appel à la méthode Response::getHeader(), en passant en paramètre la directive qui nous intéresse.

$response = $client->get('/')->send();
echo $response->getHeader('Content-Type')."\n";
echo $response->getHeader('Content-Length')."\n";

Pour récupérer le corps de la requête, nous pouvons soit faire appel à la méthode Response::getBody() ou caster Response.

$response = $client->get('/')->send();
echo $response->getBody()."\n";
echo $response."\n";

Si vous attendez en réponse du Json ou du XML, vous pouvez appeler respectivement les méthodes Response::json et Response::xml (Retourne un objet SimpleXMLElement).

Plugins

Il existe un système de plugins dont le rôle est d’exécuter certaines actions suivant les événements de la requête. Leur mise en place est très simple, en voici un exemple avec le PluginHistory.

use Guzzle\Http\Client;
use Guzzle\Plugin\History\HistoryPlugin;

$client = new Client();

$client->addSubscriber(new HistoryPlugin());
$client->get('http://www.google.com/')->send();

foreach ($history as $request) {
    echo $request."\n";
}

#GET /?ts=1385835012 HTTP/1.1
#Host: www.google.com
#User-Agent: Guzzle/3.7.4 curl/7.29.0 PHP/5.4.9-4ubuntu2.3

#GET /?gws_rd=cr&ei=BSqaUur9DcOo0QX0qoDAAQ HTTP/1.1
#Host: www.google.fr
#User-Agent: Guzzle/3.7.4 curl/7.29.0 PHP/5.4.9-4ubuntu2.3

Guzzle\Plugin\Async\AsyncPlugin

Permet de faire du traitement de réponse de manière asynchrone.

Guzzle\Plugin\Backoff\BackoffPlugin

Permet de réexécuter automatiquement une requête ayant échouée.

Guzzle\Plugin\Cache\CachePlugin

Permet de mettre en place un système de cache en s’appuyant sur plusieurs valeurs, comme expires, Etag, last-modified …

Guzzle\Plugin\Cookie\CookiePlugin

Ce plugin, permet de conserver les cookie d’une requête à une autre sur un même client Guzzle.

Guzzle\Plugin\History\HistoryPlugin

Permet d’obtenir l’historique de la requête.

Guzzle\Plugin\Log\LogPlugin

Permet de journaliser les requêtes envoyées, les redirections effectuées …

Guzzle\Plugin\Md5\Md5ValidatorPlugin

Permet de vérifier que la réponse HTTP est complète via l’entête Content-MD5.

Guzzle\Plugin\Mock\MockPlugin

Un Mock pour tester le client.

Guzzle\Plugin\Oauth\OauthPlugin

Permet d’utiliser le système d’authentification OAuth.

Conclusion

Guzzle est un client HTTP très complet. et simple à mettre en place que je vais très certainement intégrer à mes projets personnels.

Lien: https://github.com/guzzle/guzzle , http://docs.guzzlephp.org/en/latest/index.html

Category: Php

Monolog

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

Category: Php

1 an après

L’année dernière, j’avais écris un rapide article suite à mon changement de société pour comparer une grosse structure et une structure à taille humaine. Je reviens donc dessus avec plus de matière. Les comparaisons vont être faites sur les deux dernières sociétés dans lesquelles j’ai travaillé. Continue reading

Mes outils

Boite à outilsCet article est un peu particulier, il va me servir à la fois pour référencer les outils que j’utilise mais aussi ceux sur lesquels je compte me pencher. Cette liste d’outils ne sera donc jamais exhaustive, étant donné que les outils que je vais présenter seront uniquement ceux que je veux. (Et oui c’est moi le patron !). Continue reading

uniqid est lent !!

C’est un constat que j’ai fait depuis un certain moment, mais le problème ayant refait surface il y a peu de temps sur l’application d’un de mes collègues, je me suis dit qu’il serait sympa de vous le partager ! Continue reading

Category: Php