JeuWeb - Crée ton jeu par navigateur
Logger le contexte - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Logger le contexte (/showthread.php?tid=5007)

Pages : 1 2


Logger le contexte - Myrina - 26-10-2010

Dans mon application je logge beaucoup d'informations mais pas forcément exploitable.
par exemple, j'ai une classe Lang qui s'occupe de l'internationalisation et qui de temps à autre ne trouve pas une clé demandée.
J'obtiens ce comportement par un code du style:

public function __get($name) {
$key=str_replace(' ','',trim(mb_strtolower($name,"UTF-8")));
if (array_key_exists($key,$this->msg)) {
return $this->msg[$key];
} else {
$this->log(Log:Big GrinEBUG,'String internationalisée non trouvée: '.$key);
return 'Undefined';
}
}

Mon souci, c'est d'où provient ma clé ($name)?

Avec le code suivant, j'obtiens la succession des classes appelés et je peux répondre à ma question:

public function __get($name) {
$key=str_replace(' ','',trim(mb_strtolower($name,"UTF-8")));
if (array_key_exists($key,$this->msg)) {
return $this->msg[$key];
} else {
try {
//Lancement d'une exception juste pour avoir la pile d'exécution
throw new Exception('String internationalisée non trouvée: '.$key);
} catch (Exception $e) {
$tab=array();
foreach ($e->getTrace() as $pile) {
//Injection avec la clé afin de supprimer les doublons
$tab[$pile['class']]=$pile['class'];
}
$this->log(Log:Big GrinEBUG,$e->getMessage().' dans ('.implode('->',array_reverse($tab)).')');
return 'Undefined';
}
}
}

et le log obtenu ainsi ressemble à:
Code :
26/10/2010 10:09:08> #MODE DEBUG: (Lang):String internationalisée non trouvée: pageaccueil dans (IndexController->IndexView->UserMenuView->MenuView->BaseView->Lang)



RE: Logger le contexte - Ter Rowan - 26-10-2010

merci pour cette initiative, je commençais à chauffer un peu dans l'affichage de mes messages d'erreur, guère lisible, et je trouve ton système très intéressant (c'est plus l'exploitation de la trace que je laissais assez... en friche, et là miracle ça devrait être très bien)


merci beaucoup !


RE: Logger le contexte - Myrina - 26-10-2010

(26-10-2010, 01:27 PM)Ter Rowan a écrit : merci pour cette initiative, je commençais à chauffer un peu dans l'affichage de mes messages d'erreur, guère lisible,...
Tu aurais besoin de ma classe Log?


RE: Logger le contexte - Ter Rowan - 26-10-2010

ce serait un luxe énorme, mais volontiers, j'ai réalisé un truc à l'arrache pour le moment (du genre print_r....)

