février
2008
Si vous utilisez Log4j dans vos projets, et si en plus vous êtes sur Java >= 5, alors log5j devrait vous intéresser.
C’est un tout petit projet hébergé dans Google Code et qui se contente de présenter une interface moderne à Log4j, en utilisant les varargs mais surtout java.util.Formatter, le tout pour éviter l’erreur très répandue que voici:
log.debug("Nom="+nom+", prenom="+prenom);
On sait tous que la charge induite lors de la vérification du niveau de log est très réduite pour Log4j, mais c’est pas le cas tout le temps, et certainement pas avec un tas d’instructions comme celle au dessus.
En effet, quelque soit le niveau de log défini (disons info par exemple), cette instruction va induire une charge qui est la génération de la chaine passée au logger, ce qui peut être très couteux avec des milliers d’occurences décimées disséminées le long du code.
La bonne manière de procéder serait de vérifier d’abord si le logger est en mode debug avant d’appeler la méthode debug, mais ceci peut vite devenir pénible et casse gueule: on décide de changer le debug en warn sans toutefos changer le test du if xD, etc.
Log5j offre une solution élégante à ce problème, en surchargant les 5 méthodes de log de log4j (debug, info, warn, error, fatal) avec des méthode acceptant un paramètre format et un varargs, ce qui donne:
log.debug("Nom=%s, prenom=%s", nom, prenom);
Log5j s’occupe ensuite de tester si on est au bon niveau de log, et si c’est le cas il utilise java.util.Formatter pour générer la chaine résultante et la passer à Log4j.
Enfin, Log5j offre un autre goodie qui est la simplification de la déclaration du logger: Qui n’a jamais mal-usé du copier/coller de la (longue) déclaration d’un Logger en oubliant de changer la classe passée comme paramètre ?
Ainsi, ceci (Log4j)
private static final Logger log = Logger.getLogger(MaClasse.class);
devient (log5j):
private static final Logger log = Logger.getLogger();
Et ce pour un même résultat.
Pour finir, la bête (le jar) ne fait que 3Ko, et est composé d’une seule classe (com.spinn3r.log5j.Logger)
J’ai oublié de donner un exemple d’appel
public static Logger make() { <br />
return Logger.getLogger(StackUtil.getCaller(false)); <br />
} <br />
Concernant le tip pour déclarer le logger de cette facon :
private static final Logger log = Logger.getLogger(); <br />
vous pouvez consulter ce billet
http://www.javaspecialists.eu/archive/Issue137.html
principalement cette partie (vers la fin)
package com.cretesoft.tjsn; <br />
<br />
import org.apache.log4j.Logger; <br />
<br />
public class LoggerFactory { <br />
public static Logger make() { <br />
Throwable t = new Throwable(); <br />
StackTraceElement directCaller = t.getStackTrace()[1]; <br />
return Logger.getLogger(directCaller.getClassName()); <br />
} <br />
} <br />
Dans un environnement utilisant un JDK inférieur à 1.4 (oui, je sais, mais ca existe encore, la preuve est que je n’ai pas le choix), il n’y a pas encore de notion de StackTraceElement ; l’astuce consiste alors a écrire la stack trace dans un flux particulier.
Un exemple tres vite fait (avec toute la javadoc bien sur) :
<br />
public class StackUtil { <br />
public static final String getCaller() { <br />
return getCaller(false); <br />
} <br />
<br />
public static final String getCaller(boolean withMethodName) { <br />
Throwable t = new Throwable(); <br />
StackPrinter writer = new StackPrinter(System.err, withMethodName); <br />
t.printStackTrace(writer); <br />
return writer.getCaller(); <br />
} <br />
} <br />
<br />
<br />
class StackPrinter extends PrintStream { <br />
<br />
public StackPrinter(OutputStream out, boolean method) { <br />
super(out); <br />
this.withMethodName = method; <br />
} <br />
<br />
private boolean withMethodName; <br />
<br />
private Collection stack = new LinkedList(); <br />
<br />
public Collection getStackTrace() { <br />
return stack; <br />
} <br />
public String getCaller() { <br />
for (Iterator it = stack.iterator(); it.hasNext();) { <br />
String caller = it.next().toString(); <br />
//example : " at com.company.util.log.LogFactory.make(LogFactory.java:29)" <br />
caller = caller.trim(); <br />
int delim1 = caller.indexOf(' '); <br />
int delim2 = caller.indexOf('('); <br />
if (delim1 >= 0 && delim2 > delim1) { <br />
String method = ""; <br />
caller = caller.substring(delim1, delim2).trim(); <br />
int lastDot = caller.lastIndexOf('.'); <br />
if (lastDot > 0) { <br />
if (Character.isLowerCase(caller.charAt(lastDot + 1))) { <br />
// method name <br />
method = caller.substring(lastDot + 1); <br />
caller = caller.substring(0, lastDot); <br />
} <br />
} <br />
<br />
if (caller.startsWith("com.company") && !StackUtil.class.getName().equals(caller)) { <br />
if (withMethodName && !"".equals(method)) { <br />
return caller + "." + method; <br />
} <br />
return caller; <br />
} <br />
} <br />
} <br />
return ""; <br />
} <br />
<br />
public void println(Object obj) { <br />
stack.add(obj.toString()); <br />
} <br />
<br />
public void println(String data) { <br />
stack.add(data); <br />
} <br />
<br />
} <br />
Comment ca, « c’est laid » ? Je trouve ca super drole moi
sinon, ca fait un moment que je veux jeter un oeil sur Logback , mais j’ai pas encore eu le temps.
@lunatix: Oui en effet, j’ai remarqué ça aussi … bon, vais certainement y jeter un coup d’oeil, même si je deteste vraiment son nom (slf4j).
@zedros: Tout à fait, c’est corrigé ! xD
salut
chtite remarque :
milliers d’occurences décimées le long du code
ne devrait pas plutôt être :
milliers d’occurrences disséminées le long du code ?
voili
++
Joseph en mode pointilleux
intéressant. moi perso, je suis passé a slf4j a la place de common loggin (http://www.slf4j.org/), j’utilise rarement log4j en direct.
ca permet aussi de s’affranchir de la concatenation de texte avec une api du genre
logger.debug(« je veux logger {} et aussi {} », var1, var2);
bon nombre de projets sont en train de passer a cette api (spring, hibernate etc…)
et en plus, pour remplacer common loggin, y’a juste un jar a mettre qui reprendre l’exacte api de l’ancienne lib, en attendant d’aller corriger tout le code.