L'enregistreur doit-il être statique privé ou non

103

L'enregistreur doit-il être déclaré statique ou non? Habituellement, j'ai vu deux types de déclaration pour un enregistreur:

    log log protégé = nouveau Log4JLogger (aClass.class);

ou

    journal statique privé du journal = nouveau Log4JLogger (aClass.class);

Lequel faut-il utiliser? Quels sont les avantages et les inconvénients des deux?

Drahakar
la source
1
L'exploitation forestière est une préoccupation transversale. Utilisez Aspects et la question est sans objet.
Dave Jarvis
4
staticest une référence par classe. non-statique est une référence par instance (+ initialisation). Donc, dans certains cas, ce dernier a un impact important sur la mémoire si vous avez des tonnes d'instances. N'utilisez jamais le non-statique dans un objet fréquent . J'utilise toujours la version statique. (qui devrait être en majuscule LOG )
A QUITTER - Anony-Mousse
2
comme suggéré déjà, utilisez AOP et les annotations, par exemple: jcabi.com/jcabi-aspects/annotation-loggable.html
yegor256
1
RobertHume la version statique est utilise une constante. C'est exactement pourquoi il doit être mis en majuscules.
A QUITTER - Anony-Mousse
2
Non, il devrait être en private static final Log logminuscules. L'enregistreur n'est pas une constante, l'enregistreur est un objet final statique (qui peut être muté). Personnellement, j'utilise toujours logger.
osundblad

Réponses:

99

L'avantage de la forme non statique est que vous pouvez la déclarer dans une classe de base (abstraite) comme suit sans vous soucier que le bon nom de classe sera utilisé:

protected Log log = new Log4JLogger(getClass());

Cependant, son inconvénient est évidemment qu'une toute nouvelle instance de journalisation sera créée pour chaque instance de la classe. Cela peut ne pas être cher en soi, mais cela ajoute une surcharge importante. Si vous souhaitez éviter cela, vous souhaitez utiliser le staticformulaire à la place. Mais son inconvénient est à son tour que vous devez le déclarer dans chaque classe individuelle et veiller dans chaque classe à ce que le bon nom de classe soit utilisé pendant la construction de l'enregistreur car getClass()il ne peut pas être utilisé dans un contexte statique. Cependant, dans l'EDI moyen, vous pouvez créer un modèle de saisie semi-automatique pour cela. Par exemple logger+ ctrl+space.

D'un autre côté, si vous obtenez le logger par une fabrique qui à son tour peut mettre en cache les loggers déjà instanciés, alors l'utilisation du formulaire non statique n'ajoutera pas beaucoup de surcharge. Log4j par exemple a un LogManagerpour cela.

protected Log log = LogManager.getLogger(getClass());
BalusC
la source
6
Déclarez abstract Log getLogger();dans la classe abstraite. Implémentez cette méthode en renvoyant le journal statique pour l'instance particulière. Ajoutez private final static Log LOG = LogManager.getLogger(Clazz.class);à votre modèle de classe IDE.
A QUITTER - Anony-Mousse
2
Pour slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Markus Pscheidt
3
@BalusC le problème avec la transmission de getClass () à la méthode getLogger est qu'elle renvoie la classe de l'instance actuelle. Normalement, il est préférable que la journalisation soit associée à la classe où se trouve le code. Par exemple, si le code de journalisation est dans la classe Parent, nous voulons que la journalisation soit associée à Parent même si l'instance en cours d'exécution est une instance de la classe Child qui est une sous-classe de Parent. Avec getClass (), il sera associé à l'enfant, de manière incorrecte
ou le
@inor: "incorrectement"? Si vous ne voulez pas abstraire la classe, vous ne devriez tout simplement pas utiliser le getClass () hérité en premier lieu. Il y a des développeurs qui le trouvent correct et utile car il révèle des informations dans quelle sous-classe exactement la logique a été exécutée.
BalusC
2
@ BalusC getLogger (getClass ()) entraîne toujours une journalisation incorrecte du nom de la sous-classe. Les classes de journalisation doivent toujours faire getLogger (Clazz.class) pour associer la journalisation effectuée par le code de la classe Clazz. Les développeurs qui veulent savoir laquelle des sous-classes est en cours d'exécution (par exemple, SubClazz étend Clazz) devraient faire dans SubClazz: getLogger (SubClazz.class) et quelque chose comme: log.info ("appeler <quelque chose dans ma classe de base>");
inor
44