EDIt : pour le moment, comme je fonctionne aux tests qui dirigent le développement et que je n'ai pas de couche présentation encore développée, je n'affiche que des tests, et donc des logs (d'où le print_r)


RE: Logger le contexte - Myrina - 26-10-2010

Voici ma classe de log:
<?php
/**
* Classe de gestion des Logs applicatifs
* Intègre le pattern SINGLETON
*
* @copyright 2010
* @author Myrina
* @version 1.0
*/
class Log {

/**
* Ce niveau sert pour ne rien logger
*
*/
const NONE = -1;
/**
* Ce niveau sert à logger que les erreurs qui interrompent le script
* (l'interception des erreurs PhP n'est pas implémentée pour l'instant)
*
*/
const FATAL = 0;
/**
* Ce niveau sert à logger les erreurs de fonctionnement
*
*/
const ERROR = 1;
/**
* Ce niveau sert à logger les comportements bizarres
*
*/
const WARN = 2;
/**
* Ce niveau sert à logger tout message informatif sur le fonctionnement
*
*/
const INFO = 3;
/**
* Ce niveau sert à logger tous les messages
*
*/
const DEBUG = 4;

/**
* Tableau des entêtes de messages selon la gravité de celui-ci
*
* @var Array
*/
private static $levelsTxt= array (
Log::FATAL => 'ERREUR FATAL',
Log::ERROR => 'ERREUR',
Log::WARN => 'AVERTISSEMENT',
Log::INFO => 'INFORMATION',
Log:Big GrinEBUG => 'MODE DEBUG'
);

/**
* Niveau courant de logging des messages
*
* @var Integer
*/
private $logLevel=Log::INFO;

/**
* Format de génération des noms de fichier en respectant la syntaxe de la fonction <b>date()</b>
*
* @var String
*/
private $fileFormat='\R\t\s\P\h\P\G\a\m\e\r-Y-m-d.\l\o\g';

/**
* A <b>true</b> si le nom du fichier doit dépendre de la date du jour
*
* @var Boolean
*/
private $isDate=true;

/**
* Répertoire de stockage des fichiers logs
*
* @var String
*/
private $dir='logs';

/**
* Format d'affichage de la date et de l'heure pour les messages
*
* @var String
*/
private $dateFormat='d/m/Y H:iConfused';

/**
* Utilisateur connecté
*
* @var User
*/
private $user=null;

/**
* Définit si l'adresse IP apparait dans le log ou pas
*
* @var Boolean
*/
private $logIp=false;

/**
* Retour à la ligne (ici pour Windows)
*
* @var String
*/
private $eol="\r\n";

/**
* Instance de la classe
*
* @var Listes
*/
private static $instance;

/**
* Constructeur privé afin d'empêcher la création direct par new
*
*/
private function __construct()
{
}

/**
* Méthode SINGLETON
*
* @return Log
*/
public static function getInstance()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}

return self::$instance;
}

/**
* Prévient les utilisateurs sur le clônage de l'instance
*
*/
public function __clone()
{
throw new Exception('Le clônage n\'est pas autorisé.');
}

/**
* Choix du repértoire de stockage des logs
*
* @param String $dir Répertoire par défaut de stockage des fichiers de logging
*/
public function initDir($dir=null) {
//Vérification du répertoire par défaut
if ($dir!=null) {
$this->setDir($dir);
} else {
$this->setDir($this->dir);
}
}

/**
* Enregistre un message de log au niveau fourni
*
* @param Integer $logLevel Niveau du message (Log::...)
* @param String $mess Message à logger
*/
public function log($logLevel,$mess) {
if ($this->isToLog($logLevel)) {
$this->write($this->getEntete($logLevel).$mess);
}
}

/**
* Renvoi le niveau courant de logging
*
* @return Integer
*/
public function getLogLevel() {
return $this->logLevel;
}

/**
* Renvoi le texte du niveau courant courant de logging
*
* @return String
*/
public function getLogLevelTxt() {
return Log::$levelsTxt[$this->getLogLevel()];
}

/**
* Fixe le nouveau niveau courant de logging
*
* @param Integer $newLvl
*/
public function setLogLevel($newLvl) {
if ($this->isCorrectLevel($newLvl)) {
$this->logLevel=$newLvl;
}
}

/**
* Renvoi le répertoire de stockage des fichiers de logs
*
* @return String
*/
public function getDir() {
return $this->dir;
}

/**
* Change le répertoire de stockage des fichiers de logs
*
* @param String $newPath
*/
public function setDir($newPath) {
// $newDir=dirname(SITE_ROOTDIR.$newPath);
$newDir=dirname($newPath);
if ($newDir=='.') {
$newDir=$newPath;
}
//Si le répertoire n'existe pas
if (!is_dir($newDir)) {
//Si il peut être créé
if (mkdir($newDir,0777)) {
$this->dir=$newDir;
}
} else {
$this->dir=$newDir;
}
}

/**
* Modifie le nom du fichier de log
*
* @param String $file nom du fichier
* @param Boolean $useDate à <b>true</b> si le nom du fichier contient une date à formatter
*/
public function setFile($file,$useDate=true) {
$this->fileFormat=$file;
$this->isDate=$useDate;
}

/**
* Renvoi le chemin complet du fichier courant de logging
*
* @return String
*/
public function getCurrentFile() {
$file=$this->dir.'/'.($this->isDate?date($this->fileFormat,time()):$this->fileFormat);
return $file;
}

/**
* Renvoi le nom de l'utilisateur pour lequel le logging est actif
*
* @return String
*/
private function getUserNom() {
if (is_object($this->user) && ($this->user instanceof User)) {
return $this->user->getName();
} elseif (is_array($this->user)) {
return $this->user['username'];
} else {
return '';
}
}

/**
* Renvoi l'ID MySQL de l'utilisateur pour lequel le logging est actif
*
* @return Integer
*/
private function getUserId() {
if (is_object($this->user) && ($this->user instanceof User)) {
return $this->user->getId();
} elseif (is_array($this->user)) {
return $this->user['id'];
} else {
return '';
}
}

/**
* Associe l'utilisateur connecté aux messages de logging
*
* @param Mixed $user
*/
public function setUser($user) {
$this->user=$user;
}

/**
* A <b>true</b> si l'adresse IP doit apparaitre dans les messages de logging
*
* @return Boolean
*/
public function isLogIp() {
return $this->logIp;
}

/**
* Fixe le logging de l'adresse IP ou pas
*
* @param Boolean $logIp
*/
public function setLogIp($logIp) {
if (is_bool($logIp)) {
$this->logIp=$logIp;
}
}

/**
* Ecrit le message dans le fichier courant
*
* @param String $msg
*/
private function write($msg){
$txt=$msg.$this->eol;
$fp = fopen($this->getCurrentFile(), "a");
fputs($fp, $txt);
fclose($fp);
}

/**
* Vérifie si le niveau spécifié est connu par la classe
*
* @param Integer $level
* @return Boolean
*/
private function isCorrectLevel($level) {
return (in_array($level,array_keys(Log::$levelsTxt)));
}

/**
* Vérifie si le niveau spécifié est à logger
*
* @param Integer $level
* @return Boolean
*/
private function isToLog($level){
return ($this->isCorrectLevel($level)&&$level<=$this->getLogLevel());
}

