2007-07-08

Un système de "logging" en C

Je travail sur une librairie depuis quelques mois et je désirais y intégrer un système de "logging". Le but est donc de pouvoir suivre ce que fait cette librairie lors de son utilisation. J'ai découvert les fonctions vprintf, etc,.. qui se comportent exactement comme les fonctions printf, mais qui ont le mérite de prendre une va_list comme argument. Du coup, c'est extrêmement pratique pour un système de "logging".

Deux en-têtes sont donc indispensables. Les vprintf appartiennent au stdio.h tout comme les printf, et le va_list est disponible depuis le stdarg.h. Cet article sous-entend donc que vous connaissez déjà ces deux en-têtes. Et donc que vous savez que le stdarg à le mérite de permettre d'écrire des fonctions à paramètres optionnels.

Les messages de log
Nous voulons par exemple générer un message dans le stderr à l'aide de notre fonction de log. Mais dans ce message on aimerait pouvoir passer des valeurs de variables. Comme par exemple:

my_log("variable x=%i, variable y=%s", my_int, my_string);

Il est donc nécessaire de considérer que plusieurs variables peuvent être entrées tout comme pour une fonction printf. Donc notre fonction my_log() doit forcément accepter un nombre optionnel de paramètres.

Exemple:

static void my_log(const char *msg, ...) {
..// (...)
}


Maintenant essayez d'imaginer l'implémentation de cette fonction si vous utilisiez printf ou fprintf. Il serait nécessaire de récupérer chaque paramètres optionnels puis de les interpréter afin de les afficher l'un après l'autre tout en ayant analysé le *msg pour déterminer leurs emplacements. Bref, c'est beaucoup de travail pour pas grand chose. C'est là qu'interviennent les fonctions vprintf.

Quand vous utilisez les arguments optionnels, vous déclarez forcément une variable va_list. Et la fonction va_start() permet de peupler la variable va_list avec les paramètres. Il se trouve que vprintf réclame une variable de type va_list.

Reprenons alors notre exemple en le complétant:

static void my_log(const char *msg, ...) {
..va_list va;

..va_start(va, msg);
..if (msg) {
....vfprintf(stderr, msg, va);
....fprintf(stderr, "\n");
..}
..va_end(va);
}


vfprintf va donc nous éviter un code monstrueux en exploitant directement la liste de paramètres. Ainsi notre fonction de "logging" (minimaliste) est terminée!

1 commentaire:

Anonyme a dit…

pourquoi pas:)