J'avais l'habitude de penser que tous les enregistreurs devraient être statiques; cependant, cet article sur wiki.apache.org soulève des problèmes de mémoire importants, concernant les fuites de classloader. La déclaration d'un enregistreur comme statique empêche la classe déclarante (et les chargeurs de classe associés) d'être récupérés dans les conteneurs J2EE qui utilisent un chargeur de classe partagé. Cela entraînera des erreurs PermGen si vous redéployez votre application suffisamment de fois.

Je ne vois vraiment aucun moyen de contourner ce problème de fuite de chargeur de classe, autre que de déclarer les enregistreurs comme non statiques.

piepera
la source
4
Je pensais que le champ statique aurait également un problème de fuite de mémoire. Non statique peut avoir des problèmes de performances comme d'autres l'ont dit. Quelle est la manière idéale alors?
liang
@piepera le principal problème décrit dans l'article que vous avez référencé est la possibilité de contrôler le niveau de journalisation dans chaque application lorsque "considérez le cas où une classe utilisant" private static Log log = "est déployée via un ClassLoader qui est supposément de l'ascendance multiple "applications" "indépendantes. Je ne vois pas cela comme un problème parce que dans cette situation particulière les applications ont un "terrain d'entente" et dans ce "terrain d'entente" le niveau de journalisation pour la classe est décidé et oui, il est valable pour toutes les applications ... mais gardez à l' esprit que cette classe est [chargé] en dehors de ces applications
Inor
17

La différence la plus importante est la façon dont cela affecte vos fichiers journaux: dans quelle catégorie les journaux vont-ils?

  • Dans votre premier choix, les logs d'une sous-classe se retrouvent dans la catégorie de la superclasse. Cela me semble très contre-intuitif.
  • Il existe une variante de votre premier cas:

    journal protégé = nouveau Log4JLogger (getClass ());

    Dans ce cas, votre catégorie de journal indique sur quel objet le code qui s'est connecté travaillait.

  • Dans votre deuxième choix (statique privé), la catégorie de journal est la classe qui contient le code de journalisation. Donc, normalement, la classe qui fait la chose qui est enregistrée.

Je recommanderais fortement cette dernière option. Elle présente ces avantages, par rapport aux autres solutions:

  • Il existe une relation directe entre le journal et le code. Il est facile de retrouver l'origine d'un message de journal.
  • Si quelqu'un doit régler les niveaux de journalisation (ce qui est fait par catégorie), c'est généralement parce qu'il est intéressé (ou non) par certains messages particuliers, écrits par une classe particulière. Si la catégorie n'est pas la classe qui écrit les messages, il est plus difficile de régler les niveaux.
  • Vous pouvez vous connecter aux méthodes statiques
  • Les enregistreurs ne doivent être initialisés (ou recherchés) qu'une fois par classe, donc au démarrage, au lieu de pour chaque instance créée.

Il présente également des inconvénients:

  • Il doit être déclaré dans chaque classe où vous enregistrez des messages (pas de réutilisation des enregistreurs de superclasse).
  • Vous devez prendre soin de mettre le bon nom de classe lors de l'initialisation de l'enregistreur. (Mais les bons IDE s'occupent de cela pour vous).
Wouter Coekaerts
la source
4

Utilisez l'inversion de contrôle et transmettez le logger au constructeur. Si vous créez le logger dans la classe, vous allez avoir un temps fou avec vos tests unitaires. Vous écrivez des tests unitaires, n'est-ce pas?

Wayne Allen
la source
5
Des tests unitaires qui examinent la journalisation que vous produisez un son à la fois inutile et incroyablement fragile.
Michael
1
Utilité en fonction du système testé. Parfois, la journalisation est tout ce à quoi vous avez accès.
Wayne Allen
@Wayne Allen lorsque vous faites des tests unitaires, par définition, vous avez également des résultats de test. Proposez-vous une situation dans laquelle on fait des tests unitaires mais on n'a pas de résultats de tests? avoir seulement les journaux?
inor le
la création de l'enregistreur à l'intérieur de la classe ne crée pas de problèmes. Pouvez-vous montrer un exemple simple qui est difficile à UT parce que la classe crée son propre enregistreur?
inor le
1
Sûr. Que diriez-vous d'un enregistreur qui envoie des e-mails. Je ne veux pas faire cela à chaque fois que vous exécutez vos tests. De plus, comment affirmer l'effet secondaire?
Wayne Allen