c-2

C++: Créer des logs

Posted by ZedTuX 0n R00t on January 12, 2014

Introduction

Dans tout les languages de programmation, quand quelque chose ne vas pas, et que l’on doit se taper la maintenance… les fichiers de logs sont les bienvenu !

Même si, en tant que développeur indépendant, qui programme pour le plaisir, ca ne semble pas important… c’est si simple de prendre le pli, et d’être tranquille pour plus tard !

(Imaginez que votre petit programme sans intérêt, devient intéressant pour d’autre, que vous le publiez, et que vous avez un rapport de bug)

Que va-t-on utiliser ?

Vue que je suis en plein période de transition vers Java, nous allons utiliser log4cxx.

Il existe d’autres variantes comme log4cpp ou encore log4cplus.

Je ne les ai pas essayés, et il m’as semblé lire qu’il y en a des plus léger, ou plus simple…

A vous de voire.

Le fonctionnement en gros

Bon, en gros, le fonctionne du logger, ou l’architecture plutôt, est formé d’une instance de logger principal (root), puis vous créez autant de logger que vous désirez, qui seront tous rattaché au logger principal.

L’avantage, c’est que vous configurez le logger principal (à l’aide d’un fichier de configuration par exemple), puis les petits logger enverrons leur message à ce dernier, et donc, tous auront la même configuration.

Installation

Comme toutes libraires, il faut l’installer.

J’entend déjà des gens se plaindre d’avoir une dépendance de plus à son projet…

J’étais comme vous aussi, au tout début… Mais finalement, quand ont y pense bien.. on s’en tape pas mal ! Les distributions Linux ont tous des systèmes de paquets de nos jours (ou presque), alors je ne voie pas où est le problème !

Bref, sous notre cher Ubuntu :

1
sudo apt-get install liblog4cxx10-dev

ou

1
sudo apt-get install liblog4cxx9-dev

Voila.

Passons au code !

Je vais travailler sur un exemple que j’ai créé, avant d’écrire cet article.

Pour les includes, il vous faut inclure des fichiers dans le main.cpp:

1
2
3
4
5
// include log4cxx header files.
#include "log4cxx/logger.h"
#include "log4cxx/basicconfigurator.h"
#include "log4cxx/propertyconfigurator.h"
#include "log4cxx/helpers/exception.h"

Puis, si vous êtes un bon informaticien, et donc feignant, vous allez faire 2 petit using namespace:

1
2
using namespace log4cxx;
using namespace log4cxx::helpers;

Maintenant, pour pouvoir logger dans notre main.cpp, il nous faut une instance:

1
LoggerPtr logger(Logger::getLogger("Main"));

Alors, ici nous venons de créer notre premier petit logger.

Toutes les lignes de logs (que ce soit INFO, DEBUG, ERROR Etc…) seront taggé par “Main” ! (Cool hun !?)

Pour fini l’initialisation, il nous reste à configurer votre Logger principal !

Ca va se faire dans le main() juste au tout début dans notre exemple.

Je vous avais parlé d’un fichier de configuration ? Ici, nous le passerons en paramètre afin de pouvoir tester plus simplement différents fichiers de configuration.

1
2
3
4
5
6
if ( argc > 1 )
{
    PropertyConfigurator::configure(argv[1]);
} else {
    BasicConfigurator::configure();
}

Petites explications :

  • BasicConfigurator permet de créer une configuration de base. C’est à dire, ont affiche tout, avec un patron (layout) simple.
  • PropertyConfigurator quand à lui, permet de spécifier le chemin d’un fichier de configuration, qui va configurer le logger principal.

Il est donc possible de créer des lignes de log à partir de là !

Avec le BasicConfigurator, toutes les lignes s’afficheront dans le terminal, sauf si vous changiez les propriétés par le code.

Autrement, avec le fichier de configuration, il est possible de définir un fichier dans lequel écrire les lignes de logs grâce aux appender, dans une base de donnée, dans un fichier XML, etc …

Créer un premier log !

