web-dev-qa-db-fra.com

Log4J: Stratégies pour créer des instances de Logger

J'ai décidé d'utiliser le cadre de journalisation Log4J pour un nouveau projet Java. Je me demande quelle stratégie dois-je utiliser pour créer/gérer des instances de Logger et pourquoi?

  • une instance de Logger par classe, par ex.

    class Foo {
        private static final Logger log = Logger.getLogger(Foo.class);
    }
    
  • une instance de Logger par thread
  • une instance de Logger par application
  • découpage horizontal: une instance de Logger dans chaque couche d'une application (par exemple la couche vue, la couche contrôleur et la couche persistance)
  • découpage vertical: une instance de Logger dans les partitions fonctionnelles de l'application

Remarque: Ce problème est déjà considéré dans une certaine mesure dans ces articles:

Quel est le surcoût de la création d'un enregistreur Log4j

68
Adrian

En règle générale, vous auriez la configuration des enregistreurs par classe, car c'est un composant logique Nice. Les threads font déjà partie des messages du journal (si votre filtre les affiche), donc le découpage des enregistreurs de cette façon est probablement redondant.

En ce qui concerne les enregistreurs d'applications ou de couches, le problème est que vous devez trouver un endroit pour coller cet objet Logger. Pas vraiment grave. Le plus gros problème est que certaines classes peuvent être utilisées à plusieurs niveaux à partir de plusieurs applications ... il peut être difficile de bien configurer votre enregistreur. Ou du moins délicat.

... et la dernière chose que vous voulez, ce sont de mauvaises hypothèses dans votre configuration de journalisation.

Si vous vous souciez des applications et des couches et que vous avez des points de séparation faciles, le NDC est le chemin à parcourir. Le code peut parfois être un peu excessif mais je ne sais pas combien de fois j'ai été enregistré par une pile de contexte précise me montrant que Foo.bar () a été appelé à partir de l'application X dans la couche Y.

43
PSpeed

La stratégie la plus utilisée consiste à créer un enregistreur par classe. Si vous créez de nouveaux threads, donnez-leur un nom utile, de sorte que leur journalisation est facilement reconnaissable.

La création d'enregistreurs par classe a l'avantage de pouvoir activer/désactiver la journalisation dans la structure de package de vos classes:

log4j.logger.org.Apache = INFO
log4j.logger.com.example = DEBUG
log4j.logger.com.example.verbose = ERROR

Ce qui précède définirait tout le code de la bibliothèque Apache au niveau INFO, basculerait la journalisation de votre propre code vers le niveau DEBUG à l'exception du package détaillé.

31
rsp

Je suis certain que ce n'est pas une bonne pratique, mais j'ai déjà mis du temps sur les applications pour enregistrer des lignes de code. Plus précisément, lors du collage:

Logger logger = Logger.getLogger(MyClass.class);

... les développeurs oublient souvent de remplacer "MyClass" par le nom actuel de la classe, et plusieurs enregistreurs finissent toujours par pointer au mauvais endroit. C'est mauvais.

J'ai parfois écrit:

static Logger logger = LogUtil.getInstance(); 

Et:

class LogUtil {
   public Logger getInstance() {
      String callingClassName = 
         Thread.currentThread().getStackTrace()[2].getClass().getCanonicalName();
      return Logger.getLogger(callingClassName);
   }
}

Le "2" dans ce code peut être faux, mais l'essentiel est là; prendre un coup de performance pour (au chargement de la classe, en tant que variable statique) trouver le nom de la classe, de sorte qu'un développeur n'a pas vraiment un moyen de taper cela ou d'introduire une erreur.

Je ne suis généralement pas ravi de perdre des performances pour éviter les erreurs de développeur lors de l'exécution, mais si cela se produit en tant que singleton, une fois? Sonne souvent comme un bon métier pour moi.

13
Dean J

Comme cela a été dit par d'autres, je créerais un enregistreur par classe:

private final static Logger LOGGER = Logger.getLogger(Foo.class);

ou

private final Logger logger = Logger.getLogger(this.getClass());

Cependant, j'ai trouvé utile dans le passé d'avoir d'autres informations dans l'enregistreur. Par exemple, si vous avez un site Web, vous pouvez inclure l'ID utilisateur dans chaque message de journal. De cette façon, vous pouvez suivre tout ce qu'un utilisateur fait (très utile pour déboguer des problèmes, etc.).

La méthode la plus simple consiste à utiliser un MDC, mais vous pouvez utiliser un enregistreur créé pour chaque instance de la classe avec le nom incluant l'ID utilisateur.

