Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
ee613d911d2ff0aac67aea196864eb9f3fc634f9
[simgrid.git] / src / xbt / log.c
1 /* $Id$ */
2
3 /* log - a generic logging facility in the spirit of log4j                  */
4
5 /* Copyright (c) 2003, 2004 Martin Quinson. All rights reserved.            */
6
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. */
9
10
11 #include <stdarg.h>
12 #include "gras_config.h" /* to get a working stdarg.h */
13 #include <ctype.h>
14 #include <stdio.h> /* snprintf */
15
16 #include "xbt_modinter.h"
17
18 #include "xbt/misc.h"
19 #include "xbt/sysdep.h"
20 #include "xbt/log.h"
21 #include "xbt/error.h"
22 #include "xbt/dynar.h"
23
24 /** \addtogroup XBT_log
25  *
26  *  This section describes the API to the log functions used 
27  *  everywhere in this project.
28      
29 \section log_overview Overview
30      
31 This is an adaptation of the log4c project, which is dead upstream, and
32 which I was given the permission to fork under the LGPL licence by the
33 authors. log4c itself was loosely based on the Apache project's Log4J,
34 Log4CC, etc. project. Because C is not object oriented, a lot had to change.
35     
36 There is 3 main concepts: category, priority and appender. These three
37 concepts work together to enable developers to log messages according to
38 message type and priority, and to control at runtime how these messages are
39 formatted and where they are reported.
40
41 \section log_cat Category hierarchy
42
43 The first and foremost advantage of any logging API over plain printf()
44 resides in its ability to disable certain log statements while allowing
45 others to print unhindered. This capability assumes that the logging space,
46 that is, the space of all possible logging statements, is categorized
47 according to some developer-chosen criteria. 
48           
49 This observation led to choosing category as the central concept of the
50 system. Every category is declared by providing a name and an optional
51 parent. If no parent is explicitly named, the root category, LOG_ROOT_CAT is
52 the category's parent. 
53       
54 A category is created by a macro call at the top level of a file.  A
55 category can be created with any one of the following macros:
56
57  - \ref XBT_LOG_NEW_CATEGORY(MyCat); Create a new root
58  - \ref XBT_LOG_NEW_SUBCATEGORY(MyCat, ParentCat);
59     Create a new category being child of the category ParentCat
60  - \ref XBT_LOG_NEW_DEFAULT_CATEGORY(MyCat);
61     Like XBT_LOG_NEW_CATEGORY, but the new category is the default one
62       in this file
63  -  \ref XBT_LOG_NEW_DEFAULT_SUBCATEGORY(MyCat, ParentCat);
64     Like XBT_LOG_NEW_SUBCATEGORY, but the new category is the default one
65       in this file
66             
67 The parent cat can be defined in the same file or in another file (in
68 which case you want to use the \ref XBT_LOG_EXTERNAL_CATEGORY macro to make
69 it visible in the current file), but each category may have only one
70 definition.
71       
72 Typically, there will be a Category for each module and sub-module, so you
73 can independently control logging for each module.
74
75 For a list of all existing categories, please refer to the \ref XBT_log_cats section.
76
77 \section log_pri Priority
78
79 A category may be assigned a threshold priorty. The set of priorites are
80 defined by the \ref e_xbt_log_priority_t enum. All logging request under
81 this priority will be discarded.
82           
83 If a given category is not assigned a threshold priority, then it inherits
84 one from its closest ancestor with an assigned threshold. To ensure that all
85 categories can eventually inherit a threshold, the root category always has
86 an assigned threshold priority.
87
88 Logging requests are made by invoking a logging macro on a category.  All of
89 the macros have a printf-style format string followed by arguments. If you
90 compile with the -Wall option, gcc will warn you for unmatched arguments, ie
91 when you pass a pointer to a string where an integer was specified by the
92 format. This is usualy a good idea.
93
94 Because most C compilers do not support vararg macros, there is a version of
95 the macro for any number of arguments from 0 to 6. The macro name ends with
96 the total number of arguments.
97         
98 Here is an example of the most basic type of macro. This is a logging
99 request with priority <i>warning</i>.
100
101 <code>CLOG5(MyCat, gras_log_priority_warning, "Values are: %d and '%s'", 5,
102 "oops");</code>
103
104 A logging request is said to be enabled if its priority is higher than or
105 equal to the threshold priority of its category. Otherwise, the request is
106 said to be disabled. A category without an assigned priority will inherit
107 one from the hierarchy. 
108       
109 It is possible to use any non-negative integer as a priority. If, as in the
110 example, one of the standard priorites is used, then there is a convenience
111 macro that is typically used instead. For example, the above example is
112 equivalent to the shorter:
113
114 <code>CWARN4(MyCat, "Values are: %d and '%s'", 5, "oops");</code>
115
116 \subsection log_subcat Using a default category
117   
118 If \ref XBT_LOG_NEW_DEFAULT_SUBCATEGORY(MyCat, Parent) or
119 \ref XBT_LOG_NEW_DEFAULT_CATEGORY(MyCat) is used to create the
120 category, then the even shorter form can be used:
121
122 <code>WARN3("Values are: %d and '%s'", 5, "oops");</code>
123
124 Only one default category can be created per file, though multiple
125 non-defaults can be created and used.
126
127 \section log_example Example
128
129 Here is a more complete example:
130
131 \verbatim
132 #include "xbt/log.h"
133
134 / * create a category and a default subcategory * /
135 XBT_LOG_NEW_CATEGORY(VSS);
136 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(SA, VSS);
137
138 int main() {
139        / * Now set the parent's priority.  (the string would typcially be a runtime option) * /
140        xbt_log_control_set("SA.thresh=3");
141
142        / * This request is enabled, because WARNING &gt;= INFO. * /
143        CWARN2(VSS, "Low fuel level.");
144
145        / * This request is disabled, because DEBUG &lt; INFO. * /
146        CDEBUG2(VSS, "Starting search for nearest gas station.");
147
148        / * The default category SA inherits its priority from VSS. Thus,
149           the following request is enabled because INFO &gt;= INFO.  * /
150        INFO1("Located nearest gas station.");
151
152        / * This request is disabled, because DEBUG &lt; INFO. * /
153        DEBUG1("Exiting gas station search"); 
154 }
155 \endverbatim
156
157 \section log_conf Configuration
158 Configuration is typically done during program initialization by invoking
159 the xbt_log_control_set() method. The control string passed to it typically
160 comes from the command line. Look at the documentation for that function for
161 the format of the control string.
162
163 Any SimGrid program can furthermore be configured at run time by passing a
164 --xbt-log argument on the command line (--gras-log, --msg-log and
165 --surf-log are synonyms). You can provide several of those arguments to
166 change the setting of several categories.
167
168 \section log_perf Performance
169
170 Clever design insures efficiency. Except for the first invocation, a
171 disabled logging request requires an a single comparison of a static
172 variable to a constant.
173
174 There is also compile time constant, \ref XBT_LOG_STATIC_THRESHOLD, which
175 causes all logging requests with a lower priority to be optimized to 0 cost
176 by the compiler. By setting it to gras_log_priority_infinite, all logging
177 requests are statically disabled and cost nothing. Released executables
178 might be compiled with
179 \verbatim-DXBT_LOG_STATIC_THRESHOLD=gras_log_priority_infinite\endverbatim
180
181 Compiling with the \verbatim-DNLOG\endverbatim option disables all logging 
182 requests at compilation time while the \verbatim-DNDEBUG\endverbatim disables 
183 the requests of priority below INFO.
184  
185 \section log_app Appenders
186
187 Each category has an optional appender. An appender is a pointer to a
188 structure which starts with a pointer to a doAppend() function. DoAppend()
189 prints a message to a log.
190
191 When a category is passed a message by one of the logging macros, the
192 category performs the following actions:
193
194   - if the category has an appender, the message is passed to the
195     appender's doAppend() function,
196   - if 'willLogToParent' is true for the category, the message is passed
197     to the category's parent.
198     
199 By default, only the root category have an appender, and 'willLogToParent'
200 is true for any other category. This situation causes all messages to be
201 logged by the root category's appender.
202
203 The default appender function currently prints to stderr, and no other one
204 exist, even if more would be needed, like the one able to send the logs to a
205 remote dedicated server, or other ones offering different output formats.
206 This is on our TODO list for quite a while now, but your help would be
207 welcome here.
208
209 \section log_misc Misc and Caveats
210
211   - Do not use any of the macros that start with '_'.
212   - Log4J has a 'rolling file appender' which you can select with a run-time
213     option and specify the max file size. This would be a nice default for
214     non-kernel applications.
215   - Careful, category names are global variables.
216
217 */
218
219 /*
220 FAIRE DES ZOLIS LOGS
221 --------------------
222 Pour utiliser les logs, tu déjà faire, non ? Tu colle sur la ligne de
223 commande un ou plusieurs arguments de la forme
224   --gras-log="<réglage> [<reglage>+]" (ou sans " si t'as pas d'espace)
225 chaque réglage étant de la forme:
226   <canal>.thres=<priorité>
227 Les différents réglages sont lus de gauche à droite.
228 "root.thres=debug root.thres=critical" ferme tout, normalement.
229
230 */
231
232 typedef struct {
233   char *catname;
234   e_xbt_log_priority_t thresh;
235 } s_xbt_log_setting_t,*xbt_log_setting_t;
236
237 static xbt_dynar_t xbt_log_settings=NULL;
238 static void _free_setting(void *s) {
239   xbt_log_setting_t set=(xbt_log_setting_t)s;
240   if (set) {
241     free(set->catname);
242 /*    free(set); FIXME: uncommenting this leads to segfault when more than one chunk is passed as gras-log */
243   }
244 }
245
246 const char *xbt_log_priority_names[8] = {
247   "NONE",
248   "TRACE",
249   "DEBUG",
250   "VERBOSE",
251   "INFO",
252   "WARNING",
253   "ERROR",
254   "CRITICAL"
255 };
256
257 s_xbt_log_category_t _XBT_LOGV(XBT_LOG_ROOT_CAT) = {
258   0, 0, 0,
259   "root", xbt_log_priority_uninitialized, 0,
260   NULL, 0
261 };
262
263 XBT_LOG_NEW_CATEGORY(xbt,"All XBT categories (simgrid toolbox)");
264 XBT_LOG_NEW_CATEGORY(surf,"All SURF categories");
265 XBT_LOG_NEW_CATEGORY(msg,"All MSG categories");
266 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(log,xbt,"Loggings from the logging mechanism itself");
267
268 void xbt_log_init(int *argc,char **argv) {
269   int i,j;
270   char *opt;
271
272   /* Set logs and init log submodule */
273   for (i=1; i<*argc; i++) {
274     if (!strncmp(argv[i],"--gras-log=",strlen("--gras-log=")) ||
275         !strncmp(argv[i],"--surf-log=",strlen("--surf-log=")) ||
276         !strncmp(argv[i],"--msg-log=",strlen("--msg-log=")) ||
277         !strncmp(argv[i],"--xbt-log=",strlen("--xbt-log="))) {
278       opt=strchr(argv[i],'=');
279       opt++;
280       xbt_log_control_set(opt);
281       DEBUG1("Did apply '%s' as log setting",opt);
282       /*remove this from argv*/
283       for (j=i+1; j<*argc; j++) {
284         argv[j-1] = argv[j];
285       } 
286       argv[j-1] = NULL;
287       (*argc)--;
288       i--; /* compensate effect of next loop incrementation */
289     }
290   }
291 }
292
293 void xbt_log_exit(void) {
294   VERB0("Exiting log");
295   xbt_dynar_free(&xbt_log_settings);
296   VERB0("Exited log");
297 }
298
299 static void _apply_control(xbt_log_category_t cat) {
300   int cursor;
301   xbt_log_setting_t setting=NULL;
302   int found = 0;
303
304   if (!xbt_log_settings)
305     return;
306
307   xbt_assert0(cat,"NULL category");
308   xbt_assert(cat->name);
309
310   xbt_dynar_foreach(xbt_log_settings,cursor,setting) {
311     xbt_assert0(setting,"Damnit, NULL cat in the list");
312     xbt_assert1(setting->catname,"NULL setting(=%p)->catname",(void*)setting);
313
314     if (!strcmp(setting->catname,cat->name)) {
315       found = 1;
316
317       xbt_log_threshold_set(cat, setting->thresh);
318       xbt_dynar_cursor_rm(xbt_log_settings,&cursor);
319
320       if (cat->threshold <= xbt_log_priority_debug) {
321         s_xbt_log_event_t _log_ev = 
322           {cat,xbt_log_priority_debug,__FILE__,_XBT_FUNCTION,__LINE__};
323         _xbt_log_event_log(&_log_ev,
324                  "Apply settings for category '%s': set threshold to %s (=%d)",
325                  cat->name, 
326                  xbt_log_priority_names[cat->threshold], cat->threshold);
327       }
328     }
329   }
330   if (!found && cat->threshold <= xbt_log_priority_verbose) {
331     s_xbt_log_event_t _log_ev = 
332       {cat,xbt_log_priority_verbose,__FILE__,_XBT_FUNCTION,__LINE__};
333     _xbt_log_event_log(&_log_ev,
334                         "Category '%s': inherited threshold = %s (=%d)",
335                         cat->name,
336                         xbt_log_priority_names[cat->threshold], cat->threshold);
337   }
338
339 }
340
341 void _xbt_log_event_log( xbt_log_event_t ev, const char *fmt, ...) {
342   xbt_log_category_t cat = ev->cat;
343   va_start(ev->ap, fmt);
344   while(1) {
345     xbt_log_appender_t appender = cat->appender;
346     if (appender != NULL) {
347       appender->do_append(appender, ev, fmt);
348     }
349     if (!cat->willLogToParent)
350       break;
351
352     cat = cat->parent;
353   } 
354   va_end(ev->ap);
355 }
356
357 static void _cat_init(xbt_log_category_t category) {
358   if (category == &_XBT_LOGV(XBT_LOG_ROOT_CAT)) {
359     category->threshold = xbt_log_priority_info;
360     category->appender = xbt_log_default_appender;
361   } else {
362     xbt_log_parent_set(category, category->parent);
363   }
364   _apply_control(category);
365 }
366
367 /*
368  * This gets called the first time a category is referenced and performs the
369  * initialization. 
370  * Also resets threshold to inherited!
371  */
372 int _xbt_log_cat_init(e_xbt_log_priority_t priority,
373                        xbt_log_category_t   category) {
374     
375   _cat_init(category);
376         
377   return priority >= category->threshold;
378 }
379
380 void xbt_log_parent_set(xbt_log_category_t cat,
381                          xbt_log_category_t parent) {
382
383   xbt_assert0(cat,"NULL category to be given a parent");
384   xbt_assert1(parent,"The parent category of %s is NULL",cat->name);
385
386   /* unlink from current parent */
387   if (cat->threshold != xbt_log_priority_uninitialized) {
388     xbt_log_category_t* cpp = &parent->firstChild;
389     while(*cpp != cat && *cpp != NULL) {
390       cpp = &(*cpp)->nextSibling;
391     }
392     xbt_assert(*cpp == cat);
393     *cpp = cat->nextSibling;
394   }
395
396   /* Set new parent */
397   cat->parent = parent;
398   cat->nextSibling = parent->firstChild;
399   parent->firstChild = cat;
400
401   /* Make sure parent is initialized */
402   if (parent->threshold == xbt_log_priority_uninitialized) {
403     _cat_init(parent);
404   }
405     
406   /* Reset priority */
407   cat->threshold = parent->threshold;
408   cat->isThreshInherited = 1;
409 } /* log_setParent */
410
411 static void _set_inherited_thresholds(xbt_log_category_t cat) {
412   xbt_log_category_t child = cat->firstChild;
413   for( ; child != NULL; child = child->nextSibling) {
414     if (child->isThreshInherited) {
415       if (cat != &_XBT_LOGV(log))
416         VERB3("Set category threshold of %s to %s (=%d)",
417               child->name,xbt_log_priority_names[cat->threshold],cat->threshold);
418       child->threshold = cat->threshold;
419       _set_inherited_thresholds(child);
420     }
421   }
422 }
423
424 void xbt_log_threshold_set(xbt_log_category_t   cat,
425                             e_xbt_log_priority_t threshold) {
426   cat->threshold = threshold;
427   cat->isThreshInherited = 0;
428   _set_inherited_thresholds(cat);
429 }
430
431 static void _xbt_log_parse_setting(const char*        control_string,
432                                     xbt_log_setting_t set) {
433   const char *name, *dot, *eq;
434   
435   set->catname=NULL;
436   if (!*control_string) 
437     return;
438   DEBUG1("Parse log setting '%s'",control_string);
439
440   control_string += strspn(control_string, " ");
441   name = control_string;
442   control_string += strcspn(control_string, ".= ");
443   dot = control_string;
444   control_string += strcspn(control_string, "= ");
445   eq = control_string;
446   control_string += strcspn(control_string, " ");
447
448   xbt_assert1(*dot == '.' && *eq == '=',
449                "Invalid control string '%s'",control_string);
450
451   if (!strncmp(dot + 1, "thresh", min(eq - dot - 1,strlen("thresh")))) {
452     int i;
453     char *neweq=xbt_strdup(eq+1);
454     char *p=neweq-1;
455     
456     while (*(++p) != '\0') {
457       if (*p >= 'a' && *p <= 'z') {
458         *p-='a'-'A';
459       }
460     }
461     
462     DEBUG1("New priority name = %s",neweq);
463     for (i=0; i<xbt_log_priority_infinite-1; i++) {
464       if (!strncmp(xbt_log_priority_names[i],neweq,p-eq)) {
465         DEBUG1("This is priority %d",i);
466         break;
467       }
468     }
469     if (i<xbt_log_priority_infinite-1) {
470       set->thresh=i;
471     } else {
472       xbt_assert1(FALSE,"Unknown priority name: %s",eq+1);
473     }
474     free(neweq);
475   } else {
476     char buff[512];
477     snprintf(buff,min(512,eq - dot - 1),"%s",dot+1);
478     xbt_assert1(FALSE,"Unknown setting of the log category: %s",buff);
479   }
480   set->catname=(char*)xbt_malloc(dot - name+1);
481     
482   strncpy(set->catname,name,dot-name);
483   set->catname[dot-name]='\0'; /* Just in case */
484   DEBUG1("This is for cat '%s'", set->catname);
485 }
486
487 static xbt_error_t _xbt_log_cat_searchsub(xbt_log_category_t cat,char *name,
488                                             /*OUT*/xbt_log_category_t*whereto) {
489   xbt_error_t errcode;
490   xbt_log_category_t child;
491   
492   if (!strcmp(cat->name,name)) {
493     *whereto=cat;
494     return no_error;
495   }
496   for(child=cat->firstChild ; child != NULL; child = child->nextSibling) {
497     errcode=_xbt_log_cat_searchsub(child,name,whereto);
498     if (errcode==no_error)
499       return no_error;
500   }
501   return old_mismatch_error;
502 }
503
504 static void _cleanup_double_spaces(char *s) {
505   char *p = s;
506   int   e = 0;
507   
508   while (1) {
509     if (!*p)
510       goto end;
511     
512     if (!isspace(*p))
513       break;
514     
515     p++;
516   }
517   
518   e = 1;
519   
520   do {
521     if (e)
522       *s++ = *p;
523     
524     if (!*++p)
525       goto end;
526     
527     if (e ^ !isspace(*p))
528       if ((e = !e))
529         *s++ = ' ';
530   } while (1);
531
532  end:
533   *s = '\0';
534 }
535
536 /**
537  * \ingroup XBT_log  
538  * \param control_string What to parse
539  *
540  * Typically passed a command-line argument. The string has the syntax:
541  *
542  *      ( [category] "." [keyword] "=" value (" ")... )...
543  *
544  * where [category] is one the category names and keyword is one of the
545  * following:
546  *
547  *      thres           value is an integer priority level. Sets the category's
548  *                        threshold priority.
549  *
550  * \warning
551  * This routine may only be called once and that must be before any other
552  * logging command! Typically, this is done from main().
553  */
554 void xbt_log_control_set(const char* control_string) {
555   xbt_error_t errcode;
556   xbt_log_setting_t set;
557   char *cs;
558   char *p;
559   int done = 0;
560   
561   DEBUG1("Parse log settings '%s'",control_string);
562   if (control_string == NULL)
563     return;
564   if (xbt_log_settings == NULL)
565     xbt_log_settings = xbt_dynar_new(sizeof(xbt_log_setting_t),
566                                        _free_setting);
567
568   set = xbt_new(s_xbt_log_setting_t,1);
569   cs=xbt_strdup(control_string);
570
571   _cleanup_double_spaces(cs);
572
573   while (!done) {
574     xbt_log_category_t cat;
575     
576     p=strrchr(cs,' ');
577     if (p) {
578       *p='\0';
579       *p++;
580     } else {
581       p=cs;
582       done = 1;
583     }
584     _xbt_log_parse_setting(p,set);
585     
586     errcode = _xbt_log_cat_searchsub(&_XBT_LOGV(root),set->catname,&cat);
587     if (errcode == old_mismatch_error) {
588       DEBUG0("Store for further application");
589       DEBUG1("push %p to the settings",(void*)set);
590       xbt_dynar_push(xbt_log_settings,&set);
591       /* malloc in advance the next slot */
592       set = xbt_new(s_xbt_log_setting_t,1);
593     } else {
594       DEBUG0("Apply directly");
595       free(set->catname);
596       xbt_log_threshold_set(cat,set->thresh);
597     }
598   }
599   free(set);
600   free(cs);
601
602
603 void xbt_log_appender_set(xbt_log_category_t cat, xbt_log_appender_t app) {
604   cat->appender = app;
605 }
606