Logo AND Algorithmique Numérique Distribuée

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