web-dev-qa-db-fra.com

Connexion Java et en général: Meilleures pratiques?

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 ...

63
Malax

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é.

37
cletus

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:

  1. J'évite d'utiliser SLF4J pour le moment pour les raisons suivantes:
  2. Effectuez tous les enregistrements réguliers à l'aide d'un enregistreur asynchrone pour de meilleures performances
  3. Consigner les messages d'erreur dans un fichier séparé à l'aide de l'enregistreur synchrone car nous voulons voir les messages d'erreur dès qu'ils se produisent
  4. N'utilisez pas les informations d'emplacement, telles que le nom de fichier, le nom de classe, le nom de méthode, le numéro de ligne dans la journalisation régulière, car afin de dériver ces informations, le framework prend un instantané de la pile et le parcourt. Cela a un impact sur les performances. Par conséquent, utilisez les informations de localisation uniquement dans le journal des erreurs et non dans le journal normal
  5. Dans le but de suivre les demandes individuelles traitées par des threads séparés, envisagez d'utiliser le contexte de thread et l'UUID aléatoire comme expliqué ici
  6. Étant donné que nous enregistrons les erreurs dans un fichier séparé, il est très important que nous enregistrions les informations de contexte également dans le journal des erreurs. Par exemple si l'application a rencontré une erreur lors du traitement d'un fichier, imprimez le nom de fichier et l'enregistrement de fichier en cours de traitement dans le fichier journal des erreurs avec le stacktrace
  7. Le fichier journal doit être facile à comprendre et facile à comprendre. Par exemple si une application traite les enregistrements client dans plusieurs fichiers, chaque message de journal doit être comme ci-dessous:
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
  1. Consignez toutes les instructions SQL à l'aide d'un marqueur SQL comme indiqué ci-dessous et utilisez un filtre pour l'activer ou le désactiver:
private static final Marker sqlMarker = 
  MarkerManager.getMarker("SQL");

private void method1() {
    logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE");
}
  1. Consigner tous les paramètres à l'aide de Java 8 Lambdas. Cela sauvera l'application du message de formatage lorsque le niveau de journal donné est désactivé:
int i=5, j=10;
logger.info("Sample output {}, {}", ()->i, ()->j);
  1. N'utilisez pas la concaténation de chaînes. Utiliser un message paramétré comme indiqué ci-dessus

  2. 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

  3. N'utilisez pas printStackTrace() ou System.out.println()

  4. L'application doit fermer l'enregistreur avant de quitter:

LogManager.shutdown();
  1. Enfin, pour la référence de tout le monde, j'utilise la configuration suivante:
<?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>
  1. Les dépendances Maven requises sont ici:
<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>
9
Saptarshi Basu

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é.

8
serv-inc

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.

6
justinpitts

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.

4
John Feminella

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)

4
KarlP

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.

4
Patrick

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.

2
Yauhen