Parfois, quand je vois mon code de journalisation, je me demande si je le fais bien. Il n'y a peut-être pas de réponse définitive à cela, mais j'ai les préoccupations suivantes:
Classes de bibliothèque
J'ai plusieurs classes de bibliothèque qui peuvent enregistrer certains messages INFO
. Les erreurs fatales sont signalées comme exceptions. Actuellement, j'ai une instance d'enregistreur statique dans mes classes avec le nom de classe comme nom de journalisation. (Log4j's: Logger.getLogger(MyClass.class)
)
Est-ce la bonne façon? L'utilisateur de cette classe de bibliothèque ne souhaite peut-être aucun message de mon implémentation ou souhaite les rediriger vers un journal spécifique à l'application. Dois-je permettre à l'utilisateur de configurer un enregistreur à partir du "monde extérieur"? Comment gérez-vous de tels cas?
Journaux généraux
Dans certaines applications, mes classes peuvent vouloir écrire un message de journal dans un journal spécifique non identifié par le nom de la classe. (C'est à dire.: HTTP Request log
) Quelle est la meilleure façon de faire une telle chose? Un service de recherche vient à l'esprit ...
Vos conventions sont assez standard et assez fines (à mon humble avis).
La seule chose à surveiller est la fragmentation de la mémoire due aux appels de débogage non intégrés excessifs.Ainsi, avec Log4J (et la plupart des autres cadres de journalisation Java), vous vous retrouvez avec quelque chose comme ceci:
if (log.isDebugEnabled()) {
log.debug("...");
}
car la construction de ce message de journal (que vous n'utilisez probablement pas) pourrait être coûteuse, surtout si elle est effectuée des milliers ou des millions de fois.
Votre journalisation de niveau INFO ne doit pas être trop "bavarde" (et d'après ce que vous dites, il semble que ce ne soit pas le cas). Les messages INFO doivent être généralement significatifs et significatifs, comme le démarrage et l'arrêt de l'application. Choses que vous voudrez peut-être savoir si vous rencontrez un problème. La journalisation de débogage/niveau fin est plus utilisée lorsque vous avez réellement un problème que vous essayez de diagnostiquer. Le débogage/journalisation fine n'est généralement activé qu'en cas de besoin. Les informations sont généralement affichées en permanence.
Si quelqu'un ne veut pas de messages INFO spécifiques de vos classes, il est bien sûr libre de modifier votre configuration log4j pour ne pas les obtenir. Log4j est magnifiquement simple dans ce département (par opposition à Java 1.4 logging).
En ce qui concerne votre problème HTTP, je n'ai généralement pas trouvé que c'était un problème avec la journalisation Java car généralement une seule classe est responsable de ce qui vous intéresse, vous n'avez donc qu'à le mettre dans un (rare dans mon expérience) lorsque vous voulez des messages de journal communs à travers des classes apparemment sans rapport, il suffit de mettre un jeton qui peut facilement être recherché.
Ce qui suit est l'ensemble des directives que je respecte dans tous mes projets pour assurer une bonne performance. Je suis venu pour former cet ensemble de directives basées sur les entrées de diverses sources sur Internet.
Comme aujourd'hui, je pense que Log4j 2 est de loin la meilleure option pour se connecter en Java.
Les benchmarks sont disponibles ici . La pratique que je suis pour obtenir les meilleures performances est la suivante:
12:01:00,127 INFO FILE_NAME=file1.txt - Processing starts 12:01:00,127 DEBUG FILE_NAME=file1.txt, CUSTOMER_ID=756 12:01:00,129 INFO FILE_NAME=file1.txt - Processing ends
private static final Marker sqlMarker = MarkerManager.getMarker("SQL"); private void method1() { logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE"); }
int i=5, j=10; logger.info("Sample output {}, {}", ()->i, ()->j);
N'utilisez pas la concaténation de chaînes. Utiliser un message paramétré comme indiqué ci-dessus
Utiliser le rechargement dynamique de la configuration de journalisation pour que l'application recharge automatiquement les modifications de la configuration de journalisation sans avoir besoin de redémarrer l'application
N'utilisez pas printStackTrace()
ou System.out.println()
L'application doit fermer l'enregistreur avant de quitter:
LogManager.shutdown();
<?xml version="1.0" encoding="UTF-8"?> <Configuration monitorinterval="300" status="info" strict="true"> <Properties> <Property name="filePath">${env:LOG_ROOT}/SAMPLE</Property> <Property name="filename">${env:LOG_ROOT}/SAMPLE/sample </Property> <property name="logSize">10 MB</property> </Properties> <Appenders> <RollingFile name="RollingFileRegular" fileName="${filename}.log" filePattern="${filePath}/sample-%d{yyyy-dd-MM}-%i.log"> <Filters> <MarkerFilter marker="SQL" onMatch="DENY" onMismatch="NEUTRAL" /> </Filters> <PatternLayout> <Pattern>%d{HH:mm:ss,SSS} %m%n </Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <SizeBasedTriggeringPolicy size="${logSize}" /> </Policies> </RollingFile> <RollingFile name="RollingFileError" fileName="${filename}_error.log" filePattern="${filePath}/sample_error-%d{yyyy-dd-MM}-%i.log" immediateFlush="true"> <PatternLayout> <Pattern>%d{HH:mm:ss,SSS} %p %c{1.}[%L] [%t] %m%n </Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <SizeBasedTriggeringPolicy size="${logSize}" /> </Policies> </RollingFile> </Appenders> <Loggers> <AsyncLogger name="com" level="trace"> <AppenderRef ref="RollingFileRegular"/> </AsyncLogger> <Root includeLocation="true" level="trace"> <AppenderRef ref="RollingFileError" level="error" /> </Root> </Loggers> </Configuration>
<dependency> <groupId>org.Apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.Apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.3.6</version> </dependency> <!-- (Optional)To be used when working with the applications using Log4j 1.x --> <dependency> <groupId>org.Apache.logging.log4j</groupId> <artifactId>log4j-1.2-api</artifactId> <version>2.8.1</version> </dependency>
Dans @ réponse de cletus , il a écrit sur le problème de
if (log.isDebugEnabled()) {
log.debug("val is " + value);
}
qui pourrait être surmonté en utilisant SL4J . Il fournit une aide au formatage
log.debug("val is {}", value);
où le message est construit uniquement si le niveau est un débogage.
Ainsi, de nos jours, l'utilisation de SL4J et de son enregistreur compagnon, Logback, est conseillé pour des raisons de performances et de stabilité.
En ce qui concerne l'instanciation des enregistreurs, j'ai réussi à utiliser un modèle Eclipse Java pour configurer mes enregistreurs:
private static Logger log = Logger.getLogger(${enclosing_type}.class);
Cela évite le problème d'un nettoyage de la JVM avec votre trace de pile et réduit (trivialement, peut-être) la surcharge de création de la trace de pile en premier lieu.
La grande chose à propos de l'utilisation d'un modèle comme celui-ci est que vous pouvez le partager avec votre équipe si vous souhaitez définir une norme cohérente pour les enregistreurs.
Il semble qu'IntelliJ supporte le même concept pour une variable de modèle représentant le nom du type englobant. Je ne vois pas de moyen de le faire facilement dans NetBeans.
L'option préférée pour le type de configuration log4j que vous décrivez est d'utiliser le fichier de configuration log4j . Cela permet aux utilisateurs de votre implémentation de faire exactement ce que vous demandez, car ils peuvent remplacer votre configuration plus tard par quelque chose de plus adapté à leur propre implémentation. Voir ici pour une introduction très approfondie.
J'ai probablement volé ça quelque part, mais c'est sympa.
Il réduit le risque de mélanger les enregistreurs lors de la copie et de la refactorisation des pâtes, et c'est moins à taper.
Dans votre code:
private final static Logger logger = LoggerFactory.make();
... et dans LoggerFactory:
public static Logger make() {
Throwable t = new Throwable();
StackTraceElement directCaller = t.getStackTrace()[1];
return Logger.getLogger(directCaller.getClassName());
}
(Notez que le stackdump est effectué lors de l'initialisation. Le stacktrace ne sera probablement pas optimisé par la JVM mais il n'y a en fait aucune garantie)
J'examine les niveaux de journalisation d'une application et je détecte actuellement un modèle:
private static final Logger logger = Logger.getLogger(Things.class)
public void bla() {
logger.debug("Starting " + ...)
// Do stuff
...
logger.debug("Situational")
// Algorithms
for(Thing t : things) {
logger.trace(...)
}
// Breaking happy things
if(things.isEmpty){
logger.warn("Things shouldn't be empty!")
}
// Catching things
try {
...
} catch(Exception e) {
logger.error("Something bad happened")
}
logger.info("Completed "+...)
}
Un fichier log4j2 définit un socket-appender, avec un fail-over file-appender. Et une console-appender. Parfois, j'utilise des marqueurs log4j2 lorsque la situation l'exige.
Je pensais qu'une perspective supplémentaire pourrait aider.
En outre, je pense qu'il est important de signifier Simple Logging Facade pour Java (SLF4J) ( http://www.slf4j.org/ ). En raison de certains problèmes d'utilisation de différents cadres de journalisation dans des parties diversifiées d'un grand projet, SLF4J est la norme de facto pour résoudre un problème de gestion réussie de ces parties, n'est-ce pas?
La deuxième notion: il semble que certaines tâches à l'ancienne peuvent être remplacées par Aspect-Oriented-Programming , Spring frmwrk a sa propre implémentation , approche AOP pour la journalisation considérée ici sur StackOverflow et ici sur le blog Spring.