Logo AND Algorithmique Numérique Distribuée

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