2010-01-28 13 views
2

(Notez qu'il est plus d'une question de Bash qu'une question Java, voir note ci-dessous)Way pour détecter automatiquement mauvais log4j initialisation statique

Lors de la configuration log4j dans chaque classe, nous faisons ce qui suit:

public class Example { 

    private static final Logger log = Logger.getLogger(Example.class); 

Le problème est que nous avons maintenant une base de code de taille moyenne (200K LOC) contenant beaucoup de classes Java et ... Un certain nombre de loggeurs log4j mal configurés.

C'est parce que les gens (y compris moi, je l'avoue), a fait cut'n'paste stupide résultant parfois ceci:

public class Another { 

    private static final Logger log = Logger.getLogger(Example.class); 

Et boum, au lieu d'avoir Another.class, il est l'ancien Exemple.classe qui est à gauche et apparaît donc à tort dans les journaux (causant ainsi pas mal de maux de tête). Je trouve un peu bizarre qu'un tel type de mauvaise configuration puisse se produire mais il peut et notre problème principal n'est pas que cela puisse arriver mais que nous devions réparer les enregistreurs mal configurés.

Comment pouvons-nous les détecter automatiquement? (le correctif peut être manuel mais j'aimerais trouver un moyen de trouver toutes les classes où log4j est mal configuré). Un script shell Bash, par exemple, serait le bienvenu.

  1. pour chaque fichier .java
  2. trouver tous les "classe XXX"
  3. analyser les lignes suivantes 'x' (disons 20)
  4. est là une ligne Logger.getLogger (...)?
  5. Si oui, correspond-il à la "classe XXX"?
  6. si aucun rapport

de faux positifs est pas un problème il est donc pas un problème si quelques faux « classe XXX » sont analysées etc.

NOTE: le problème est vraiment que nous maintenant avoir 200 000 lignes de code et nous aimerions détecter la violation automatiquement (le correctif peut être manuel) donc la question n'est pas similaire à:

[Existe-t-il un meilleur moyen d'obtenir la variable de classe actuelle dans Java ? 1

En fait, il est probablement plus d'une question de Bash qu'une question Java :)

Toute aide sur ce bienvenu.

Répondre

0

Je suppose que si vous êtes à la recherche d'un one-liner, un revêtement

find -name "*.java" -exec sed -i \ 
    -e 's/private static final Logger \([a-zA-Z_][a-zA-Z0-9_]*).*$/private static final Logger \1 = LoggerFactory.make()/g' \ 
    -e 's/import org\.apache\.log4j\.Logger;/&\nimport path.to.LoggerFactory;/g' \ 
    {} \; 

Je sauvegarder votre code avant d'essayer cela. Il est probablement cassé dans quelques endroits, mais avec quelques corrections vous obtiendrez ce que vous cherchez. Si vous utilisez svn ou quelque chose, vous devrez modifier pour exclure les répertoires .svn, sinon vos commits seront vraiment foirés.

L'essentiel: ne vous embêtez même pas à essayer de capturer des noms de classe. Incorporer the solution indirectly linked to by Alexander. Mais remplacez vos déclarations Logger initial par des appels d'usine. La seule chose que vous devez capturer est le nom de la variable locale. Ensuite, vous devez trouver où sont vos importations, ce que je suppose que vous pouvez faire assez exactement parce que vous importez log4j (ou java.util.logging). Trouvez cette déclaration import et importez votre usine juste en dessous.

BTW, tous les avertissements que vous obtenez sur l'automatisation de ce sont corrects et s'appliquent également à cette solution. Vous besoin de pour être prêt à javac tout juste après avoir essayé cela à tout le moins. Vraiment, vous devriez avoir des suites de test avec une couverture de code monstre à exécuter automatiquement à ce stade.

+0

+1 Votre approche est intéressante. Je n'ai pas songé à changer automagiquement le nom de la classe pour utiliser l'approche make d'usine. Ne vous inquiétez pas du code: Mercurial/hg un peu partout, une couverture de code massive, des tests unitaires etc. Pendant ce temps, j'ai écrit quelque chose de très similaire à ce que Denis a posté et ça a marché. Maintenant, je vais probablement ajouter l'usine et utiliser votre seul doublure. Encore une fois, pas de soucis c'est Mercurial :) – SyntaxT3rr0r

0

Rechercher des solutions dans cette annonce: Is there a better way to get the current class variable in java?

+0

Ce qui est intéressant, mais je n'est pas spécifié ce mon problème: mon problème est que nous avons 200 000 lignes de code où le problème est là, et nous cherchons un moyen automatisé pour détecter les problèmes (par exemple pour ensuite les corriger en utilisant les suggestions que vous avez liées aussi :) – SyntaxT3rr0r

0

vous pouvez essayer le tissage Logger.getLogger avec AspectJ, afin de déterminer si le paramètre, Example.class dans votre cas, est égal à la « classe actuelle » nom.

Conseil: vous pouvez obtenir la "classe actuelle" nom en utilisant quelque chose comme programme:

String className = new Exception().getStackTrace()[0].getClassName(); 
+0

Ah c'est intéressant: je n'ai aucune expérience avec AspectJ qui dit et espérait un moyen de "ligne de commande" pour trouver rapidement la violation. – SyntaxT3rr0r

-1

Regardez dans checkstyle. Vous pouvez écrire une règle personnalisée checkstyle qui le fait. Ce sera un exercice intéressant dans XPath.

Cependant, si le code est structuré de façon très prévisible, j'offre que cela pourrait être fait dans sed. Si vous voulez structurer le calcul en bash, alors ...

  1. utilisation exec pour ouvrir un descripteur de fichier à la boucle fichier
  2. avec tandis que les lignes de lecture
  3. quand vous voyez la première déclaration « classe » , prenez le nom de la classe.
  4. Lorsque vous voyez la construction de l'enregistreur, saisissez et vérifiez.
+0

Je pense que je vais ajouter une nouvelle question, plus précisément formulée alors. Une grande partie de la puissance d'un environnement Un * x a toujours été la capacité à combiner rapidement quelques commandes pour obtenir, de manière pragmatique, le travail accompli. Vous faites comme si j'essayais d'analyser un fichier Java structuré pour récupérer l'AST mais je ne le suis vraiment pas. Ce que je demande est très raisonnable et probablement pas difficile du tout pour quelqu'un de plus familier que moi avec find/awk/grep. – SyntaxT3rr0r

+0

@OldEnthusiast - Si votre code est très, très, prévisible dans le format, alors je ne nierai pas que sed et bash pourraient faire le travail. – bmargulies

+0

@bm - même si ce n'est pas très prévisible, le script sera utilisé avec un humain dans la boucle pour vérifier les faux positifs. Il est maladroit, mais nulle part aussi dangereux que le langage XML regex-ing ... intégré dans un serveur de production. –

0

Untested:

find *.java | while read file 
    do 
     lines=$(grep -A 20 "public class .* {" "$file") 
     class=$(echo "$lines" | sed -n '1 s/public class \(.*\) {/\1/p' 
     log=$(echo "$lines" | grep "Logger.getLogger" 
     log=$(echo "$log" | sed -n 's/.*(*\(.*\).class *).*') 
     if [[ "$log" != "$class" ]] 
     then 
      echo "There's a mis-match in file $file, class $class, for logger $log" 
     fi 
    done 
+0

Merci beaucoup, j'ai fini par écrire quelque chose de similaire et cela m'a permis de trouver quelques classes où l'enregistreur était incorrectement initialisé. – SyntaxT3rr0r

0

Il peut y avoir un détecteur FindBugs - s'il n'y a pas, il est certainement l'un d'écrire ...

Questions connexes