IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Logging Java Discussion :

Java Logging API - évaluation de la qualité de mon code


Sujet :

Logging Java

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2012
    Messages
    23
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2012
    Messages : 23
    Points : 26
    Points
    26
    Par défaut Java Logging API - évaluation de la qualité de mon code
    Bonjour,

    Je m'interroge sur les bonnes pratiques de l'utilisation du de Java Logging API.
    Durant le cours, j' ai retenu que :

    1. Il faut bannir system.out.println() pour le remplacer par un log de type Level.INFO
    2. logger dans un fichier externe. L'ingénieur système peut avoir besoin de ces logs.


    Durant le premier test, j'avais une solution du style :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    public class Application {
     
        public static void main(String[] args) {
     
            Logger logger = null;
     
            try {
     
                //Configuration du logger
                FileHandler out = new FileHandler("C:\\Temp\\Log\\log1.xml");
                logger = Logger.getLogger(src.uni.presentation.Application.class.getName());
                logger.setLevel(Level.INFO);
                logger.addHandler(out); 
     
                //utilisation du logger
                logger.log(Level.INFO, "Hello world");
     
            } catch (IOException ex) {
     
                logger.log(Level.SEVERE, null, ex);
            } 
     
        }
     
    }
    Le code fonctionne, mais j'ai reçu une insuffisance. Il paraît qu'il faudrait faire entre 2 et 3 classes en réalité. J'ai donc retravaillé et produit le code ci-dessous :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    public class Logging {
     
        private final String fichier = "C:\\Temp\\Log\\log.xml";
     
        public Logging(){}
     
        public Logger getLogging(Class classe) throws IOException {
     
                FileHandler out = new FileHandler(fichier);
                Logger logger = Logger.getLogger(classe.getName());
                logger.setLevel(Level.INFO);
                logger.addHandler(out); 
     
                return logger;
     
        }
     
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    public class Application {
     
        public static void main(String[] args) {
     
            Logger logger = null;
     
            try {
     
                Logging log = new Logging();
                logger = log.getLogging(src.hearc.app.Application.class);
     
                logger.log(Level.INFO, "Hello World - info");
                logger.log(Level.SEVERE, "Hello World - Severe");
                //Ne sera pas afficher car SETLevel(Level.INFO)
                logger.log(Level.FINE, "Hello World - Fine");
     
            } catch (IOException ex) {
                logger.log(Level.SEVERE, null, ex);
            }   
        }  
    }
    Pour ceux qui ont de l'expérience, qu'en pensez-vous. Comment puis-je améliorer mon code ? Merci

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Février 2010
    Messages
    765
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 765
    Points : 1 036
    Points
    1 036
    Par défaut
    Bonjour,

    Pour moi le premier problème est un nom de fichier en dur dans le code.
    La plupart du temps le fichier de log est précisé dans un properties, cela permet
    de le changer sans recompiler le code.

    Comme avec l'api log4j ou tu configure ton logger de cette manière.

  3. #3
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    au sujet des logs, sous forme de fichier externe de préférence pour pouvoir être récupéré en effet (mais on pourrait rediriger la console dans un fichier), la règle principale que je respecte personnellement est qu'il faut un maximum d'informations et qu'elles soient pertinentes.

    Comment déterminer les informations nécessaires et suffisantes :
    1. le niveau de log indique l'importance du message par rapport au bon ou mauvais fonctionnement de l'application


      1. le niveau information indique des messages qui permettent de savoir ce que l'application a fait : par exemple Object truc d'id=103 effacé par user d'id=233

      2. le niveau alerte indique par exemple un évenement anormal auquel il est nécessaire de porter une attention particulière : par exemple, Nom d'objet truc d'id=127 non précisé : non par défaut utilisé "Sans nom 12". Ou "Tentative d'accès non autorisé à l'objet d'id 208 par l'utilisateur d'id 233 depuis la vue xxx : cet objet ne devrait pas être visible pour cet utilisateur

      3. ...


    2. pour les messages d'erreur, en plus du contexte de l'erreur (les informations sur les objets concernés par l'erreur), il est nécessaire de savoir ou se trouve l'erreur dans le code et quelles circonstances ont mené à l'erreur


      1. la stacktrace permet en particulier de savoir ou se trouve l'erreur, et quelle "chaine" d'appel y a mené

      2. l'objet qui est concerné par l'erreur pour lequel il faudra peut être intervenir (pour le corriger, le restaurer, etc...) : par exemple, effacement de l'objet bidule d'id 128 annulé à cause d'un time out : objet marqué comme désactivé.

      3. le contexte de l'erreur : par exemple, "erreur lors du changement de la phase de workflow de l'objet d'id 45 de la phase d'id 12 à la phase d'id 11 à cause de l'erreur suivant : nullpointerexception..." suivi d'une stacktrace



    3. enfin d'une manière générale il est toujours utile d'avoir pour tout message


      1. l'endroit ou se trouve l'appel de la méthode de logging (pour retrouver l'emplacement dans le code), donc en gros le nom de la classe, et le numéro de la ligne (éventuellement le nom de la méthode, mais ce n'est pas absolument nécessaire pour moi, c'est un confort)

      2. le nom du thread

      3. le timestamp de l'évenement (sous forme de date et heure lisible, et éventuellement de timestamp pour faire des sélections d'intervalles)



    De plus, il est important que le process de logging ne pénalise pas le déroulement du code : il faut veiller à ce que le maximum de cpu consommé par l'opération de logging se déroule dans un thread différent de celui qui à provoqué la demande de production du message, tout en conservant l'ordre temporelle de production de tous les messages (ce que les API de logging sont prévues pour faire, mais qui doivent parfois être paramétrées pour).
    Il est peut être utile par exemple de ne pas invoquer la méthode de logging si le niveau n'est pas actif, si le message est construit à partir d'un processus long : le message n'étant pas logué au final, autant ne pas consommer de cpu pour produire.

    Il faut également veiller à ce que le logging lui même ne puissent pas provoquer d'erreur nécessitant elle même un log...

    Enfin, il est nécessaire de fusionner la trace de la console (le system.out et le system.err) de manière claire avec le log : une application peut utiliser des apis ou des programmes externes qui tracent malheureusement dans la console et ces messages peuvent être indispensable à comprendre un dysfonctionnement.

    En ce qui concerne le nombre de classes et "l'insufisance" dont tu parles :
    - je ne vois pas ce que tu veux dire par insuffisance
    - Faire des classes pour faire des classes, ne sert à rien. Il y a deux approches pour moi, lorsqu'on utilise une api de loggin, mais elles ne concernent pas le nombre de classes, mais le nombre d'instances : soit on place dans chaque classe une variable statique attachée à la classe et dédiée au log, ce qui permet d'éviter l'appel systématique à la fabrique, mais consomme de la mémoire, soit faire une méthode utilitaire avec les méthodes qui permettent de simplifier la génération du log (ces méthodes veillant à formater les messages sans qu'on soit obligé de répéter ce code pour chaque appel).

    On peut mettre en place éventuellement des classes du type handler : par exemple une classe chargé de gérer le cas d'exception, qu'il suffit d'appeler dans un catch par un truc dans le genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    DBErrorHandler.handle(exception, DBACTION.DELETE,  id1, id2)
    et qui gère automatiquement la production d'un message du genre "une erreur a eu lieu lors d'une opération de type <type d'opération> ; les identifiants des objets concérnés étaient <liste des ids>"


    Pour finir, j'ai mis tous les exemples de messages en français, mais je logue toujours pour ma part en anglais, c'est plus compact, plus cohérent, plus international, etc...
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  4. #4
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2012
    Messages
    23
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2012
    Messages : 23
    Points : 26
    Points
    26
    Par défaut Vos feedbacks
    Citation Envoyé par Jimmy_ Voir le message
    Pour moi le premier problème est un nom de fichier en dur dans le code.
    La plupart du temps le fichier de log est précisé dans un properties, cela permet
    de le changer sans recompiler le code.
    Merci. Le prof m'a dit qu'il n'aimait pas le code en dur, mais ne m'a pas donné de solution. En réfléchissant, c'est vrai que ta solution aide beaucoup pour la maintenance. S'il faut changer le fichier, on le change à un seul endroit

    Citation Envoyé par joel.drigo Voir le message
    En ce qui concerne le nombre de classes et "l'insufisance" dont tu parles :
    - je ne vois pas ce que tu veux dire par insuffisance
    Je parlais d'une note en dessous de la moyenne dans un test. En effet, je suis étudiant.

    J'ai tenu comptes des remarques et j'ai le résultat suivant. Ex de log :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    août 20, 2013 4:41:33 PM ch.uni.ig.presentation.Application main
    SEVERE: main
    ch.uni.ig.exception.NumberException: NumberException - 'impossible d ajouter un client'
    	at ch.uni.ig.business.Classe.addCustomer(Classe.java:29)
    	at ch.uni.ig.business.Customer.addToClasse(Customer.java:22)
    	at ch.uni.ig.presentation.Application.main(Application.java:32)
    J'ai modifié le code ainsi :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
     
    public class Logging {
     
        private static String fichier = null;
        private static Logger logger = null;
     
        public static void initLogging() throws IOException {
     
                Properties prop = new Properties();
                prop.load(Logging.class.getResourceAsStream("fichier.properties"));
                Logging.fichier = prop.getProperty("LogFile");
     
                FileHandler out = new FileHandler(fichier);
                Logging.formatToText(out);
                Logging.logger.setLevel(Level.INFO);
                Logging.logger.addHandler(out); 
     
        }
     
        private static void formatToText(Handler handler) {
     
            SimpleFormatter sf =new SimpleFormatter();
            handler.setFormatter(sf);
     
        }
     
        /**
         * @return the logger
         */
        public static Logger getLogger() {
            return logger;
        }
     
        /**
         * @param aLogger the logger to set
         */
        public static void setLogger(Logger aLogger) {
            logger = aLogger;
        } 
     
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    public class Application {
     
        private static final Logger logger = Logger.getLogger(ch.uni.ig.presentation.Application.class.getName());
     
        public static void main(String[] args) {
     
            try {
     
                Logging.setLogger(logger);
                Logging.initLogging();
     
                logger.log(Level.INFO, "Hello World - info");
     
            } catch (IOException ex) {
                logger.log(Level.SEVERE, Thread.currentThread().getName(), ex);
     
            } catch (NumberException ne) {
               logger.log(Level.SEVERE, Thread.currentThread().getName(), ne);
            } 
        }  
    }
    C'est acceptable comme log ?

  5. #5
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Oui le nom du fichier en dur dans le code c'est pas terrible. Pour moi c'est surtout l'emplacement qu'on doit pouvoir paramétrer et pouvoir le paramétrer simplement : le fait de le mettre dans un fichier de properties permet juste d'éviter de recompiler le code pour le changer, mais pouvoir le changer à la volée dans la ligne de commende peut être un plus : par exemple, le même dans une propriété système (avec -D dans la ligne de commande), qui viendrait surcharger la valeur du fichier propriété.

    De la même façon, au lieu de lire directement le fichier properties dans le code du Logger, faire plutot une méthode setLogFile(File file), et lire la config avec une classe qui hérite d'une abstraction dans le genre AbstractConfig : tu fournie une implémentation concrète qui va lire la config dans un properties, mais tu montres qu'on peut facilement ajouter une implémentation concrète qui irait chercher la config dans un fichier xml, une base de données ou autre.

    En plus, si le nom du fichier de config n'est pas en dur, mais est transmis sous forme de paramètre de ton application sous forme l'url du fichier de config (ou l'url de connexion à une base), tu montres qu'on peut facilement intégrer ton application dans un environnement de production, ou les sysadm ou l'habitude de centraliser les logs mais aussi les configs (voire de les partager).

    Dans le genre on peut faire plein de choses, et toujours plus, difficile de déterminer une limite. Pour avoir travailler sur un serveur maison de type osgi, ou chaque composant pouvait avoir son propre logger, avec des dispatchers qui permettait à des loggers de communiquer entre eux, tout paramétrable, avec valeur par défaut, préférences locales, gestion automatique paramétrable des rotations de logs, et de leur granulosité, connecteurs pour différentes API de log, etc... on peut aller très loin, et comment définir ou est la limite de ce qui est suffisant ou pas. Tiens par exemple, on pourrait dire qu'on pourrait s'attendre à avoir un système de gestion d'alertes, avec envoi de mail paramétrable, lorsqu'on détecte certains motifs dans les logs produit : on a ajouté ça dans notre nouveau système, et ça nous semble aujourd'hui indispensable...
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  6. #6
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Citation Envoyé par Swissboy Voir le message
    Le code fonctionne, mais j'ai reçu une insuffisance. Il paraît qu'il faudrait faire entre 2 et 3 classes en réalité.

    2 et 3 classe pour du logging? Non, personellement, je ne travaille jamais comme ça. Certes, l'api logging de base java n'est pas aussi evoluée que les api système, mais ton code de départ a 2 problèmes:

    1) il code en dur les règles de logging. Si on fait du logging, c'est bien pour pouvoir adapter en fonction des besoinq du jour => fichier de config (en base, c'est ce que fait le logging java)

    2) Moins important et sujet à débat: création du logger dans une méthode: Personellement, c'est toujours un champ de classe que j'utilise, comme ça instancié une fois pour toutes:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Application {
     
     
        Logger logger = Logger.getLogger (Application.class.getName());   
        public static void main(String[] args) {
     
     
            try {
     
                logger.log(Level.INFO, "Hello World - info");
                logger.log(Level.SEVERE, "Hello World - Severe");
                //Ne sera pas afficher car SETLevel(Level.INFO)
                logger.log(Level.FINE, "Hello World - Fine");
     
            } catch (IOException ex) {
                logger.log(Level.SEVERE, null, ex);
            }   
        }  
    }

    Ton code retravaillé, quand à lui, réinvente la roue juste pour ajouter une classe. Tu crée une classe qui récupère le Logger: Logger a déjà ces méthodes (cf mon example).


    Pour changer le fichier de configuration par défaut (JRE/lib/logging.properties), soit tu démarre avec le paramètre:

    -Djava.util.logging.config.file="/path/to/logging/config.properties"

    Ce qui est quand même assez chiant à faire,

    Soit, tu rajoute en première ligne dans ton main un truc du genre

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    LogManager.getLogManager().readConfiguration(new FileInputStream("/path/to/logging/config.properties");

Discussions similaires

  1. [API Java Logging] erreur lock
    Par ites dans le forum Logging
    Réponses: 3
    Dernier message: 04/09/2009, 16h35
  2. [API Java Logging] Message d'erreur
    Par netangel dans le forum Logging
    Réponses: 1
    Dernier message: 14/12/2006, 22h18
  3. [SOAP][JAVA] quelle api utiliser
    Par gandalf72 dans le forum XML/XSL et SOAP
    Réponses: 5
    Dernier message: 23/05/2006, 17h04
  4. [API] Lancer une application externe via un code Java
    Par k o D dans le forum Général Java
    Réponses: 19
    Dernier message: 09/03/2006, 15h12
  5. [Logging]API Logging de Java
    Par trainscoding dans le forum Logging
    Réponses: 6
    Dernier message: 17/01/2005, 15h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo