3 /* log - a generic logging facility in the spirit of log4j */
5 /* Copyright (c) 2003, 2004 Martin Quinson. All rights reserved. */
7 /* This program is free software; you can redistribute it and/or modify it
8 * under the terms of the license (GNU LGPL) which comes with this package. */
13 #include "xbt_modinter.h"
16 #include "xbt/sysdep.h"
18 #include "xbt/error.h"
19 #include "xbt/dynar.h"
25 Dans gras, tu ne te contente pas d'écrire des choses à l'écran, mais tu
26 écris sur un sujet particulier (notion de canal) des choses d'une gravité
27 particulière. Il y a 7 niveaux de gravité.
28 trace: tracer les entrées dans une fonction, retour de fonction
29 (famille de macros XBT_IN/XBT_OUT)
30 debug: pour t'aider à mettre au point le module, potentiellement tres bavard
31 verbose: quelques infos succintes sur les internals du module
32 info: niveau normal, ton de la conversation
33 warning: problème potentiel, mais auquel on a su faire face
34 error: problème qui t'as empêché de faire ton job
35 critical: juste avant de mourir
37 Quand on compile avec -DNDEBUG (par défaut dans le paquet Debian), tout ce
38 qui est '>= verbose' est supprimé au moment de la compilation. Retiré du
41 Ensuite, tu écris dans un canal particulier. Tous les canaux sont rangés en
42 arbre. Il faudrait faire un ptit script qui fouille les sources à la
43 recherche des macros XBT_LOG_NEW_* utilisées pour créer des canaux. Le
44 dernier argument de ces macros est ignoré dans le source. Il est destiné à
45 être la documentation de la chose en une ligne. En gros, ca fait:
66 +--raw_trp (Je devrais tuer ce module, un jour)
72 Et ensuite les utilisateurs peuvent choisir le niveau de gravité qui les
73 interresse sur tel ou tel sujet.
75 Toute la mécanique de logging repose sur des variables statiques dont le nom
76 dépend du nom du canal.
77 => attention aux conflits de nom de canal
78 => il faut une macro XBT_LOG dans chaque fichier où tu fais des logs.
80 XBT_LOG_NEW_CATEGORY: nouveau canal sous "root". Rare, donc.
81 XBT_LOG_NEW_SUBCATEGORY: nouveau canal dont on précise le père.
82 XBT_LOG_DEFAULT_CATEGORY: indique quel est le canal par défaut dans ce fichier
83 XBT_LOG_NEW_DEFAULT_CATEGORY: Crèe un canal et l'utilise par défaut
84 XBT_LOG_NEW_DEFAULT_SUBCATEGORY: devine
85 XBT_LOG_EXTERNAL_CATEGORY: quand tu veux utiliser par défaut un canal créé
86 dans un autre fichier.
88 Une fois que ton canal est créé, tu l'utilise avec les macros LOG, DEBUG,
89 VERB, WARN, ERROR et CRITICAL. Il faut que tu donne le nombre d'arguments
90 après le nom de macro. Exemple: LOG2("My name is %s %s","Martin","Quinson")
91 Si tu veux préciser explicitement le canal où écrire, ajoute un C devant le
92 nom de la macro. Exemple: CCRITICAL0(module, "Cannot initialize GRAS")
94 Toutes ces macros (enfin, ce en quoi elles se réécrivent) vérifient leurs
95 arguments comme printf le fait lorsqu'on compile avec gcc.
96 LOG1("name: %d","toto"); donne un warning, et donc une erreur en mode
99 Enfin, tu peux tester si un canal est ouvert à une priorité donnée (pour
100 préparer plus de débug, par exemple. Dans le parseur, je fais du pretty
101 printing sur ce qu'il faut parser dans ce cas).
102 XBT_LOG_ISENABLED(catName, priority) Le second argument doit être une valeur
103 de e_xbt_log_priority_t (log.h). Par exemple: xbt_log_priority_verbose
105 Voila sur comment mettre des logs dans ton code. N'hesite pas à faire pleins
106 de canaux différents pour des aspects différents de ton code. En
107 particulier, dans les dict, j'ai un canal pour l'ajout, le retrait, le
108 netoyage du code après suppression et ainsi de suite. De cette façon, je
109 peux choisir qui m'interresse.
112 Pour utiliser les logs, tu déjà faire, non ? Tu colle sur la ligne de
113 commande un ou plusieurs arguments de la forme
114 --gras-log="<réglage> [<reglage>+]" (ou sans " si t'as pas d'espace)
115 chaque réglage étant de la forme:
116 <canal>.thres=<priorité>
117 Les différents réglages sont lus de gauche à droite.
118 "root.thres=debug root.thres=critical" ferme tout, normalement.
124 e_xbt_log_priority_t thresh;
125 } s_xbt_log_setting_t,*xbt_log_setting_t;
127 static xbt_dynar_t xbt_log_settings=NULL;
128 static void _free_setting(void *s) {
129 xbt_log_setting_t set=(xbt_log_setting_t)s;
131 xbt_free(set->catname);
132 /* xbt_free(set); FIXME: uncommenting this leads to segfault when more than one chunk is passed as gras-log */
136 const char *xbt_log_priority_names[8] = {
147 s_xbt_log_category_t _XBT_LOGV(XBT_LOG_ROOT_CAT) = {
149 "root", xbt_log_priority_uninitialized, 0,
153 XBT_LOG_NEW_SUBCATEGORY(xbt,XBT_LOG_ROOT_CAT,"All XBT categories (simgrid toolbox)");
154 XBT_LOG_NEW_SUBCATEGORY(surf,XBT_LOG_ROOT_CAT,"All SURF categories");
155 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(log,xbt,"Loggings from the logging mecanism itself");
157 void xbt_log_init(int *argc,char **argv, const char *defaultlog) {
162 /** Set logs and init log submodule */
163 for (i=1; i<*argc; i++) {
164 if (!strncmp(argv[i],"--gras-log=",strlen("--gras-log="))) {
166 opt=strchr(argv[i],'=');
168 xbt_log_control_set(opt);
169 DEBUG1("Did apply '%s' as log setting",opt);
170 /*remove this from argv*/
171 for (j=i+1; j<*argc; j++) {
176 i--; /* compensate effect of next loop incrementation */
179 if (!found && defaultlog) {
180 xbt_log_control_set(defaultlog);
184 void xbt_log_exit(void) {
185 VERB0("Exiting log");
186 xbt_dynar_free(&xbt_log_settings);
190 static void _apply_control(xbt_log_category_t cat) {
192 xbt_log_setting_t setting=NULL;
195 if (!xbt_log_settings)
198 xbt_assert0(cat,"NULL category");
199 xbt_assert(cat->name);
201 xbt_dynar_foreach(xbt_log_settings,cursor,setting) {
202 xbt_assert0(setting,"Damnit, NULL cat in the list");
203 xbt_assert1(setting->catname,"NULL setting(=%p)->catname",(void*)setting);
205 if (!strcmp(setting->catname,cat->name)) {
208 xbt_log_threshold_set(cat, setting->thresh);
209 xbt_dynar_cursor_rm(xbt_log_settings,&cursor);
211 if (cat->threshold <= xbt_log_priority_verbose) {
212 s_xbt_log_event_t _log_ev =
213 {cat,xbt_log_priority_verbose,__FILE__,_XBT_GNUC_FUNCTION,__LINE__};
214 _xbt_log_event_log(&_log_ev,
215 "Apply settings for category '%s': set threshold to %s (=%d)",
217 xbt_log_priority_names[cat->threshold], cat->threshold);
221 if (!found && cat->threshold <= xbt_log_priority_verbose) {
222 s_xbt_log_event_t _log_ev =
223 {cat,xbt_log_priority_verbose,__FILE__,_XBT_GNUC_FUNCTION,__LINE__};
224 _xbt_log_event_log(&_log_ev,
225 "Category '%s': inherited threshold = %s (=%d)",
227 xbt_log_priority_names[cat->threshold], cat->threshold);
232 void _xbt_log_event_log( xbt_log_event_t ev, const char *fmt, ...) {
233 xbt_log_category_t cat = ev->cat;
234 va_start(ev->ap, fmt);
236 xbt_log_appender_t appender = cat->appender;
237 if (appender != NULL) {
238 appender->do_append(appender, ev, fmt);
240 if (!cat->willLogToParent)
248 static void _cat_init(xbt_log_category_t category) {
249 if (category == &_XBT_LOGV(XBT_LOG_ROOT_CAT)) {
250 category->threshold = xbt_log_priority_info;
251 category->appender = xbt_log_default_appender;
253 xbt_log_parent_set(category, category->parent);
255 _apply_control(category);
259 * This gets called the first time a category is referenced and performs the
261 * Also resets threshold to inherited!
263 int _xbt_log_cat_init(e_xbt_log_priority_t priority,
264 xbt_log_category_t category) {
268 return priority >= category->threshold;
271 void xbt_log_parent_set(xbt_log_category_t cat,
272 xbt_log_category_t parent) {
274 xbt_assert0(cat,"NULL category to be given a parent");
275 xbt_assert1(parent,"The parent category of %s is NULL",cat->name);
277 /* unlink from current parent */
278 if (cat->threshold != xbt_log_priority_uninitialized) {
279 xbt_log_category_t* cpp = &parent->firstChild;
280 while(*cpp != cat && *cpp != NULL) {
281 cpp = &(*cpp)->nextSibling;
283 xbt_assert(*cpp == cat);
284 *cpp = cat->nextSibling;
288 cat->parent = parent;
289 cat->nextSibling = parent->firstChild;
290 parent->firstChild = cat;
292 /* Make sure parent is initialized */
293 if (parent->threshold == xbt_log_priority_uninitialized) {
298 cat->threshold = parent->threshold;
299 cat->isThreshInherited = 1;
300 } /* log_setParent */
302 static void _set_inherited_thresholds(xbt_log_category_t cat) {
303 xbt_log_category_t child = cat->firstChild;
304 for( ; child != NULL; child = child->nextSibling) {
305 if (child->isThreshInherited) {
306 if (cat != &_XBT_LOGV(log))
307 VERB3("Set category threshold of %s to %s (=%d)",
308 child->name,xbt_log_priority_names[cat->threshold],cat->threshold);
309 child->threshold = cat->threshold;
310 _set_inherited_thresholds(child);
315 void xbt_log_threshold_set(xbt_log_category_t cat,
316 e_xbt_log_priority_t threshold) {
317 cat->threshold = threshold;
318 cat->isThreshInherited = 0;
319 _set_inherited_thresholds(cat);
322 static void _xbt_log_parse_setting(const char* control_string,
323 xbt_log_setting_t set) {
324 const char *name, *dot, *eq;
327 if (!*control_string)
329 DEBUG1("Parse log setting '%s'",control_string);
331 control_string += strspn(control_string, " ");
332 name = control_string;
333 control_string += strcspn(control_string, ".= ");
334 dot = control_string;
335 control_string += strcspn(control_string, "= ");
337 control_string += strcspn(control_string, " ");
339 xbt_assert1(*dot == '.' && *eq == '=',
340 "Invalid control string '%s'",control_string);
342 if (!strncmp(dot + 1, "thresh", min(eq - dot - 1,strlen("thresh")))) {
344 char *neweq=xbt_strdup(eq+1);
347 while (*(++p) != '\0') {
348 if (*p >= 'a' && *p <= 'z') {
353 DEBUG1("New priority name = %s",neweq);
354 for (i=0; i<xbt_log_priority_infinite-1; i++) {
355 if (!strncmp(xbt_log_priority_names[i],neweq,p-eq)) {
356 DEBUG1("This is priority %d",i);
360 if (i<xbt_log_priority_infinite-1) {
363 xbt_assert1(FALSE,"Unknown priority name: %s",eq+1);
368 snprintf(buff,min(512,eq - dot - 1),"%s",dot+1);
369 xbt_assert1(FALSE,"Unknown setting of the log category: %s",buff);
371 set->catname=(char*)xbt_malloc(dot - name+1);
373 strncpy(set->catname,name,dot-name);
374 set->catname[dot-name]='\0'; /* Just in case */
375 DEBUG1("This is for cat '%s'", set->catname);
378 static xbt_error_t _xbt_log_cat_searchsub(xbt_log_category_t cat,char *name,
379 /*OUT*/xbt_log_category_t*whereto) {
381 xbt_log_category_t child;
383 if (!strcmp(cat->name,name)) {
387 for(child=cat->firstChild ; child != NULL; child = child->nextSibling) {
388 errcode=_xbt_log_cat_searchsub(child,name,whereto);
389 if (errcode==no_error)
392 return mismatch_error;
395 static void _cleanup_double_spaces(char *s) {
418 if (e ^ !isspace(*p))
429 * \param control_string What to parse
431 * Typically passed a command-line argument. The string has the syntax:
433 * ( [category] "." [keyword] "=" value (" ")... )...
435 * where [category] is one the category names and keyword is one of the
438 * thresh value is an integer priority level. Sets the category's
439 * threshold priority.
442 * This routine may only be called once and that must be before any other
443 * logging command! Typically, this is done from main().
445 void xbt_log_control_set(const char* control_string) {
447 xbt_log_setting_t set;
452 DEBUG1("Parse log settings '%s'",control_string);
453 if (control_string == NULL)
455 if (xbt_log_settings == NULL)
456 xbt_log_settings = xbt_dynar_new(sizeof(xbt_log_setting_t),
459 set = xbt_new(s_xbt_log_setting_t,1);
460 cs=xbt_strdup(control_string);
462 _cleanup_double_spaces(cs);
465 xbt_log_category_t cat;
475 _xbt_log_parse_setting(p,set);
477 errcode = _xbt_log_cat_searchsub(&_XBT_LOGV(root),set->catname,&cat);
478 if (errcode == mismatch_error) {
479 DEBUG0("Store for further application");
480 DEBUG1("push %p to the settings",(void*)set);
481 xbt_dynar_push(xbt_log_settings,&set);
482 /* malloc in advance the next slot */
483 set = xbt_new(s_xbt_log_setting_t,1);
485 DEBUG0("Apply directly");
486 xbt_free(set->catname);
487 xbt_log_threshold_set(cat,set->thresh);
494 void xbt_log_appender_set(xbt_log_category_t cat, xbt_log_appender_t app) {