web-dev-qa-db-fra.com

Quelle est la bonne façon de gérer la sortie de débogage en Java?

Au fur et à mesure que mes projets Java Java grandissent de plus en plus, je ressens un besoin croissant d'insérer la sortie de débogage dans plusieurs points de mon code.

Pour activer ou désactiver cette fonctionnalité de manière appropriée, en fonction de l'ouverture ou de la fermeture des sessions de test, je mets généralement un private static final boolean DEBUG = false au début des classes mes tests inspectent, et l'utiliser trivialement de cette façon (par exemple):

public MyClass {
  private static final boolean DEBUG = false;

  ... some code ...

  public void myMethod(String s) {
    if (DEBUG) {
      System.out.println(s);
    }
  }
}

etc.

Mais cela ne me dérange pas, car cela fonctionne bien sûr, mais il pourrait y avoir trop de classes pour définir DEBUG sur true, si vous n'en fixez pas quelques-unes.

Inversement, je (comme - je pense - beaucoup d'autres) n'aimerais pas mettre toute l'application en mode débogage, car la quantité de texte produite pourrait être écrasante.

Existe-t-il donc une manière correcte de gérer cette situation sur le plan architectural ou la manière la plus correcte consiste à utiliser le membre de classe DEBUG?

32
Federico Zancan

Vous voulez regarder un cadre de journalisation, et peut-être un cadre de façade de journalisation.

Il existe plusieurs cadres de journalisation, souvent avec des fonctionnalités qui se chevauchent, à tel point que, au fil du temps, beaucoup ont évolué pour s'appuyer sur une API commune, ou sont devenus utilisés par le biais d'un cadre de façade pour abstraire leur utilisation et leur permettre d'être échangés sur place si besoin.

Cadres

Quelques cadres de journalisation

Quelques façades forestières

Usage

Exemple de base

La plupart de ces cadres vous permettraient d'écrire quelque chose du formulaire (ici en utilisant slf4j-api Et logback-core):

package chapters.introduction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// copied from: http://www.slf4j.org/manual.html
public class HelloWorld {

  public static void main(String[] args) {
    final Logger logger = LoggerFactory.getLogger(HelloWorld.class);

    logger.debug("Hello world, I'm a DEBUG level message");
    logger.info("Hello world, I'm an INFO level message");
    logger.warn("Hello world, I'm a WARNING level message");
    logger.error("Hello world, I'm an ERROR level message");
  }
}

Notez l'utilisation d'une classe actuelle pour créer un enregistreur dédié, ce qui permettrait à SLF4J/LogBack de formater la sortie et d'indiquer la provenance du message de journalisation.

Comme indiqué dans le manuel SLF4J , un modèle d'utilisation typique dans une classe est généralement:

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  

public class MyClass {

    final Logger logger = LoggerFactory.getLogger(MyCLASS.class);

    public void doSomething() {
        // some code here
        logger.debug("this is useful");

        if (isSomeConditionTrue()) {
            logger.info("I entered by conditional block!");
        }
    }
}

Mais en fait, il est encore plus courant de déclarer l'enregistreur avec le formulaire:

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

Cela permet également à l'enregistreur d'être utilisé à partir de méthodes statiques, et il est partagé entre toutes les instances de la classe. Il s'agit très probablement de votre forme préférée. Cependant, comme l'a noté Brendan Long dans les commentaires, vous voulez être sûr de comprendre les implications et décider en conséquence (cela s'applique à tous les cadres de journalisation suivant ces idiomes).

Il existe d'autres façons d'instancier des enregistreurs, par exemple en utilisant un paramètre de chaîne pour créer un enregistreur nommé:

Logger logger = LoggerFactory.getLogger("MyModuleName");

Niveaux de débogage

Les niveaux de débogage varient d'un framework à un autre, mais les plus courants sont (par ordre de criticité, de bénin à mauvais, et de probablement très commun à, espérons-le, très rare):

  • TRACE  Informations très détaillées. Doit être écrit dans les journaux uniquement. Utilisé uniquement pour suivre le flux du programme aux points de contrôle.

  • DEBUG  Informations détaillées. Doit être écrit uniquement dans les journaux.

  • INFO  Événements d'exécution notables. Doit être immédiatement visible sur une console, donc utilisez-le avec parcimonie.

  • WARNING  Curiosités d'exécution et erreurs récupérables.

  • ERROR  Autres erreurs d'exécution ou conditions inattendues.

  • FATAL  Erreurs graves provoquant une interruption prématurée.

Blocs et gardes

Supposons maintenant que vous ayez une section de code où vous êtes sur le point d'écrire un certain nombre d'instructions de débogage. Cela pourrait rapidement affecter vos performances, à la fois en raison de l'impact de la journalisation elle-même et de la génération des paramètres que vous pourriez transmettre à la méthode de journalisation.

Pour éviter ce genre de problème, vous voulez souvent écrire quelque chose de la forme:

if (LOGGER.isDebugEnabled()) {
   // lots of debug logging here, or even code that
   // is only used in a debugging context.
   LOGGER.debug(" result: " + heavyComputation());
}

Si vous n'aviez pas utilisé cette garde avant votre bloc d'instructions de débogage, même si les messages peuvent ne pas être émis (si, par exemple, votre enregistreur est actuellement configuré pour imprimer uniquement les éléments supérieurs au niveau INFO), le La méthode heavyComputation() aurait toujours été exécutée.

Configuration

La configuration dépend assez de votre infrastructure de journalisation, mais ils offrent principalement les mêmes techniques pour cela:

  • configuration programmatique (à l'exécution, via une API - permet des modifications à l'exécution),
  • configuration déclarative statique (au démarrage, généralement via un fichier XML ou de propriétés - probablement ce dont vous avez besoin au début).

Ils offrent également principalement les mêmes capacités:

  • configuration du format du message de sortie (horodatages, marqueurs, etc ...),
  • configuration des niveaux de sortie,
  • configuration de filtres fins (par exemple pour inclure/exclure des packages ou des classes),
  • configuration des annexes pour déterminer où se connecter (à la console, au fichier, à un service Web ...) et éventuellement quoi faire avec les journaux plus anciens (par exemple, avec les fichiers de roulement automatique).

Voici un exemple courant de configuration déclarative, utilisant un fichier logback.xml.

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Comme mentionné, cela dépend de votre framework et il peut y avoir d'autres alternatives (par exemple, LogBack permet également d'utiliser un script Groovy). Le format de configuration XML peut également varier d'une implémentation à l'autre.

Pour plus d'exemples de configuration, veuillez vous référer (entre autres) à:

Du plaisir historique

Veuillez noter que Log4J voit actuellement une mise à jour majeure, passant de la version 1.x à 2.x . Vous voudrez peut-être jeter un œil aux deux pour plus de plaisir historique ou de confusion, et si vous choisissez Log4J préférez probablement opter pour la version 2.x.

Il convient de noter, comme Mike Partridge l'a mentionné dans les commentaires, que LogBack a été créé par un ancien membre de l'équipe Log4J. Ce qui a été créé pour combler les lacunes du cadre de journalisation Java. Et que la prochaine version majeure de Log4J 2.x intègre elle-même maintenant quelques fonctionnalités tirées de LogBack.

Recommandation

En bout de ligne, restez découplé autant que vous le pouvez, jouez avec quelques-uns et voyez ce qui vous convient le mieux. En fin de compte ce n'est qu'un cadre de journalisation . Sauf si vous avez une raison très précise, en dehors de la facilité d'utilisation et des préférences personnelles, l'une d'entre elles serait plutôt OK, il n'y a donc pas de raison de s'y accrocher. La plupart d'entre eux peuvent également être étendus à vos besoins.

Pourtant, si je devais choisir une combinaison aujourd'hui, j'irais avec LogBack + SLF4J. Mais si vous me l'aviez demandé quelques années plus tard, j'aurais recommandé Log4J avec Apache Commons Logging, alors gardez un œil sur vos dépendances et évoluez avec elles.

52
haylem

utiliser un cadre de journalisation

la plupart du temps, il y a une méthode d'usine statique

private static final Logger logger = Logger.create("classname");

alors vous pouvez sortir votre code de journalisation avec différents niveaux:

logger.warning("error message");
logger.info("informational message");
logger.trace("detailed message");

alors il y aura un seul fichier où vous pouvez définir quels messages pour chaque classe doivent être écrits dans la sortie du journal (fichier ou stderr)

2
ratchet freak

C'est exactement ce à quoi sont destinés les frameworks de journalisation comme log4j ou le plus récent slf4j. Ils vous permettent de contrôler la journalisation dans les moindres détails et de la configurer même lorsque l'application est en cours d'exécution.

1
Michael Borgwardt

Un cadre de journalisation est certainement la voie à suivre. Cependant, vous devez également disposer d'une bonne suite de tests. Une bonne couverture des tests peut souvent éliminer la nécessité d'une sortie de débogage tous ensemble.

0
Dima