/**
* Recherche l'IP de la personne connectée
*
* @return String Adresse IP
*/
private function getIp(){
$ip='';
if ($this->isLogIp()) {
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
// Si la requête HTTP a été forwardé par un proxy
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif(isset($_SERVER['HTTP_CLIENT_IP'])){
$ip = $_SERVER['HTTP_CLIENT_IP'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
}
return $ip;
}

/**
* Formattage du texte contenant les informations de l'utilisateur
*
* @return String
*/
private function getUserTxt() {
$txt='';
if ($this->user!=null) {
$txt=' ( '.$this->getUserNom().' -ID:'.$this->getUserId().')';
}
return $txt;
}

/**
* Formattage du texte contenant les informations de l'adresse IP
*
* @return String
*/
private function getIpTxt(){
$txt='';
$ip=$this->getIp();
if ($ip!='') {
$txt=' [IP:'.$ip.']';
}
return $txt;
}

/**
* Formattage du texte contenant la date du logging
*
* @return String
*/
private function getDateTxt(){
$txt='';
$date=date($this->dateFormat,time());
$txt=$date.'>';
return $txt;
}

/**
* Formattage du texte contenant le nom du niveau de log
*
* @param Integer $logLevel
* @return String
*/
private function getLogTxt($logLevel){
$txt='';
$log=Log::$levelsTxt[$logLevel];
$txt=' #'.$log.': ';
return $txt;
}

/**
* Renvoi la partie à logger avant le message
*
* @param Integer $logLevel
* @return String
*/
private function getEntete($logLevel){
return $this->getDateTxt().$this->getUserTxt().$this->getIpTxt().$this->getLogTxt($logLevel);
}

/**
* Enregistre un message de log au niveau fourni dans le fichier fourni
*
* @param String $file Nom du fichier de stockage
* @param Integer $logLevel Niveau du message (Log::...)
* @param String $mess Message à logger
*/
public function logFile($file,$logLevel,$mess) {
$tmpfile=$this->fileFormat;
$tmpdate=$this->isDate;
$this->setFile($file,false);
$this->log($logLevel,$mess);
$this->setFile($tmpfile,$tmpdate);
}
}
?>
Il faudra certainement prévoir des adaptations:
* le nom des fichiers logs (private $fileFormat='\R\t\s\P\h\P\G\a\m\e\r-Y-m-d.\l\o\g'Wink est à adapter
* les méthodes getUserNom() et getUserId() doivent être reprise selon la gestion interne de l'utilisateur connecté.
* les méthodes de rendus (getUserTxt(), getIpTxt(), getDateTxt(), getLogTxt() et getEntete() ) sont à adapter selon le format de log souhaité

L'initialisation du log (au début du index.php):
		//Initialisation du logger
$log=Log::getInstance();
$log->initDir();
$log->setLogLevel(Log:Big GrinEBUG);


L'utilisation du log:
	
$log=Log::getInstance();
$log->log(Log:Big GrinEBUG,'String internationalisée non trouvée: '.$key);



Il y a aussi la possibilité de logger ponctuellement dans un autre fichier avec la méthode logFile().


PS: en bonus, j'ai créé une classe de base dont héritent toutes mes autres classes pour avoir à disposition le log:
<?php
/**
* Classe abstraite servant de base à toutes les classes et implémentant le mécanisme de log
*
* @copyright 2010
* @author Myrina
* @version 1.0
*/
abstract class BaseClass {

/**
* Enregistre un message de log au niveau fourni
*
* @param Integer $logLevel Niveau du message (Log::...)
* @param String $mess Message à logger
* @param Boolean $ip à <b>true</b> pour faire apparaître l'@ IP dans le log
*/
protected function log($logLevel,$mess,$ip=false) {
$log=Log::getInstance();
$class=get_class($this);
$svgIp=$log->isLogIp();
if ($ip) {
$log->setLogIp(true);
}
$log->log($logLevel,'('.$class.'):'.$mess);
$log->setLogIp($svgIp);
}

}
?>



RE: Logger le contexte - Ter Rowan - 26-10-2010

merci pour tout cela, je m'en vais l'étudier Smile !


RE: Logger le contexte - Myrina - 23-12-2010

(26-10-2010, 07:39 PM)Ter Rowan a écrit : merci pour tout cela, je m'en vais l'étudier Smile !
Du neuf?
Des commentaires suite à ton étude?


RE: Logger le contexte - Jeckel - 24-12-2010

Avis perso :

Faire une classe de base pour avoir accès à la fonction log partout c'est pas très "clean" en terme de modélisation POO.

Une classe de gestion de Log par contre c'est très bien.

Quand on utilise des exceptions, le mieux est de toujours typer ses exceptions, celà permet ensuite de conditionner facilement la gestion des erreurs en fonction du type et du contexte :


class LangException extends Exception { }

Du coup, lorsque tu as un problème de langue, je reprend ton code :

if (array_key_exists($key,$this->msg)) {
return $this->msg[$key];
} else {
try {
throw new LangException('String internationalisée non trouvée: '.$key);
}
...

Si tu ne veux pas interrompre ton programme et juste logger ton message, alors c'est dans l'exception qu'il faut le faire :

class LangException extends Exception {
public function __construct($message = null, $code = 0, Exception $previous = null)
{
parent::__construct($message, $code, $previous);

$tab=array();
foreach ($this->getTrace() as $pile)
{
//Injection avec la clé afin de supprimer les doublons
$tab[$pile['class']]=$pile['class'];
}
$this->log(Log:Big GrinEBUG,$this->getMessage().' dans ('.implode('->',array_reverse($tab)).')');
}
}
Maintenant à chaque fois que tu lèves une Exception de type LangException le message de l'exception et son contexte peuvent être loggé automatiquement...

Sinon, si vous voulez aller plus loin dans la gestion des logs et des erreurs, même sans utiliser Zend Framework, allez voir la documentation de Zend_Log, à vous donnera sans doute des idées : http://framework.zend.com/manual/fr/zend.log.overview.html


RE: Logger le contexte - Sephi-Chan - 24-12-2010

Je plussoie pour l'utilisation de Zend_Log. A quoi bon redévelopper des composants pour ça alors que celui-ci est très bien et simple d'emploi. Smile


Sephi-Chan


RE: Logger le contexte - Jeckel - 24-12-2010

Je ne suis pas sûr, mais il probable que Zend_Log soit en plus un composant qui peut être utilisé sans utiliser le reste du Framework... à confirmer