Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Use doxygen's section instead of my poor approximation; link to the list of all exist...
[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 #include <stdarg.h>
11 #include <ctype.h>
12
13 #include "xbt_modinter.h"
14
15 #include "xbt/misc.h"
16 #include "xbt/sysdep.h"
17 #include "xbt/log.h"
18 #include "xbt/error.h"
19 #include "xbt/dynar.h"
20
21
22 /** \defgroup XBT_log Logging support.
23  *  \brief A generic logging facility in the spirit of log4j
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 Dans gras, tu ne te contente pas d'écrire des choses à l'écran, mais tu
222 écris sur un sujet particulier (notion de canal) des choses d'une gravité
223 particulière. Il y a 7 niveaux de gravité.
224  trace: tracer les entrées dans une fonction, retour de fonction
225         (famille de macros XBT_IN/XBT_OUT)
226  debug: pour t'aider à mettre au point le module, potentiellement tres bavard
227  verbose: quelques infos succintes sur les internals du module
228  info: niveau normal, ton de la conversation
229  warning: problème potentiel, mais auquel on a su faire face
230  error: problème qui t'as empêché de faire ton job
231  critical: juste avant de mourir
232
233 Quand on compile avec -DNDEBUG (par défaut dans le paquet Debian), tout ce
234 qui est '>= verbose' est supprimé au moment de la compilation. Retiré du
235 binaire, killé.
236
237 Ensuite, tu écris dans un canal particulier. Tous les canaux sont rangés en
238 arbre. Il faudrait faire un ptit script qui fouille les sources à la
239 recherche des macros XBT_LOG_NEW_* utilisées pour créer des canaux. Le
240 dernier argument de ces macros est ignoré dans le source. Il est destiné à
241 être la documentation de la chose en une ligne. En gros, ca fait:
242 root
243  +--xbt
244  |   +--config
245  |   +--dict
246  |   |   +--dict_cursor
247  |   |   +--dict_elm
248  |   |   ...
249  |   +--dynar
250  |   +--set
251  |   +--log
252  |   +--module
253  +--gras
254      +--datadesc
255      |   +--ddt_cbps
256      |   +--ddt_convert
257      |   +--ddt_exchange
258      |   +--ddt_parse
259      |       +--lexer
260      +--msg
261      +--transport
262          +--raw_trp (Je devrais tuer ce module, un jour)
263          +--trp_buf
264          +--trp_sg
265          +--trp_file
266          +--trp_tcp
267          
268 Et ensuite les utilisateurs peuvent choisir le niveau de gravité qui les
269 interresse sur tel ou tel sujet.
270
271 Toute la mécanique de logging repose sur des variables statiques dont le nom
272 dépend du nom du canal.
273  => attention aux conflits de nom de canal
274  => il faut une macro XBT_LOG dans chaque fichier où tu fais des logs.
275  
276 XBT_LOG_NEW_CATEGORY: nouveau canal sous "root". Rare, donc.
277 XBT_LOG_NEW_SUBCATEGORY: nouveau canal dont on précise le père.
278 XBT_LOG_DEFAULT_CATEGORY: indique quel est le canal par défaut dans ce fichier
279 XBT_LOG_NEW_DEFAULT_CATEGORY: Crèe un canal et l'utilise par défaut
280 XBT_LOG_NEW_DEFAULT_SUBCATEGORY: devine
281 XBT_LOG_EXTERNAL_CATEGORY: quand tu veux utiliser par défaut un canal créé
282                            dans un autre fichier.
283
284 Une fois que ton canal est créé, tu l'utilise avec les macros LOG, DEBUG,
285 VERB, WARN, ERROR et CRITICAL. Il faut que tu donne le nombre d'arguments
286 après le nom de macro. Exemple: LOG2("My name is %s %s","Martin","Quinson")
287 Si tu veux préciser explicitement le canal où écrire, ajoute un C devant le
288 nom de la macro. Exemple: CCRITICAL0(module, "Cannot initialize GRAS")
289
290 Toutes ces macros (enfin, ce en quoi elles se réécrivent) vérifient leurs
291 arguments comme printf le fait lorsqu'on compile avec gcc. 
292 LOG1("name: %d","toto"); donne un warning, et donc une erreur en mode
293 mainteneur.
294
295 Enfin, tu peux tester si un canal est ouvert à une priorité donnée (pour
296 préparer plus de débug, par exemple. Dans le parseur, je fais du pretty
297 printing sur ce qu'il faut parser dans ce cas).
298 XBT_LOG_ISENABLED(catName, priority) Le second argument doit être une valeur
299 de e_xbt_log_priority_t (log.h). Par exemple: xbt_log_priority_verbose
300
301 Voila sur comment mettre des logs dans ton code. N'hesite pas à faire pleins
302 de canaux différents pour des aspects différents de ton code. En
303 particulier, dans les dict, j'ai un canal pour l'ajout, le retrait, le
304 netoyage du code après suppression et ainsi de suite. De cette façon, je
305 peux choisir qui m'interresse.
306
307
308 Pour utiliser les logs, tu déjà faire, non ? Tu colle sur la ligne de
309 commande un ou plusieurs arguments de la forme
310   --gras-log="<réglage> [<reglage>+]" (ou sans " si t'as pas d'espace)
311 chaque réglage étant de la forme:
312   <canal>.thres=<priorité>
313 Les différents réglages sont lus de gauche à droite.
314 "root.thres=debug root.thres=critical" ferme tout, normalement.
315
316 */
317
318 typedef struct {
319   char *catname;
320   e_xbt_log_priority_t thresh;
321 } s_xbt_log_setting_t,*xbt_log_setting_t;
322
323 static xbt_dynar_t xbt_log_settings=NULL;
324 static void _free_setting(void *s) {
325   xbt_log_setting_t set=(xbt_log_setting_t)s;
326   if (set) {
327     xbt_free(set->catname);
328 /*    xbt_free(set); FIXME: uncommenting this leads to segfault when more than one chunk is passed as gras-log */
329   }
330 }
331
332 const char *xbt_log_priority_names[8] = {
333   "NONE",
334   "TRACE",
335   "DEBUG",
336   "VERBOSE",
337   "INFO",
338   "WARNING",
339   "ERROR",
340   "CRITICAL"
341 };
342
343 s_xbt_log_category_t _XBT_LOGV(XBT_LOG_ROOT_CAT) = {
344   0, 0, 0,
345   "root", xbt_log_priority_uninitialized, 0,
346   NULL, 0
347 };
348
349 XBT_LOG_NEW_CATEGORY(xbt,"All XBT categories (simgrid toolbox)");
350 XBT_LOG_NEW_CATEGORY(surf,"All SURF categories");
351 XBT_LOG_NEW_CATEGORY(msg,"All MSG categories");
352 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(log,xbt,"Loggings from the logging mecanism itself");
353
354 void xbt_log_init(int *argc,char **argv, const char *defaultlog) {
355   int i,j;
356   char *opt;
357   int found=0;
358
359   /* Set logs and init log submodule */
360   for (i=1; i<*argc; i++) {
361     if (!strncmp(argv[i],"--gras-log=",strlen("--gras-log=")) ||
362         !strncmp(argv[i],"--surf-log=",strlen("--surf-log=")) ||
363         !strncmp(argv[i],"--msg-log=",strlen("--msg-log=")) ||
364         !strncmp(argv[i],"--xbt-log=",strlen("--xbt-log="))) {
365       found = 1;
366       opt=strchr(argv[i],'=');
367       opt++;
368       xbt_log_control_set(opt);
369       DEBUG1("Did apply '%s' as log setting",opt);
370       /*remove this from argv*/
371       for (j=i+1; j<*argc; j++) {
372         argv[j-1] = argv[j];
373       } 
374       argv[j-1] = NULL;
375       (*argc)--;
376       i--; /* compensate effect of next loop incrementation */
377     }
378   }
379   if (!found && defaultlog) {
380      xbt_log_control_set(defaultlog);
381   }
382 }
383
384 void xbt_log_exit(void) {
385   VERB0("Exiting log");
386   xbt_dynar_free(&xbt_log_settings);
387   VERB0("Exited log");
388 }
389
390 static void _apply_control(xbt_log_category_t cat) {
391   int cursor;
392   xbt_log_setting_t setting=NULL;
393   int found = 0;
394
395   if (!xbt_log_settings)
396     return;
397
398   xbt_assert0(cat,"NULL category");
399   xbt_assert(cat->name);
400
401   xbt_dynar_foreach(xbt_log_settings,cursor,setting) {
402     xbt_assert0(setting,"Damnit, NULL cat in the list");
403     xbt_assert1(setting->catname,"NULL setting(=%p)->catname",(void*)setting);
404
405     if (!strcmp(setting->catname,cat->name)) {
406       found = 1;
407
408       xbt_log_threshold_set(cat, setting->thresh);
409       xbt_dynar_cursor_rm(xbt_log_settings,&cursor);
410
411       if (cat->threshold <= xbt_log_priority_verbose) {
412         s_xbt_log_event_t _log_ev = 
413           {cat,xbt_log_priority_verbose,__FILE__,_XBT_GNUC_FUNCTION,__LINE__};
414         _xbt_log_event_log(&_log_ev,
415                  "Apply settings for category '%s': set threshold to %s (=%d)",
416                  cat->name, 
417                  xbt_log_priority_names[cat->threshold], cat->threshold);
418       }
419     }
420   }
421   if (!found && cat->threshold <= xbt_log_priority_verbose) {
422     s_xbt_log_event_t _log_ev = 
423       {cat,xbt_log_priority_verbose,__FILE__,_XBT_GNUC_FUNCTION,__LINE__};
424     _xbt_log_event_log(&_log_ev,
425                         "Category '%s': inherited threshold = %s (=%d)",
426                         cat->name,
427                         xbt_log_priority_names[cat->threshold], cat->threshold);
428   }
429
430 }
431
432 void _xbt_log_event_log( xbt_log_event_t ev, const char *fmt, ...) {
433   xbt_log_category_t cat = ev->cat;
434   va_start(ev->ap, fmt);
435   while(1) {
436     xbt_log_appender_t appender = cat->appender;
437     if (appender != NULL) {
438       appender->do_append(appender, ev, fmt);
439     }
440     if (!cat->willLogToParent)
441       break;
442
443     cat = cat->parent;
444   } 
445   va_end(ev->ap);
446 }
447
448 static void _cat_init(xbt_log_category_t category) {
449   if (category == &_XBT_LOGV(XBT_LOG_ROOT_CAT)) {
450     category->threshold = xbt_log_priority_info;
451     category->appender = xbt_log_default_appender;
452   } else {
453     xbt_log_parent_set(category, category->parent);
454   }
455   _apply_control(category);
456 }
457
458 /*
459  * This gets called the first time a category is referenced and performs the
460  * initialization. 
461  * Also resets threshold to inherited!
462  */
463 int _xbt_log_cat_init(e_xbt_log_priority_t priority,
464                        xbt_log_category_t   category) {
465     
466   _cat_init(category);
467         
468   return priority >= category->threshold;
469 }
470
471 void xbt_log_parent_set(xbt_log_category_t cat,
472                          xbt_log_category_t parent) {
473
474   xbt_assert0(cat,"NULL category to be given a parent");
475   xbt_assert1(parent,"The parent category of %s is NULL",cat->name);
476
477   /* unlink from current parent */
478   if (cat->threshold != xbt_log_priority_uninitialized) {
479     xbt_log_category_t* cpp = &parent->firstChild;
480     while(*cpp != cat && *cpp != NULL) {
481       cpp = &(*cpp)->nextSibling;
482     }
483     xbt_assert(*cpp == cat);
484     *cpp = cat->nextSibling;
485   }
486
487   /* Set new parent */
488   cat->parent = parent;
489   cat->nextSibling = parent->firstChild;
490   parent->firstChild = cat;
491
492   /* Make sure parent is initialized */
493   if (parent->threshold == xbt_log_priority_uninitialized) {
494     _cat_init(parent);
495   }
496     
497   /* Reset priority */
498   cat->threshold = parent->threshold;
499   cat->isThreshInherited = 1;
500 } /* log_setParent */
501
502 static void _set_inherited_thresholds(xbt_log_category_t cat) {
503   xbt_log_category_t child = cat->firstChild;
504   for( ; child != NULL; child = child->nextSibling) {
505     if (child->isThreshInherited) {
506       if (cat != &_XBT_LOGV(log))
507         VERB3("Set category threshold of %s to %s (=%d)",
508               child->name,xbt_log_priority_names[cat->threshold],cat->threshold);
509       child->threshold = cat->threshold;
510       _set_inherited_thresholds(child);
511     }
512   }
513 }
514
515 void xbt_log_threshold_set(xbt_log_category_t   cat,
516                             e_xbt_log_priority_t threshold) {
517   cat->threshold = threshold;
518   cat->isThreshInherited = 0;
519   _set_inherited_thresholds(cat);
520 }
521
522 static void _xbt_log_parse_setting(const char*        control_string,
523                                     xbt_log_setting_t set) {
524   const char *name, *dot, *eq;
525   
526   set->catname=NULL;
527   if (!*control_string) 
528     return;
529   DEBUG1("Parse log setting '%s'",control_string);
530
531   control_string += strspn(control_string, " ");
532   name = control_string;
533   control_string += strcspn(control_string, ".= ");
534   dot = control_string;
535   control_string += strcspn(control_string, "= ");
536   eq = control_string;
537   control_string += strcspn(control_string, " ");
538
539   xbt_assert1(*dot == '.' && *eq == '=',
540                "Invalid control string '%s'",control_string);
541
542   if (!strncmp(dot + 1, "thresh", min(eq - dot - 1,strlen("thresh")))) {
543     int i;
544     char *neweq=xbt_strdup(eq+1);
545     char *p=neweq-1;
546     
547     while (*(++p) != '\0') {
548       if (*p >= 'a' && *p <= 'z') {
549         *p-='a'-'A';
550       }
551     }
552     
553     DEBUG1("New priority name = %s",neweq);
554     for (i=0; i<xbt_log_priority_infinite-1; i++) {
555       if (!strncmp(xbt_log_priority_names[i],neweq,p-eq)) {
556         DEBUG1("This is priority %d",i);
557         break;
558       }
559     }
560     if (i<xbt_log_priority_infinite-1) {
561       set->thresh=i;
562     } else {
563       xbt_assert1(FALSE,"Unknown priority name: %s",eq+1);
564     }
565     xbt_free(neweq);
566   } else {
567     char buff[512];
568     snprintf(buff,min(512,eq - dot - 1),"%s",dot+1);
569     xbt_assert1(FALSE,"Unknown setting of the log category: %s",buff);
570   }
571   set->catname=(char*)xbt_malloc(dot - name+1);
572     
573   strncpy(set->catname,name,dot-name);
574   set->catname[dot-name]='\0'; /* Just in case */
575   DEBUG1("This is for cat '%s'", set->catname);
576 }
577
578 static xbt_error_t _xbt_log_cat_searchsub(xbt_log_category_t cat,char *name,
579                                             /*OUT*/xbt_log_category_t*whereto) {
580   xbt_error_t errcode;
581   xbt_log_category_t child;
582   
583   if (!strcmp(cat->name,name)) {
584     *whereto=cat;
585     return no_error;
586   }
587   for(child=cat->firstChild ; child != NULL; child = child->nextSibling) {
588     errcode=_xbt_log_cat_searchsub(child,name,whereto);
589     if (errcode==no_error)
590       return no_error;
591   }
592   return mismatch_error;
593 }
594
595 static void _cleanup_double_spaces(char *s) {
596   char *p = s;
597   int   e = 0;
598   
599   while (1) {
600     if (!*p)
601       goto end;
602     
603     if (!isspace(*p))
604       break;
605     
606     p++;
607   }
608   
609   e = 1;
610   
611   do {
612     if (e)
613       *s++ = *p;
614     
615     if (!*++p)
616       goto end;
617     
618     if (e ^ !isspace(*p))
619       if ((e = !e))
620         *s++ = ' ';
621   } while (1);
622
623  end:
624   *s = '\0';
625 }
626
627 /**
628  * \ingroup XBT_log  
629  * \param control_string What to parse
630  *
631  * Typically passed a command-line argument. The string has the syntax:
632  *
633  *      ( [category] "." [keyword] "=" value (" ")... )...
634  *
635  * where [category] is one the category names and keyword is one of the
636  * following:
637  *
638  *      thresh          value is an integer priority level. Sets the category's
639  *                        threshold priority.
640  *
641  * \warning
642  * This routine may only be called once and that must be before any other
643  * logging command! Typically, this is done from main().
644  */
645 void xbt_log_control_set(const char* control_string) {
646   xbt_error_t errcode;
647   xbt_log_setting_t set;
648   char *cs;
649   char *p;
650   int done = 0;
651   
652   DEBUG1("Parse log settings '%s'",control_string);
653   if (control_string == NULL)
654     return;
655   if (xbt_log_settings == NULL)
656     xbt_log_settings = xbt_dynar_new(sizeof(xbt_log_setting_t),
657                                        _free_setting);
658
659   set = xbt_new(s_xbt_log_setting_t,1);
660   cs=xbt_strdup(control_string);
661
662   _cleanup_double_spaces(cs);
663
664   while (!done) {
665     xbt_log_category_t cat;
666     
667     p=strrchr(cs,' ');
668     if (p) {
669       *p='\0';
670       *p++;
671     } else {
672       p=cs;
673       done = 1;
674     }
675     _xbt_log_parse_setting(p,set);
676     
677     errcode = _xbt_log_cat_searchsub(&_XBT_LOGV(root),set->catname,&cat);
678     if (errcode == mismatch_error) {
679       DEBUG0("Store for further application");
680       DEBUG1("push %p to the settings",(void*)set);
681       xbt_dynar_push(xbt_log_settings,&set);
682       /* malloc in advance the next slot */
683       set = xbt_new(s_xbt_log_setting_t,1);
684     } else {
685       DEBUG0("Apply directly");
686       xbt_free(set->catname);
687       xbt_log_threshold_set(cat,set->thresh);
688     }
689   }
690   xbt_free(set);
691   xbt_free(cs);
692
693
694 void xbt_log_appender_set(xbt_log_category_t cat, xbt_log_appender_t app) {
695   cat->appender = app;
696 }
697