Pour créer vos logs, il ne vous reste plus qu’a utiliser les macros :

  • LOG4CXX_DEBUG
  • LOG4CXX_INFO
  • LOG4CXX_WARN
  • LOG4CXX_ERROR
  • LOG4CXX_FATAL

Voici un exemple de notre main.cpp:

Compilation

Pour compiler ce petit code, voici la ligne à executer dans un terminal, après s’être placer dans le dossier où se trouve le fichier main.cpp :

1
g++ -o log4cxxtst *.cpp -llog4cxx

Lancer l’exemple

Pour lancer et voir le resultat:

1
2
$ ./log4cxxtst
0 [0x7fc4ca4f3770] INFO Main null - Ma première ligne de log avec log4cxx !

Finir le main.cpp

Pour en finir, je vais juste rajouter un try, catch pour gérer les exceptions possibles, et faire 2 lignes de logs, 1 avant d’instancier un objet ClassA et un autre ClassB que vous verrons par la suite :

La suite ?

Maintenant, je vais vous montrer comment travailler avec le logger dans les autres classes d’un projet. (He oui… les projets, ont généralement plus d’1 fichier :-p )

La suite sera beaucoup plus rapide !

Quand vous créer une classe, dans le fichier header (.h) il suffit d’inclure le fichier log4cxx/logger.h. C’est tout !

Ensuite, il faut créer un pointeur de logger, puis l’initialiser dans le(s) constructeur(s), et c’est tout :)

Rendons ceci concret par un exemple :

Fichier header de ClassA :

Puis son fichier cpp :

Donc, dans le fichier header, à la ligne 14, on créer un pointeur dans le private, puis dans le fichier cpp, à la ligne 4, dans le constructeur, il est initialisé, et reçoit le nom de l’objet, de la classe.

Le fichier de configuration

Pour finir, je vais vous parler de fichier de configuration.

Il peut être un fichier XML, ou un simple fichier plat. Pour faire simple, j’utilise ce dernier.

1
2
3
4
5
6
7
8
9
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%r %d{dd/MM/yyyy HH:mm:ss} | %5p | [%F::%c:%L]: %m%n

La première ligne créer un premier type de configuration pour le logger princpal (rootLogger).

Ici il affichera tout les messages (DEBUG est le niveau le plus haut, donc tout les messages, qu’ils soient INFO, ERROR, FATAL etc …) seront affiché.

Le A1 est la référence de cette configuration.

Donc pour configurer ce rootLogger, il faut utiliser la référence A1. (vous pouvez l’appeler comme bon vous semble !).

La ligne suivante définit la manière de sauvegarder les logs. Ici, les messages iront dans la console.

Si vous désiriez les placer dans une base de donnée, ca serai ici qu’il faut le faire.

Puis, avec les dernières lignes, on définis comment afficher les divers informations, tel que la date, l’heure, le type, le tag, le message etc …

Là où l’utilisation de ce fichier devient puissante, c’est lorsque vous désirez afficher dans le terminal les messages INFO, WARN, ERROR et FATAL, mais tout les messages DEBUG, quand à eux, iraient dans un fichier de log.

Il ne vous reste qu’a à créer une configuration comme celle-ci :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Set root logger level to INFO and its only appender to A1.
log4j.rootLogger=INFO, A1

# Set root logger level to DEBUG and its only appender to A2.
log4j.rootLogger=DEBUG, A2

# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%5p %m%n

# Define parameters for log file
log4j.appender.A2=org.apache.log4j.RollingFileAppender
log4j.appender.A2.File=${user.home}/.tuxtremsplit/tuxtremsplit.log
log4j.appender.A2.MaxFileSize=100KB
log4j.appender.A2.MaxBackupIndex=1
log4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern=%r %d{dd/MM/yyyy HH:mm:ss} | %5p | [%F::%c:%L]: %m%n

Conclusion

Voila ! Vous savez logger en C++ !

Je pense que ce n’est pas une perte de temps, et que vous avez tout à y gagner (même si vous n’utilisez pas log4cxx).