Voici un petit article sur l’utilisation de FirePHP lors de vos développements Magento. Vous allez voir qu’avec juste quelques lignes de code, vous allez pouvoir utiliser FirePHP afin de recevoir sur votre console FireBug des informations de Logs envoyées via les fonctions Mage::log et Mage::logException. Cette dernière info a son importance: vous n’utiliserez pas une nouvelle classe maison! De cette façon, tous les appels existants (Magento utilise notemment Mage::logException dans son code) seront maintenus et routés vers la console FireBug plutôt que vers un fichier.
De plus, notre « astuce » devrait fonctionner quelque soit la version de Magento !
Présentation de FireBug et FirePHP
Pour commencer, pour ceux qui ne connaissent pas encore FireBug et FirePHP, je vous laisse visiter leurs sites respectifs pour vous faire une idée du bénéfice apporté par ces 2 extensions (à Firefox) dans le cadre du développement d’un site internet.
Pour utiliser ces 2 extensions, vous devez obligatoirement passer par Firefox. Il existe bien des équivalents pour Chrome mais cela n’est pas aussi efficace:
- Chrome intègre en natif un outil de développement basé très fortement sur Firebug
- Pour obtenir l’équivalent de FirePHP, vous devez installer l’extension Webug. Mais elle ne fonctionne pas aussi bien. Et surtout, vous verrez que cela ne fonctionne pas avec notre « astuce ».
Dernier point avant de passer au coeur du sujet: il existe une extension permettant d’utiliser FirePHP avec Magento mais cette extension ne fonctionne plus depuis la version 1.3 de Magento. D’autre part, vous deviez utiliser des instructions de logs différentes de celles fournies par défaut par Magento (et ça, ça ne me plaisait pas…
).
Fonctionnement du logging Magento
Dans l’article sur le debugging avec Magento, je vous indiquai comment activer le logging (désactivé par défaut) et quelles fonctions utiliser pour envoyer vos informations de logs (Mage::log et Mage:logException). Par défaut, Magento ne propose qu’un envoi des données de logs vers le fichier de votre choix.
Lorsqu’on regarde le fonctionnement de ses 2 fonctions, on se rend compte:
- que Mage::logException se base sur Mage::log pour envoyer les données de l’exception vers le fichier de log destiné aux exceptions: il formate l’exception en une chaîne de caractères qu’il donne ensuite à Mage::log en plus du nom du fichier de log à utiliser (celui que vous avez définit dans la console d’administration).
- que Mage::log se base sur le framework Zend pour gérer l’envoi des logs vers un fichier de logs.
Cela signifie donc que si vous souhaitez modifier le comportement par défaut du logging de Magento, il vous suffit de modifier celui de la fonction Mage::log: la fonction Mage::logException s’appuyant sur elle, elle sera automatiquement impactée par vos modifications.
L’autre information importante concerne le fait que Mage utilise la classe Zend_Log pour ses logs. Car cette classe s’accompagne d’une extrême richesse pour la gestion des logs : envoi des logs vers plusieurs lieux de « stockage » (fichiers, bases de données, mails, …), filtrages possibles, différents formats de sortie, … Je vous invite vivement à lire le guide sur cette classe sur le site de Zend pour vous rendre compte de toutes les possibilités offertes par cette classe.
Ce qui est surprenant, c’est la façon dont Magento l’utilise! Je ne rentre pas dans le détail mais lorsqu’on regarde le code de la fonction Mage::log, on se rend vite compte que ce n’est pas tout à fait comme cela que Zend préconise l’utilisation de sa classe Zend_Log… (mais pourquoi avoir indexé ses loggers par le nom d’un fichier de sortie!!!!).
Bref, l’utilisation de Zend_Log par Magento est une bonne nouvelle mais l’utilisation qui en est faite bride l’évolution qu’on pourrait en faire…
Malgré tout, nous allons tout de même utiliser une petite particularité du fonctionnement interne à cette fonction Mage::log pour router les logs vers la console Firebug et non plus vers des fichiers: l’utilisation de l’élément « global/log/core/writer_model » dans votre fichier de config. Si celui-ci contient une valeur, elle doit correspondre au nom d’une classe Zend_Log_Writer_ que Magento utilisera pour l’envoi des logs.
Cela n’est peut-être pas très clair, je m’explique: Zend utilise la notion de Writer pour définir les composants qui seront chargés d’envoyés les logs. Zend_Log_Writer_Db pour l’envoi vers une base de données, Zend_Log_Writer_Stream pour l’envoi vers un fichier, … Par défaut, Magento utilise la classe Zend_Log_Writer_Stream pour envoyer les informations de logs vers un fichier. Mais si l’élément « global/log/core/writer_model » contient une valeur, il va l’utiliser à la place du composant Zend_Log_Writer_Stream !
Remarque: à noter que Zend permet l’utilisation de plusieurs writers en parallèle, permettant ainsi d’envoyer vos logs vers plusieurs lieux de stockages en même temps (fichiers, base de données, …). Ceci n’est pas possible dans Magento… Et c’est bien dommage!
Hack du logging Magento
Il s’avère que Zend fournit un Writer dédié à l’envoi de logs vers la console Firebug: Zend_Log_Writer_Firebug.
Si vous avez bien suivi, vous vous doutez de la suite: il vous faut ajouter la valeur Zend_Log_Writer_Firebug à l’élément »global/log/core/writer_model » de votre fichier de configuration !
<global> <log> <core> <writer_model>Zend_Log_Writer_Firebug</writer_model> </core> </log> </global>
Mais hélas, cela ne suffit pas.
Déjà, il faut savoir que même si Magento met à disposition l’élément »global/log/core/writer_model » pour choisir le Writer à utiliser, il le configure toujours comme s’il s’agissait d’un writer vers un fichier (il lui donne en paramètre le nom du fichier de sortie). Notre astuce va fonctionner car le writer Zend_Log_Writer_Firebug va ignorer les données de configuration, mais notre astuce ne pourra pas être utilisée pour le writer vers une base de données par exemple (qui nécessite une configuration spécifique).
Pour pouvoir fonctionner, le writer Zend_Log_Writer_Firebug nécessite cependant quelques paramétrages supplémentaires: pour envoyer les données de logs du serveur vers la console Firebug du navigateur, sans perturber le contenu de la page, il utilise un composant Zend_Wildfire_Channel_HttpHeaders afin de créer un « canal » via les entêtes HTTP.
Nous allons créer ce canal lors de l’initialisation du contrôleur Front de Magento, et « flusher » ce canal juste avant l’envoi de la page Html vers le client. Pour cela, nous allons réagir à 2 évènements levés par Magento:
- controller_front_init_routers : levé à la fin de l’initialisation du contrôleur Front
- controller_front_send_response_before : levé juste avant l’envoi par Magento du contenu de l’objet Response vers le navigateur
Si vous n’avez pas tout compris, ce n’est pas très important. Retenez juste que pour pouvoir faire fonctionner notre astuce, nous devons réagir aux 2 évènements que je viens de citer.
Code à mettre en place
Passons maintenant à la pratique. Si vous ne l’avez pas encore fait, modifiez votre fichier de config pour modifier le writer par défaut de Magento:
<global> <log> <core> <writer_model>Zend_Log_Writer_Firebug</writer_model> </core> </log> </global>
Tant que vous êtes dans votre fichier de configuration, ajoutez le code suivant dans /config/global/events :
<controller_front_init_routers> <observers> <Nosymena_Helloworld_Model_Observer_LogFirePHP> <type>singleton</type> <class>travel/observer_logfirephp</class> <method>initFront</method> </Nosymena_Helloworld_Model_Observer_LogFirePHP> </observers> </controller_front_init_routers> <controller_front_send_response_before> <observers> <Nosymena_Helloworld_Model_Observer_LogFirePHP> <type>singleton</type> <class>travel/observer_logfirephp</class> <method>sendResponseBefore</method> </Nosymena_Helloworld_Model_Observer_LogFirePHP> </observers> </controller_front_send_response_before>
Il s’agit de la configuration qui indique à Magento le nom des 2 évènements que nous allons « catcher »:
- controller_front_init_routers: à la levée de cet évènement, Magento appellera la méthode initFront de notre classe Nosymena_Helloworld_Model_Observer_LogFirePHP
- controller_front_send_response_before: à la levée de cet évènement, Magento appellera la méthode sendResponseBefore de notre classe Nosymena_Helloworld_Model_Observer_LogFirePHP
Il ne reste plus qu’à créer cette classe Nosymena_Helloworld_Model_Observer_LogFirePHP et ces 2 méthodes (dans le dossier Model/Observer):
<?php
class Nosymena_Helloworld_Model_Observer_LogFirePHP extends Varien_Event_Observer
{
const ZENDFIREPHP = "Zend_Log_Writer_Firebug";
protected $_channel;
public function initFront($observer)
{
$event = $observer->getEvent();
$front = $event->getFront();
$writerModel = (string) Mage::getConfig()->getNode('global/log/core/writer_model');
if ($writerModel == self::ZENDFIREPHP) {
if (!isset($this->_channel)) $this->_channel = Zend_Wildfire_Channel_HttpHeaders::getInstance();
$this->_channel->setRequest($front->getRequest());
$this->_channel->setResponse($front->getResponse());
}
return $this;
}
public function sendResponseBefore($observer)
{
$event = $observer->getEvent();
$front = $event->getFront();
if (isset($this->_channel)) {
$this->_channel->flush();
$front->getResponse()->sendHeaders();
}
return $this;
}
}
Et voilà!
Désormais, tous vos appels à Mage::log seront reçus directement dans votre console Firebug (à condition que FirePHP soit installé et correctement configuré) ainsi que toutes les exceptions !
Lorsque vous passerez votre site en production, il vous suffira de supprimer l’élément « global/log/core/writer_model » pour revenir au fonctionnement par défaut de Magento: l’envoi des logs vers un fichier.
Remarque: si jamais cette astuce ne fonctionne pas chez vous, essayez d’ajouter l’instruction « ob_start(); » dans la méthode initFront. La documentation de Zend la mentionne mais chez moi elle n’était pas nécessaire pour que cela fonctionne.
Remarque 2: avec Webug (FirePHP pour Chrome), notre astuce ne fonctionne pas car le format de log est mal géré par cette extension. Or, c’est Magento qui décide du format des logs et nous ne pouvons pas le modifier (sauf à surcharger la classe Mage qui contient la fonction Mage::log).
Remarque 3: notre astuce a l’avantage de ne pas être dépendante de la version de Magento et de conserver les routines de logs proposées par Magento mais elle possède quelques inconvénients: ne fonctionne pas avec Webug et pas de possibilité de conserver les logs fichiers en plus des logs Firebug. Si vous souhaitez absolument avoir ces 2 fonctionnalités, il vous faudra:
- soit surcharger la classe Mage (en la recopiant dans votre espace code/local et en modifiant le code de la méthode log) : mais il vous faudra tout refaire à chaque changement de version de Magento
- soit créer une nouvelle classe dédiée au logging. Mais tous les appels déjà existants de Mage::logException n’utiliserons pas cette nouvelle classe.
A nouveau, n’hésitez pas à me donner votre avis sur cet article !