Un autre avantage de l'utilisation d'un MDC est que si vous utilisez SL4J, vous pouvez modifier les paramètres en fonction des valeurs de votre MDC. Donc, si vous souhaitez consigner toutes les activités d'un utilisateur particulier au niveau DEBUG et laisser tous les autres utilisateurs à ERREUR, vous pouvez. Vous pouvez également rediriger différentes sorties vers différents endroits en fonction de votre MDC.

Quelques liens utiles:

http://logging.Apache.org/log4j/1.2/apidocs/org/Apache/log4j/MDC.html

http://www.slf4j.org/api/index.html?org/slf4j/MDC.html

10
Matthew Farwell
  • Créez un enregistreur par classe.
  • Si vous avez des dépendances qui nécessitent la journalisation Commons (très probablement) utilisez bridge de slf4j pour la journalisation Commons. Instanciez vos enregistreurs (par classe) en utilisant l'interface Commons Logging: private static final Log log = LogFactory.getLog(MyClass.class);
  • Manifestez ce modèle dans votre IDE en utilisant des raccourcis. J'utilise les IDEA modèles en direct à cet effet.
  • Fournissez des informations contextuelles aux threads à l'aide d'un NDC (thread local pile de chaînes) ou d'un MDC (thread local map of String →?).

Exemples de modèles:

private static final Log log = LogFactory.getLog($class$.class); // live template 'log'

if (log.isDebugEnabled())
    log.debug(String.format("$string$", $vars$)); // live template 'ld', 'lw', 'le' ...
4
yawn

Une autre option: vous pouvez essayer AspectJ de recouper la journalisation. Cochez ici: Simplifiez votre journalisation. (Si vous ne voulez pas utiliser AOP , vous pouvez regarder slf4j)

//Without AOP

    Class A{
       methodx(){
        logger.info("INFO");
       }
    }

    Class B{
       methody(){
        logger.info("INFO");
       }
    }

//With AOP

    Class A{
       methodx(){
         ......
       }
    }

    Class B{
       methody(){
         ......
       }
    }

    Class LoggingInterceptor{

       //Catched defined method before process
       public void before(...xyz){
         logger.info("INFO" + ...xyz);
       }

       //Catched defined method after processed          
       public void after(...xyz){
         logger.info("INFO" + ...xyz);
       }
       .....

    }

PS: AOP sera mieux, c'est SEC (ne vous répétez pas) façon.

3
baybora.oren

la méthode la meilleure et la plus simple pour créer des enregistreurs personnalisés, non liés à un nom de classe est:

// create logger
Logger customLogger = Logger.getLogger("myCustomLogName");

// create log file, where messages will be sent, 
// you can also use console appender
FileAppender fileAppender = new FileAppender(new PatternLayout(), 
                                             "/home/user/some.log");

// sometimes you can call this if you reuse this logger 
// to avoid useless traces
customLogger.removeAllAppenders();

// tell to logger where to write
customLogger.addAppender(fileAppender);

 // send message (of type :: info, you can use also error, warn, etc)
customLogger.info("Hello! message from custom logger");

maintenant, si vous avez besoin d'un autre enregistreur dans la même classe, pas de problème :) créez-en un nouveau

// create logger
Logger otherCustomLogger = Logger.getLogger("myOtherCustomLogName");

voyez maintenant le code ci-dessus et créez un nouveau fileappender afin que votre sortie soit envoyée dans un autre fichier

C'est utile pour (au moins) 2 situations

  • lorsque vous voulez séparer l'erreur des informations et avertit

  • lorsque vous gérez plusieurs processus et que vous avez besoin de la sortie de chaque processus

ps. avoir des questions ? N'hésitez pas à demander! :)

2
Rodislav Moldovan

Lors du déploiement de plusieurs fichiers EAR/WAR, il peut être préférable de regrouper le log4j.jar plus haut dans la hiérarchie du chargeur de classe.
c'est à dire. pas dans WAR ou EAR, mais dans le chargeur de classe système de votre conteneur, sinon plusieurs instances Log4J écriront simultanément dans le même fichier, ce qui entraînera un comportement étrange.

0
Bas

La convention courante est "une classe pr enregistreur et utilise le nom de classe comme son nom". C'est un bon conseil.

Mon expérience personnelle est que cette variable de l'enregistreur ne doit PAS être déclarée statique mais une variable d'instance qui est récupérée pour chaque nouvelle. Cela permet au cadre de journalisation de traiter deux appels différemment selon leur provenance. Une variable statique est la même pour TOUTES les instances de cette classe (dans ce chargeur de classe).

Vous devez également apprendre toutes les possibilités avec le backend de journalisation de votre choix. Vous pouvez avoir des possibilités que vous ne vous attendiez pas possibles.