Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Interface revolution: do not try to survive to malloc failure
[simgrid.git] / src / xbt / log.c
1 /* $Id$ */
2
3 /* log - a generic logging facility in the spirit of log4j                  */
4
5 /* Authors: Martin Quinson                                                  */
6 /* Copyright (C) 2003, 2004 Martin Quinson.                                 */
7
8 /* This program is free software; you can redistribute it and/or modify it
9    under the terms of the license (GNU LGPL) which comes with this package. */
10
11
12 #include "xbt_interface.h"
13 #include "gras_private.h"
14 #include <stdarg.h>
15 #include <assert.h>
16 #include <ctype.h>
17
18 typedef struct {
19   char *catname;
20   gras_log_priority_t thresh;
21 } gras_log_setting_t;
22
23 static gras_dynar_t *gras_log_settings=NULL;
24 static void _free_setting(void *s) {
25   gras_log_setting_t *set=(gras_log_setting_t*)s;
26   if (set) {
27     gras_free(set->catname);
28 /*    free(set); FIXME: uncommenting this leads to segfault when more than one chunk is passed as gras-log */
29   }
30 }
31
32 const char *gras_log_priority_names[8] = {
33   "NONE",
34   "TRACE",
35   "DEBUG",
36   "VERBOSE",
37   "INFO",
38   "WARNING",
39   "ERROR",
40   "CRITICAL"
41 };
42
43 gras_log_category_t _GRAS_LOGV(GRAS_LOG_ROOT_CAT) = {
44   0, 0, 0,
45   "root", gras_log_priority_uninitialized, 0,
46   NULL, 0
47 };
48
49 GRAS_LOG_NEW_SUBCATEGORY(gras,GRAS_LOG_ROOT_CAT,"All GRAS categories");
50 GRAS_LOG_NEW_SUBCATEGORY(gros,GRAS_LOG_ROOT_CAT,"All GROS categories (gras toolbox)");
51 GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(log,gros,"Loggings from the logging mecanism itself");
52
53
54 static void _apply_control(gras_log_category_t* cat) {
55   int cursor;
56   gras_log_setting_t *setting=NULL;
57   int found = 0;
58
59   if (!gras_log_settings)
60     return;
61
62   gras_assert0(cat,"NULL category");
63   gras_assert(cat->name);
64
65   gras_dynar_foreach(gras_log_settings,cursor,setting) {
66     gras_assert0(setting,"Damnit, NULL cat in the list");
67     gras_assert1(setting->catname,"NULL setting(=%p)->catname",(void*)setting);
68
69     if (!strcmp(setting->catname,cat->name)) {
70       found = 1;
71
72       gras_log_threshold_set(cat, setting->thresh);
73       gras_dynar_cursor_rm(gras_log_settings,&cursor);
74
75       if (cat->threshold <= gras_log_priority_verbose) {
76         gras_log_event_t _log_ev = 
77           {cat,gras_log_priority_verbose,__FILE__,__FUNCTION__,__LINE__};
78         _gras_log_event_log(&_log_ev,
79                  "Apply settings for category '%s': set threshold to %s (=%d)",
80                  cat->name, 
81                  gras_log_priority_names[cat->threshold], cat->threshold);
82       }
83     }
84   }
85   if (!found && cat->threshold <= gras_log_priority_verbose) {
86     gras_log_event_t _log_ev = 
87       {cat,gras_log_priority_verbose,__FILE__,__FUNCTION__,__LINE__};
88     _gras_log_event_log(&_log_ev,
89                         "Category '%s': inherited threshold = %s (=%d)",
90                         cat->name,
91                         gras_log_priority_names[cat->threshold], cat->threshold);
92   }
93
94 }
95
96 void _gras_log_event_log( gras_log_event_t* ev, const char *fmt, ...) {
97   gras_log_category_t* cat = ev->cat;
98   va_start(ev->ap, fmt);
99   while(1) {
100     gras_log_appender_t* appender = cat->appender;
101     if (appender != NULL) {
102       appender->do_append(appender, ev, fmt);
103     }
104     if (!cat->willLogToParent)
105       break;
106
107     cat = cat->parent;
108   } 
109   va_end(ev->ap);
110 }
111
112 static void _cat_init(gras_log_category_t* category) {
113   if (category == &_GRAS_LOGV(GRAS_LOG_ROOT_CAT)) {
114     category->threshold = gras_log_priority_info;
115     category->appender = gras_log_default_appender;
116   } else {
117     gras_log_parent_set(category, category->parent);
118   }
119   _apply_control(category);
120 }
121
122 /*
123  * This gets called the first time a category is referenced and performs the
124  * initialization. 
125  * Also resets threshold to inherited!
126  */
127 int _gras_log_cat_init(gras_log_priority_t priority,
128                        gras_log_category_t* category) {
129     
130   _cat_init(category);
131         
132   return priority >= category->threshold;
133 }
134
135 void gras_log_parent_set(gras_log_category_t* cat,
136                          gras_log_category_t* parent) {
137
138   gras_assert0(cat,"NULL category to be given a parent");
139   gras_assert1(parent,"The parent category of %s is NULL",cat->name);
140
141   /* unlink from current parent */
142   if (cat->threshold != gras_log_priority_uninitialized) {
143     gras_log_category_t** cpp = &parent->firstChild;
144     while(*cpp != cat && *cpp != NULL) {
145       cpp = &(*cpp)->nextSibling;
146     }
147     assert(*cpp == cat);
148     *cpp = cat->nextSibling;
149   }
150
151   /* Set new parent */
152   cat->parent = parent;
153   cat->nextSibling = parent->firstChild;
154   parent->firstChild = cat;
155
156   /* Make sure parent is initialized */
157   if (parent->threshold == gras_log_priority_uninitialized) {
158     _cat_init(parent);
159   }
160     
161   /* Reset priority */
162   cat->threshold = parent->threshold;
163   cat->isThreshInherited = 1;
164 } /* log_setParent */
165
166 static void _set_inherited_thresholds(gras_log_category_t* cat) {
167   gras_log_category_t* child = cat->firstChild;
168   for( ; child != NULL; child = child->nextSibling) {
169     if (child->isThreshInherited) {
170       if (cat != &_GRAS_LOGV(log))
171         VERB3("Set category threshold of %s to %s (=%d)",
172               child->name,gras_log_priority_names[cat->threshold],cat->threshold);
173       child->threshold = cat->threshold;
174       _set_inherited_thresholds(child);
175     }
176   }
177 }
178
179 void gras_log_threshold_set(gras_log_category_t* cat, 
180                             gras_log_priority_t threshold) {
181   cat->threshold = threshold;
182   cat->isThreshInherited = 0;
183   _set_inherited_thresholds(cat);
184 }
185
186 static void _gras_log_parse_setting(const char* control_string,
187                                     gras_log_setting_t *set) {
188   const char *name, *dot, *eq;
189   
190   set->catname=NULL;
191   if (!*control_string) 
192     return;
193   DEBUG1("Parse log setting '%s'",control_string);
194
195   control_string += strspn(control_string, " ");
196   name = control_string;
197   control_string += strcspn(control_string, ".= ");
198   dot = control_string;
199   control_string += strcspn(control_string, "= ");
200   eq = control_string;
201   control_string += strcspn(control_string, " ");
202
203   gras_assert1(*dot == '.' && *eq == '=',
204                "Invalid control string '%s'",control_string);
205
206   if (!strncmp(dot + 1, "thresh", min(eq - dot - 1,strlen("thresh")))) {
207     int i;
208     char *neweq=gras_strdup(eq+1);
209     char *p=neweq-1;
210     
211     while (*(++p) != '\0') {
212       if (*p >= 'a' && *p <= 'z') {
213         *p-='a'-'A';
214       }
215     }
216     
217     DEBUG1("New priority name = %s",neweq);
218     for (i=0; i<gras_log_priority_infinite-1; i++) {
219       if (!strncmp(gras_log_priority_names[i],neweq,p-eq)) {
220         DEBUG1("This is priority %d",i);
221         break;
222       }
223     }
224     if (i<gras_log_priority_infinite-1) {
225       set->thresh=i;
226     } else {
227       gras_assert1(FALSE,"Unknown priority name: %s",eq+1);
228     }
229     gras_free(neweq);
230   } else {
231     char buff[512];
232     snprintf(buff,min(512,eq - dot - 1),"%s",dot+1);
233     gras_assert1(FALSE,"Unknown setting of the log category: %s",buff);
234   }
235   set->catname=(char*)gras_malloc(dot - name+1);
236     
237   strncpy(set->catname,name,dot-name);
238   set->catname[dot-name]='\0'; /* Just in case */
239   DEBUG1("This is for cat '%s'", set->catname);
240 }
241
242 static gras_error_t _gras_log_cat_searchsub(gras_log_category_t *cat,char *name,gras_log_category_t**whereto) {
243   gras_error_t errcode;
244   gras_log_category_t *child;
245   
246   if (!strcmp(cat->name,name)) {
247     *whereto=cat;
248     return no_error;
249   }
250   for(child=cat->firstChild ; child != NULL; child = child->nextSibling) {
251     errcode=_gras_log_cat_searchsub(child,name,whereto);
252     if (errcode==no_error)
253       return no_error;
254   }
255   return mismatch_error;
256 }
257
258 static void _cleanup_double_spaces(char *s) {
259   char *p = s;
260   int   e = 0;
261   
262   while (1) {
263     if (!*p)
264       goto end;
265     
266     if (!isspace(*p))
267       break;
268     
269     p++;
270   }
271   
272   e = 1;
273   
274   do {
275     if (e)
276       *s++ = *p;
277     
278     if (!*++p)
279       goto end;
280     
281     if (e ^ !isspace(*p))
282       if ((e = !e))
283         *s++ = ' ';
284   } while (1);
285
286  end:
287   *s = '\0';
288 }
289
290 /**
291  * gras_log_control_set:
292  * @cs: What to parse
293  *
294  * Typically passed a command-line argument. The string has the syntax:
295  *
296  *      ( [category] "." [keyword] "=" value (" ")... )...
297  *
298  * where [category] is one the category names and keyword is one of the
299  * following:
300  *
301  *      thresh          value is an integer priority level. Sets the category's
302  *                        threshold priority.
303  *
304  * @warning
305  * This routine may only be called once and that must be before any other
306  * logging command! Typically, this is done from main().
307  */
308 void gras_log_control_set(const char* control_string) {
309   gras_error_t errcode;
310   gras_log_setting_t *set;
311   char *cs;
312   char *p;
313   int done = 0;
314   
315   DEBUG1("Parse log settings '%s'",control_string);
316   if (control_string == NULL)
317     return;
318   if (gras_log_settings == NULL)
319     gras_dynar_new(&gras_log_settings,sizeof(gras_log_setting_t*),
320                    _free_setting);
321
322   set = gras_new(gras_log_setting_t,1);
323   cs=gras_strdup(control_string);
324
325   _cleanup_double_spaces(cs);
326
327   while (!done) {
328     gras_log_category_t *cat;
329     
330     p=strrchr(cs,' ');
331     if (p) {
332       *p='\0';
333       *p++;
334     } else {
335       p=cs;
336       done = 1;
337     }
338     _gras_log_parse_setting(p,set);
339     
340     errcode = _gras_log_cat_searchsub(&_GRAS_LOGV(root),set->catname,&cat);
341     if (errcode == mismatch_error) {
342       DEBUG0("Store for further application");
343       DEBUG1("push %p to the settings",(void*)set);
344       gras_dynar_push(gras_log_settings,&set);
345       /* malloc in advance the next slot */
346       set = gras_new(gras_log_setting_t,1);
347     } else {
348       DEBUG0("Apply directly");
349       gras_free(set->catname);
350       gras_log_threshold_set(cat,set->thresh);
351     }
352   }
353   gras_free(set);
354   gras_free(cs);
355
356
357 void gras_log_appender_set(gras_log_category_t* cat, gras_log_appender_t* app) {
358   cat->appender = app;
359 }
360
361 void gras_log_exit(void) {
362   VERB0("Exiting log");
363   gras_dynar_free(gras_log_settings);
364   VERB0("Exited log");
365 }