Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
cosmetics on log categories declaration; declare a MSG category here since MSG fails...
[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 <h3>Overview</h3>
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 <h3>Category hierarchy</h3>
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 <h3>Priority</h3>
75
76 A category may be assigned a threshold priorty. The set of priorites are
77 defined by the \ref e_xbt_log_priority_t enum. All logging request under
78 this priority will be discarded.
79           
80 If a given category is not assigned a threshold priority, then it inherits
81 one from its closest ancestor with an assigned threshold. To ensure that all
82 categories can eventually inherit a threshold, the root category always has
83 an assigned threshold priority.
84
85 Logging requests are made by invoking a logging macro on a category.  All of
86 the macros have a printf-style format string followed by arguments. If you
87 compile with the -Wall option, gcc will warn you for unmatched arguments, ie
88 when you pass a pointer to a string where an integer was specified by the
89 format. This is usualy a good idea.
90
91 Because most C compilers do not support vararg macros, there is a version of
92 the macro for any number of arguments from 0 to 6. The macro name ends with
93 the total number of arguments.
94         
95 Here is an example of the most basic type of macro. This is a logging
96 request with priority <i>warning</i>.
97
98 <code>CLOG5(MyCat, gras_log_priority_warning, "Values are: %d and '%s'", 5,
99 "oops");</code>
100
101 A logging request is said to be enabled if its priority is higher than or
102 equal to the threshold priority of its category. Otherwise, the request is
103 said to be disabled. A category without an assigned priority will inherit
104 one from the hierarchy. 
105       
106 It is possible to use any non-negative integer as a priority. If, as in the
107 example, one of the standard priorites is used, then there is a convenience
108 macro that is typically used instead. For example, the above example is
109 equivalent to the shorter:
110
111 <code>CWARN4(MyCat, "Values are: %d and '%s'", 5, "oops");</code>
112
113 <h3>Default category</h3>
114   
115 If \ref XBT_LOG_NEW_DEFAULT_SUBCATEGORY(MyCat, Parent) or
116 \ref XBT_LOG_NEW_DEFAULT_CATEGORY(MyCat) is used to create the
117 category, then the even shorter form can be used:
118
119 <code>WARN3("Values are: %d and '%s'", 5, "oops");</code>
120
121 Only one default category can be created per file, though multiple
122 non-defaults can be created and used.
123
124 <h3>Example</h3>
125
126 Here is a more complete example:
127
128 \verbatim
129 #include "xbt/log.h"
130
131 / * create a category and a default subcategory * /
132 XBT_LOG_NEW_CATEGORY(VSS);
133 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(SA, VSS);
134
135 int main() {
136        / * Now set the parent's priority.  (the string would typcially be a runtime option) * /
137        xbt_log_control_set("SA.thresh=3");
138
139        / * This request is enabled, because WARNING &gt;= INFO. * /
140        CWARN2(VSS, "Low fuel level.");
141
142        / * This request is disabled, because DEBUG &lt; INFO. * /
143        CDEBUG2(VSS, "Starting search for nearest gas station.");
144
145        / * The default category SA inherits its priority from VSS. Thus,
146           the following request is enabled because INFO &gt;= INFO.  * /
147        INFO1("Located nearest gas station.");
148
149        / * This request is disabled, because DEBUG &lt; INFO. * /
150        DEBUG1("Exiting gas station search"); 
151 }
152 \endverbatim
153
154 <h3>Configuration</h3>
155 Configuration is typically done during program initialization by invoking
156 the xbt_log_control_set() method. The control string passed to it typically
157 comes from the command line. Look at the documentation for that function for
158 the format of the control string.
159
160 Any SimGrid program can furthermore be configured at run time by passing a
161 --xbt-log argument on the command line (--gras-log, --msg-log and
162 --surf-log are synonyms). You can provide several of those arguments to
163 change the setting of several categories.
164
165 <h3>Performance</h3>
166
167 Clever design insures efficiency. Except for the first invocation, a
168 disabled logging request requires an a single comparison of a static
169 variable to a constant.
170
171 There is also compile time constant, \ref XBT_LOG_STATIC_THRESHOLD, which
172 causes all logging requests with a lower priority to be optimized to 0 cost
173 by the compiler. By setting it to gras_log_priority_infinite, all logging
174 requests are statically disabled and cost nothing. Released executables
175 might be compiled with
176 \verbatim-DXBT_LOG_STATIC_THRESHOLD=gras_log_priority_infinite\endverbatim
177
178 Compiling with the \verbatim-DNLOG\endverbatim option disables all logging 
179 requests at compilation time while the \verbatim-DNDEBUG\endverbatim disables 
180 the requests of priority below INFO.
181  
182 <h3>Appenders</h3>
183
184 Each category has an optional appender. An appender is a pointer to a
185 structure which starts with a pointer to a doAppend() function. DoAppend()
186 prints a message to a log.
187
188 When a category is passed a message by one of the logging macros, the
189 category performs the following actions:
190
191   - if the category has an appender, the message is passed to the
192     appender's doAppend() function,
193   - if 'willLogToParent' is true for the category, the message is passed
194     to the category's parent.
195     
196 By default, only the root category have an appender, and 'willLogToParent'
197 is true for any other category. This situation causes all messages to be
198 logged by the root category's appender.
199
200 The default appender function currently prints to stderr, and no other one
201 exist, even if more would be needed, like the one able to send the logs to a
202 remote dedicated server, or other ones offering different output formats.
203 This is on our TODO list for quite a while now, but your help would be
204 welcome here.
205
206 <h3>Misc and Caveats</h3>
207
208   - Do not use any of the macros that start with '_'.
209   - Log4J has a 'rolling file appender' which you can select with a run-time
210     option and specify the max file size. This would be a nice default for
211     non-kernel applications.
212   - Careful, category names are global variables.
213
214 */
215
216 /*
217 FAIRE DES ZOLIS LOGS
218 --------------------
219 Dans gras, tu ne te contente pas d'écrire des choses à l'écran, mais tu
220 écris sur un sujet particulier (notion de canal) des choses d'une gravité
221 particulière. Il y a 7 niveaux de gravité.
222  trace: tracer les entrées dans une fonction, retour de fonction
223         (famille de macros XBT_IN/XBT_OUT)
224  debug: pour t'aider à mettre au point le module, potentiellement tres bavard
225  verbose: quelques infos succintes sur les internals du module
226  info: niveau normal, ton de la conversation
227  warning: problème potentiel, mais auquel on a su faire face
228  error: problème qui t'as empêché de faire ton job
229  critical: juste avant de mourir
230
231 Quand on compile avec -DNDEBUG (par défaut dans le paquet Debian), tout ce
232 qui est '>= verbose' est supprimé au moment de la compilation. Retiré du
233 binaire, killé.
234
235 Ensuite, tu écris dans un canal particulier. Tous les canaux sont rangés en
236 arbre. Il faudrait faire un ptit script qui fouille les sources à la
237 recherche des macros XBT_LOG_NEW_* utilisées pour créer des canaux. Le
238 dernier argument de ces macros est ignoré dans le source. Il est destiné à
239 être la documentation de la chose en une ligne. En gros, ca fait:
240 root
241  +--xbt
242  |   +--config
243  |   +--dict
244  |   |   +--dict_cursor
245  |   |   +--dict_elm
246  |   |   ...
247  |   +--dynar
248  |   +--set
249  |   +--log
250  |   +--module
251  +--gras
252      +--datadesc
253      |   +--ddt_cbps
254      |   +--ddt_convert
255      |   +--ddt_exchange
256      |   +--ddt_parse
257      |       +--lexer
258      +--msg
259      +--transport
260          +--raw_trp (Je devrais tuer ce module, un jour)
261          +--trp_buf
262          +--trp_sg
263          +--trp_file
264          +--trp_tcp
265          
266 Et ensuite les utilisateurs peuvent choisir le niveau de gravité qui les
267 interresse sur tel ou tel sujet.
268
269 Toute la mécanique de logging repose sur des variables statiques dont le nom
270 dépend du nom du canal.
271  => attention aux conflits de nom de canal
272  => il faut une macro XBT_LOG dans chaque fichier où tu fais des logs.
273  
274 XBT_LOG_NEW_CATEGORY: nouveau canal sous "root". Rare, donc.
275 XBT_LOG_NEW_SUBCATEGORY: nouveau canal dont on précise le père.
276 XBT_LOG_DEFAULT_CATEGORY: indique quel est le canal par défaut dans ce fichier
277 XBT_LOG_NEW_DEFAULT_CATEGORY: Crèe un canal et l'utilise par défaut
278 XBT_LOG_NEW_DEFAULT_SUBCATEGORY: devine
279 XBT_LOG_EXTERNAL_CATEGORY: quand tu veux utiliser par défaut un canal créé
280                            dans un autre fichier.
281
282 Une fois que ton canal est créé, tu l'utilise avec les macros LOG, DEBUG,
283 VERB, WARN, ERROR et CRITICAL. Il faut que tu donne le nombre d'arguments
284 après le nom de macro. Exemple: LOG2("My name is %s %s","Martin","Quinson")
285 Si tu veux préciser explicitement le canal où écrire, ajoute un C devant le
286 nom de la macro. Exemple: CCRITICAL0(module, "Cannot initialize GRAS")
287
288 Toutes ces macros (enfin, ce en quoi elles se réécrivent) vérifient leurs
289 arguments comme printf le fait lorsqu'on compile avec gcc. 
290 LOG1("name: %d","toto"); donne un warning, et donc une erreur en mode
291 mainteneur.
292
293 Enfin, tu peux tester si un canal est ouvert à une priorité donnée (pour
294 préparer plus de débug, par exemple. Dans le parseur, je fais du pretty
295 printing sur ce qu'il faut parser dans ce cas).
296 XBT_LOG_ISENABLED(catName, priority) Le second argument doit être une valeur
297 de e_xbt_log_priority_t (log.h). Par exemple: xbt_log_priority_verbose
298
299 Voila sur comment mettre des logs dans ton code. N'hesite pas à faire pleins
300 de canaux différents pour des aspects différents de ton code. En
301 particulier, dans les dict, j'ai un canal pour l'ajout, le retrait, le
302 netoyage du code après suppression et ainsi de suite. De cette façon, je
303 peux choisir qui m'interresse.
304
305
306 Pour utiliser les logs, tu déjà faire, non ? Tu colle sur la ligne de
307 commande un ou plusieurs arguments de la forme
308   --gras-log="<réglage> [<reglage>+]" (ou sans " si t'as pas d'espace)
309 chaque réglage étant de la forme:
310   <canal>.thres=<priorité>
311 Les différents réglages sont lus de gauche à droite.
312 "root.thres=debug root.thres=critical" ferme tout, normalement.
313
314 */
315
316 typedef struct {
317   char *catname;
318   e_xbt_log_priority_t thresh;
319 } s_xbt_log_setting_t,*xbt_log_setting_t;
320
321 static xbt_dynar_t xbt_log_settings=NULL;
322 static void _free_setting(void *s) {
323   xbt_log_setting_t set=(xbt_log_setting_t)s;
324   if (set) {
325     xbt_free(set->catname);
326 /*    xbt_free(set); FIXME: uncommenting this leads to segfault when more than one chunk is passed as gras-log */
327   }
328 }
329
330 const char *xbt_log_priority_names[8] = {
331   "NONE",
332   "TRACE",
333   "DEBUG",
334   "VERBOSE",
335   "INFO",
336   "WARNING",
337   "ERROR",
338   "CRITICAL"
339 };
340
341 s_xbt_log_category_t _XBT_LOGV(XBT_LOG_ROOT_CAT) = {
342   0, 0, 0,
343   "root", xbt_log_priority_uninitialized, 0,
344   NULL, 0
345 };
346
347 XBT_LOG_NEW_CATEGORY(xbt,"All XBT categories (simgrid toolbox)");
348 XBT_LOG_NEW_CATEGORY(surf,"All SURF categories");
349 XBT_LOG_NEW_CATEGORY(msg,"All MSG categories");
350 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(log,xbt,"Loggings from the logging mecanism itself");
351
352 void xbt_log_init(int *argc,char **argv, const char *defaultlog) {
353   int i,j;
354   char *opt;
355   int found=0;
356
357   /* Set logs and init log submodule */
358   for (i=1; i<*argc; i++) {
359     if (!strncmp(argv[i],"--gras-log=",strlen("--gras-log=")) ||
360         !strncmp(argv[i],"--surf-log=",strlen("--surf-log=")) ||
361         !strncmp(argv[i],"--msg-log=",strlen("--msg-log=")) ||
362         !strncmp(argv[i],"--xbt-log=",strlen("--xbt-log="))) {
363       found = 1;
364       opt=strchr(argv[i],'=');
365       opt++;
366       xbt_log_control_set(opt);
367       DEBUG1("Did apply '%s' as log setting",opt);
368       /*remove this from argv*/
369       for (j=i+1; j<*argc; j++) {
370         argv[j-1] = argv[j];
371       } 
372       argv[j-1] = NULL;
373       (*argc)--;
374       i--; /* compensate effect of next loop incrementation */
375     }
376   }
377   if (!found && defaultlog) {
378      xbt_log_control_set(defaultlog);
379   }
380 }
381
382 void xbt_log_exit(void) {
383   VERB0("Exiting log");
384   xbt_dynar_free(&xbt_log_settings);
385   VERB0("Exited log");
386 }
387
388 static void _apply_control(xbt_log_category_t cat) {
389   int cursor;
390   xbt_log_setting_t setting=NULL;
391   int found = 0;
392
393   if (!xbt_log_settings)
394     return;
395
396   xbt_assert0(cat,"NULL category");
397   xbt_assert(cat->name);
398
399   xbt_dynar_foreach(xbt_log_settings,cursor,setting) {
400     xbt_assert0(setting,"Damnit, NULL cat in the list");
401     xbt_assert1(setting->catname,"NULL setting(=%p)->catname",(void*)setting);
402
403     if (!strcmp(setting->catname,cat->name)) {
404       found = 1;
405
406       xbt_log_threshold_set(cat, setting->thresh);
407       xbt_dynar_cursor_rm(xbt_log_settings,&cursor);
408
409       if (cat->threshold <= xbt_log_priority_verbose) {
410         s_xbt_log_event_t _log_ev = 
411           {cat,xbt_log_priority_verbose,__FILE__,_XBT_GNUC_FUNCTION,__LINE__};
412         _xbt_log_event_log(&_log_ev,
413                  "Apply settings for category '%s': set threshold to %s (=%d)",
414                  cat->name, 
415                  xbt_log_priority_names[cat->threshold], cat->threshold);
416       }
417     }
418   }
419   if (!found && cat->threshold <= xbt_log_priority_verbose) {
420     s_xbt_log_event_t _log_ev = 
421       {cat,xbt_log_priority_verbose,__FILE__,_XBT_GNUC_FUNCTION,__LINE__};
422     _xbt_log_event_log(&_log_ev,
423                         "Category '%s': inherited threshold = %s (=%d)",
424                         cat->name,
425                         xbt_log_priority_names[cat->threshold], cat->threshold);
426   }
427
428 }
429
430 void _xbt_log_event_log( xbt_log_event_t ev, const char *fmt, ...) {
431   xbt_log_category_t cat = ev->cat;
432   va_start(ev->ap, fmt);
433   while(1) {
434     xbt_log_appender_t appender = cat->appender;
435     if (appender != NULL) {
436       appender->do_append(appender, ev, fmt);
437     }
438     if (!cat->willLogToParent)
439       break;
440
441     cat = cat->parent;
442   } 
443   va_end(ev->ap);
444 }
445
446 static void _cat_init(xbt_log_category_t category) {
447   if (category == &_XBT_LOGV(XBT_LOG_ROOT_CAT)) {
448     category->threshold = xbt_log_priority_info;
449     category->appender = xbt_log_default_appender;
450   } else {
451     xbt_log_parent_set(category, category->parent);
452   }
453   _apply_control(category);
454 }
455
456 /*
457  * This gets called the first time a category is referenced and performs the
458  * initialization. 
459  * Also resets threshold to inherited!
460  */
461 int _xbt_log_cat_init(e_xbt_log_priority_t priority,
462                        xbt_log_category_t   category) {
463     
464   _cat_init(category);
465         
466   return priority >= category->threshold;
467 }
468
469 void xbt_log_parent_set(xbt_log_category_t cat,
470                          xbt_log_category_t parent) {
471
472   xbt_assert0(cat,"NULL category to be given a parent");
473   xbt_assert1(parent,"The parent category of %s is NULL",cat->name);
474
475   /* unlink from current parent */
476   if (cat->threshold != xbt_log_priority_uninitialized) {
477     xbt_log_category_t* cpp = &parent->firstChild;
478     while(*cpp != cat && *cpp != NULL) {
479       cpp = &(*cpp)->nextSibling;
480     }
481     xbt_assert(*cpp == cat);
482     *cpp = cat->nextSibling;
483   }
484
485   /* Set new parent */
486   cat->parent = parent;
487   cat->nextSibling = parent->firstChild;
488   parent->firstChild = cat;
489
490   /* Make sure parent is initialized */
491   if (parent->threshold == xbt_log_priority_uninitialized) {
492     _cat_init(parent);
493   }
494     
495   /* Reset priority */
496   cat->threshold = parent->threshold;
497   cat->isThreshInherited = 1;
498 } /* log_setParent */
499
500 static void _set_inherited_thresholds(xbt_log_category_t cat) {
501   xbt_log_category_t child = cat->firstChild;
502   for( ; child != NULL; child = child->nextSibling) {
503     if (child->isThreshInherited) {
504       if (cat != &_XBT_LOGV(log))
505         VERB3("Set category threshold of %s to %s (=%d)",
506               child->name,xbt_log_priority_names[cat->threshold],cat->threshold);
507       child->threshold = cat->threshold;
508       _set_inherited_thresholds(child);
509     }
510   }
511 }
512
513 void xbt_log_threshold_set(xbt_log_category_t   cat,
514                             e_xbt_log_priority_t threshold) {
515   cat->threshold = threshold;
516   cat->isThreshInherited = 0;
517   _set_inherited_thresholds(cat);
518 }
519
520 static void _xbt_log_parse_setting(const char*        control_string,
521                                     xbt_log_setting_t set) {
522   const char *name, *dot, *eq;
523   
524   set->catname=NULL;
525   if (!*control_string) 
526     return;
527   DEBUG1("Parse log setting '%s'",control_string);
528
529   control_string += strspn(control_string, " ");
530   name = control_string;
531   control_string += strcspn(control_string, ".= ");
532   dot = control_string;
533   control_string += strcspn(control_string, "= ");
534   eq = control_string;
535   control_string += strcspn(control_string, " ");
536
537   xbt_assert1(*dot == '.' && *eq == '=',
538                "Invalid control string '%s'",control_string);
539
540   if (!strncmp(dot + 1, "thresh", min(eq - dot - 1,strlen("thresh")))) {
541     int i;
542     char *neweq=xbt_strdup(eq+1);
543     char *p=neweq-1;
544     
545     while (*(++p) != '\0') {
546       if (*p >= 'a' && *p <= 'z') {
547         *p-='a'-'A';
548       }
549     }
550     
551     DEBUG1("New priority name = %s",neweq);
552     for (i=0; i<xbt_log_priority_infinite-1; i++) {
553       if (!strncmp(xbt_log_priority_names[i],neweq,p-eq)) {
554         DEBUG1("This is priority %d",i);
555         break;
556       }
557     }
558     if (i<xbt_log_priority_infinite-1) {
559       set->thresh=i;
560     } else {
561       xbt_assert1(FALSE,"Unknown priority name: %s",eq+1);
562     }
563     xbt_free(neweq);
564   } else {
565     char buff[512];
566     snprintf(buff,min(512,eq - dot - 1),"%s",dot+1);
567     xbt_assert1(FALSE,"Unknown setting of the log category: %s",buff);
568   }
569   set->catname=(char*)xbt_malloc(dot - name+1);
570     
571   strncpy(set->catname,name,dot-name);
572   set->catname[dot-name]='\0'; /* Just in case */
573   DEBUG1("This is for cat '%s'", set->catname);
574 }
575
576 static xbt_error_t _xbt_log_cat_searchsub(xbt_log_category_t cat,char *name,
577                                             /*OUT*/xbt_log_category_t*whereto) {
578   xbt_error_t errcode;
579   xbt_log_category_t child;
580   
581   if (!strcmp(cat->name,name)) {
582     *whereto=cat;
583     return no_error;
584   }
585   for(child=cat->firstChild ; child != NULL; child = child->nextSibling) {
586     errcode=_xbt_log_cat_searchsub(child,name,whereto);
587     if (errcode==no_error)
588       return no_error;
589   }
590   return mismatch_error;
591 }
592
593 static void _cleanup_double_spaces(char *s) {
594   char *p = s;
595   int   e = 0;
596   
597   while (1) {
598     if (!*p)
599       goto end;
600     
601     if (!isspace(*p))
602       break;
603     
604     p++;
605   }
606   
607   e = 1;
608   
609   do {
610     if (e)
611       *s++ = *p;
612     
613     if (!*++p)
614       goto end;
615     
616     if (e ^ !isspace(*p))
617       if ((e = !e))
618         *s++ = ' ';
619   } while (1);
620
621  end:
622   *s = '\0';
623 }
624
625 /**
626  * \ingroup XBT_log  
627  * \param control_string What to parse
628  *
629  * Typically passed a command-line argument. The string has the syntax:
630  *
631  *      ( [category] "." [keyword] "=" value (" ")... )...
632  *
633  * where [category] is one the category names and keyword is one of the
634  * following:
635  *
636  *      thresh          value is an integer priority level. Sets the category's
637  *                        threshold priority.
638  *
639  * \warning
640  * This routine may only be called once and that must be before any other
641  * logging command! Typically, this is done from main().
642  */
643 void xbt_log_control_set(const char* control_string) {
644   xbt_error_t errcode;
645   xbt_log_setting_t set;
646   char *cs;
647   char *p;
648   int done = 0;
649   
650   DEBUG1("Parse log settings '%s'",control_string);
651   if (control_string == NULL)
652     return;
653   if (xbt_log_settings == NULL)
654     xbt_log_settings = xbt_dynar_new(sizeof(xbt_log_setting_t),
655                                        _free_setting);
656
657   set = xbt_new(s_xbt_log_setting_t,1);
658   cs=xbt_strdup(control_string);
659
660   _cleanup_double_spaces(cs);
661
662   while (!done) {
663     xbt_log_category_t cat;
664     
665     p=strrchr(cs,' ');
666     if (p) {
667       *p='\0';
668       *p++;
669     } else {
670       p=cs;
671       done = 1;
672     }
673     _xbt_log_parse_setting(p,set);
674     
675     errcode = _xbt_log_cat_searchsub(&_XBT_LOGV(root),set->catname,&cat);
676     if (errcode == mismatch_error) {
677       DEBUG0("Store for further application");
678       DEBUG1("push %p to the settings",(void*)set);
679       xbt_dynar_push(xbt_log_settings,&set);
680       /* malloc in advance the next slot */
681       set = xbt_new(s_xbt_log_setting_t,1);
682     } else {
683       DEBUG0("Apply directly");
684       xbt_free(set->catname);
685       xbt_log_threshold_set(cat,set->thresh);
686     }
687   }
688   xbt_free(set);
689   xbt_free(cs);
690
691
692 void xbt_log_appender_set(xbt_log_category_t cat, xbt_log_appender_t app) {
693   cat->appender = app;
694 }
695