Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
6bcd8187edf46d45c46b13b3c4b9465182f2ad03
[simgrid.git] / src / xbt / log.cpp
1 /* log - a generic logging facility in the spirit of log4j                  */
2
3 /* Copyright (c) 2004-2019. The SimGrid Team. All rights reserved.          */
4
5 /* This program is free software; you can redistribute it and/or modify it
6  * under the terms of the license (GNU LGPL) which comes with this package. */
7
8 #include "src/xbt_modinter.h"
9 #include "src/xbt/log_private.hpp"
10 #include "xbt/asserts.h"
11 #include "xbt/dynar.h"
12 #include "xbt/str.h"
13
14 #include <mutex>
15
16 int xbt_log_no_loc = 0; /* if set to true (with --log=no_loc), file localization will be omitted (for tesh tests) */
17 static std::recursive_mutex* log_cat_init_mutex = nullptr;
18
19 /** @addtogroup XBT_log
20  *
21  *  For more information, please refer to @ref outcomes_logs Section.
22  */
23
24 xbt_log_appender_t xbt_log_default_appender = nullptr; /* set in log_init */
25 xbt_log_layout_t xbt_log_default_layout     = nullptr; /* set in log_init */
26
27 typedef struct {
28   char *catname;
29   char *fmt;
30   e_xbt_log_priority_t thresh;
31   int additivity;
32   xbt_log_appender_t appender;
33 } s_xbt_log_setting_t;
34
35 typedef s_xbt_log_setting_t* xbt_log_setting_t;
36
37 static xbt_dynar_t xbt_log_settings = nullptr;
38
39 static void _free_setting(void *s)
40 {
41   xbt_log_setting_t set = *(xbt_log_setting_t *) s;
42   if (set) {
43     xbt_free(set->catname);
44     xbt_free(set->fmt);
45     xbt_free(set);
46   }
47 }
48
49 static void _xbt_log_cat_apply_set(xbt_log_category_t category, xbt_log_setting_t setting);
50
51 const char *xbt_log_priority_names[8] = {
52   "NONE",
53   "TRACE",
54   "DEBUG",
55   "VERBOSE",
56   "INFO",
57   "WARNING",
58   "ERROR",
59   "CRITICAL"
60 };
61
62 s_xbt_log_category_t _XBT_LOGV(XBT_LOG_ROOT_CAT) = {
63     nullptr /*parent */,
64     nullptr /* firstChild */,
65     nullptr /* nextSibling */,
66     "root",
67     "The common ancestor for all categories",
68     0 /*initialized */,
69     xbt_log_priority_uninitialized /* threshold */,
70     0 /* isThreshInherited */,
71     nullptr /* appender */,
72     nullptr /* layout */,
73     0 /* additivity */
74 };
75
76 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(log, xbt, "Loggings from the logging mechanism itself");
77
78 /* create the default appender and install it in the root category,
79    which were already created (damnit. Too slow little beetle) */
80 void xbt_log_preinit(void)
81 {
82   xbt_log_default_appender             = xbt_log_appender_file_new(nullptr);
83   xbt_log_default_layout               = xbt_log_layout_simple_new(nullptr);
84   _XBT_LOGV(XBT_LOG_ROOT_CAT).appender = xbt_log_default_appender;
85   _XBT_LOGV(XBT_LOG_ROOT_CAT).layout = xbt_log_default_layout;
86   log_cat_init_mutex                   = new std::recursive_mutex();
87 }
88
89 static void xbt_log_help(void);
90 static void xbt_log_help_categories(void);
91
92 /** @brief Get all logging settings from the command line
93  *
94  * xbt_log_control_set() is called on each string we got from cmd line
95  */
96 void xbt_log_init(int *argc, char **argv)
97 {
98   unsigned help_requested = 0;  /* 1: logs; 2: categories */
99   int j                   = 1;
100   int parse_args          = 1; // Stop parsing the parameters once we found '--'
101
102   /* Set logs and init log submodule */
103   for (int i = 1; i < *argc; i++) {
104     if (!strcmp("--", argv[i])) {
105       parse_args = 0;
106       argv[j++]  = argv[i]; // Keep the '--' for sg_config
107     } else if (parse_args && !strncmp(argv[i], "--log=", strlen("--log="))) {
108       char* opt = strchr(argv[i], '=');
109       opt++;
110       xbt_log_control_set(opt);
111       XBT_DEBUG("Did apply '%s' as log setting", opt);
112     } else if (parse_args && !strcmp(argv[i], "--help-logs")) {
113       help_requested |= 1U;
114     } else if (parse_args && !strcmp(argv[i], "--help-log-categories")) {
115       help_requested |= 2U;
116     } else {
117       argv[j++] = argv[i];
118     }
119   }
120   if (j < *argc) {
121     argv[j] = nullptr;
122     *argc = j;
123   }
124
125   if (help_requested) {
126     if (help_requested & 1)
127       xbt_log_help();
128     if (help_requested & 2)
129       xbt_log_help_categories();
130     exit(0);
131   }
132 }
133
134 static void log_cat_exit(xbt_log_category_t cat)
135 {
136   xbt_log_category_t child;
137
138   if (cat->appender) {
139     if (cat->appender->free_)
140       cat->appender->free_(cat->appender);
141     xbt_free(cat->appender);
142   }
143   if (cat->layout) {
144     if (cat->layout->free_)
145       cat->layout->free_(cat->layout);
146     xbt_free(cat->layout);
147   }
148
149   for (child = cat->firstChild; child != nullptr; child = child->nextSibling)
150     log_cat_exit(child);
151 }
152
153 void xbt_log_postexit(void)
154 {
155   XBT_VERB("Exiting log");
156   delete log_cat_init_mutex;
157   xbt_dynar_free(&xbt_log_settings);
158   log_cat_exit(&_XBT_LOGV(XBT_LOG_ROOT_CAT));
159 }
160
161 /* Size of the static string in which we build the log string */
162 static constexpr size_t XBT_LOG_STATIC_BUFFER_SIZE = 2048;
163 /* Minimum size of the dynamic string in which we build the log string
164    (should be greater than XBT_LOG_STATIC_BUFFER_SIZE) */
165 static constexpr size_t XBT_LOG_DYNAMIC_BUFFER_SIZE = 4096;
166
167 void _xbt_log_event_log(xbt_log_event_t ev, const char *fmt, ...)
168 {
169   xbt_log_category_t cat = ev->cat;
170
171   xbt_assert(ev->priority >= 0, "Negative logging priority naturally forbidden");
172   xbt_assert(static_cast<size_t>(ev->priority) < sizeof(xbt_log_priority_names)/sizeof(xbt_log_priority_names[0]),
173              "Priority %d is greater than the biggest allowed value", ev->priority);
174
175   while (1) {
176     xbt_log_appender_t appender = cat->appender;
177
178     if (appender != nullptr) {
179       xbt_assert(cat->layout, "No valid layout for the appender of category %s", cat->name);
180
181       /* First, try with a static buffer */
182       int done = 0;
183       char buff[XBT_LOG_STATIC_BUFFER_SIZE];
184       ev->buffer      = buff;
185       ev->buffer_size = sizeof buff;
186       va_start(ev->ap, fmt);
187       done = cat->layout->do_layout(cat->layout, ev, fmt);
188       va_end(ev->ap);
189       if (done) {
190         appender->do_append(appender, buff);
191       } else {
192
193         /* The static buffer was too small, use a dynamically expanded one */
194         ev->buffer_size = XBT_LOG_DYNAMIC_BUFFER_SIZE;
195         ev->buffer      = static_cast<char*>(xbt_malloc(ev->buffer_size));
196         while (1) {
197           va_start(ev->ap, fmt);
198           done = cat->layout->do_layout(cat->layout, ev, fmt);
199           va_end(ev->ap);
200           if (done)
201             break; /* Got it */
202           ev->buffer_size *= 2;
203           ev->buffer = static_cast<char*>(xbt_realloc(ev->buffer, ev->buffer_size));
204         }
205         appender->do_append(appender, ev->buffer);
206         xbt_free(ev->buffer);
207       }
208     }
209
210     if (!cat->additivity)
211       break;
212     cat = cat->parent;
213   }
214 }
215
216 /* NOTE:
217  *
218  * The standard logging macros use _XBT_LOG_ISENABLED, which calls _xbt_log_cat_init().  Thus, if we want to avoid an
219  * infinite recursion, we can not use the standard logging macros in _xbt_log_cat_init(), and in all functions called
220  * from it.
221  *
222  * To circumvent the problem, we define the macro DISABLE_XBT_LOG_CAT_INIT() to hide the real _xbt_log_cat_init(). The
223  * macro has to be called at the beginning of the affected functions.
224  */
225 static int fake_xbt_log_cat_init(xbt_log_category_t, e_xbt_log_priority_t)
226 {
227   return 0;
228 }
229 #define DISABLE_XBT_LOG_CAT_INIT()                                                                                     \
230   int (*_xbt_log_cat_init)(xbt_log_category_t, e_xbt_log_priority_t) XBT_ATTRIB_UNUSED = fake_xbt_log_cat_init;
231
232 static void _xbt_log_cat_apply_set(xbt_log_category_t category, xbt_log_setting_t setting)
233 {
234   DISABLE_XBT_LOG_CAT_INIT();
235   if (setting->thresh != xbt_log_priority_uninitialized) {
236     xbt_log_threshold_set(category, setting->thresh);
237
238     XBT_DEBUG("Apply settings for category '%s': set threshold to %s (=%d)",
239            category->name, xbt_log_priority_names[category->threshold], category->threshold);
240   }
241
242   if (setting->fmt) {
243     xbt_log_layout_set(category, xbt_log_layout_format_new(setting->fmt));
244
245     XBT_DEBUG("Apply settings for category '%s': set format to %s", category->name, setting->fmt);
246   }
247
248   if (setting->additivity != -1) {
249     xbt_log_additivity_set(category, setting->additivity);
250
251     XBT_DEBUG("Apply settings for category '%s': set additivity to %s",
252            category->name, (setting->additivity ? "on" : "off"));
253   }
254   if (setting->appender) {
255     xbt_log_appender_set(category, setting->appender);
256     if (!category->layout)
257       xbt_log_layout_set(category, xbt_log_layout_simple_new(nullptr));
258     category->additivity = 0;
259     XBT_DEBUG("Set %p as appender of category '%s'", setting->appender, category->name);
260   }
261 }
262
263 /*
264  * This gets called the first time a category is referenced and performs the initialization.
265  * Also resets threshold to inherited!
266  */
267 int _xbt_log_cat_init(xbt_log_category_t category, e_xbt_log_priority_t priority)
268 {
269   DISABLE_XBT_LOG_CAT_INIT();
270   if (category->initialized)
271     return priority >= category->threshold;
272
273   if (log_cat_init_mutex != nullptr)
274     log_cat_init_mutex->lock();
275
276   unsigned int cursor;
277   xbt_log_setting_t setting = nullptr;
278
279   XBT_DEBUG("Initializing category '%s' (firstChild=%s, nextSibling=%s)", category->name,
280          (category->firstChild ? category->firstChild->name : "none"),
281          (category->nextSibling ? category->nextSibling->name : "none"));
282
283   if (category == &_XBT_LOGV(XBT_LOG_ROOT_CAT)) {
284     category->threshold = xbt_log_priority_info;
285     category->appender = xbt_log_default_appender;
286     category->layout = xbt_log_default_layout;
287   } else {
288     if (!category->parent)
289       category->parent = &_XBT_LOGV(XBT_LOG_ROOT_CAT);
290
291     XBT_DEBUG("Set %s (%s) as father of %s ", category->parent->name,
292            (category->parent->initialized ? xbt_log_priority_names[category->parent->threshold] : "uninited"),
293            category->name);
294     xbt_log_parent_set(category, category->parent);
295
296     if (XBT_LOG_ISENABLED(log, xbt_log_priority_debug)) {
297       std::string res;
298       xbt_log_category_t cpp = category->parent->firstChild;
299       while (cpp) {
300         res += std::string(" ") + cpp->name;
301         cpp = cpp->nextSibling;
302       }
303
304       XBT_DEBUG("Children of %s:%s; nextSibling: %s", category->parent->name, res.c_str(),
305                 (category->parent->nextSibling ? category->parent->nextSibling->name : "none"));
306     }
307   }
308
309   /* Apply the control */
310   if (xbt_log_settings) {
311     xbt_assert(category, "NULL category");
312     xbt_assert(category->name);
313     int found = 0;
314
315     xbt_dynar_foreach(xbt_log_settings, cursor, setting) {
316       xbt_assert(setting, "Damnit, NULL cat in the list");
317       xbt_assert(setting->catname, "NULL setting(=%p)->catname", (void *) setting);
318
319       if (!strcmp(setting->catname, category->name)) {
320         found = 1;
321         _xbt_log_cat_apply_set(category, setting);
322         xbt_dynar_cursor_rm(xbt_log_settings, &cursor);
323       }
324     }
325
326     if (!found)
327       XBT_DEBUG("Category '%s': inherited threshold = %s (=%d)",
328                 category->name, xbt_log_priority_names[category->threshold], category->threshold);
329   }
330
331   category->initialized = 1;
332   if (log_cat_init_mutex != nullptr)
333     log_cat_init_mutex->unlock();
334   return priority >= category->threshold;
335 }
336
337 void xbt_log_parent_set(xbt_log_category_t cat, xbt_log_category_t parent)
338 {
339   xbt_assert(cat, "NULL category to be given a parent");
340   xbt_assert(parent, "The parent category of %s is NULL", cat->name);
341
342   /* if the category is initialized, unlink from current parent */
343   if (cat->initialized) {
344     xbt_log_category_t *cpp = &cat->parent->firstChild;
345
346     while (*cpp != cat && *cpp != nullptr) {
347       cpp = &(*cpp)->nextSibling;
348     }
349
350     xbt_assert(*cpp == cat);
351     *cpp = cat->nextSibling;
352   }
353
354   cat->parent = parent;
355   cat->nextSibling = parent->firstChild;
356
357   parent->firstChild = cat;
358
359   if (!parent->initialized)
360     _xbt_log_cat_init(parent, xbt_log_priority_uninitialized /* ignored */ );
361
362   cat->threshold = parent->threshold;
363
364   cat->isThreshInherited = 1;
365 }
366
367 static void _set_inherited_thresholds(xbt_log_category_t cat)
368 {
369   xbt_log_category_t child = cat->firstChild;
370
371   for (; child != nullptr; child = child->nextSibling) {
372     if (child->isThreshInherited) {
373       if (cat != &_XBT_LOGV(log))
374         XBT_VERB("Set category threshold of %s to %s (=%d)",
375               child->name, xbt_log_priority_names[cat->threshold], cat->threshold);
376       child->threshold = cat->threshold;
377       _set_inherited_thresholds(child);
378     }
379   }
380 }
381
382 void xbt_log_threshold_set(xbt_log_category_t cat, e_xbt_log_priority_t threshold)
383 {
384   cat->threshold = threshold;
385   cat->isThreshInherited = 0;
386
387   _set_inherited_thresholds(cat);
388 }
389
390 static xbt_log_setting_t _xbt_log_parse_setting(const char *control_string)
391 {
392   const char *orig_control_string = control_string;
393   xbt_log_setting_t set = xbt_new(s_xbt_log_setting_t, 1);
394
395   set->catname    = nullptr;
396   set->thresh = xbt_log_priority_uninitialized;
397   set->fmt        = nullptr;
398   set->additivity = -1;
399   set->appender   = nullptr;
400
401   if (!*control_string)
402     return set;
403   XBT_DEBUG("Parse log setting '%s'", control_string);
404
405   control_string += strspn(control_string, " ");
406   const char *name = control_string;
407   control_string += strcspn(control_string, ".:= ");
408   const char *dot = control_string;
409   control_string += strcspn(control_string, ":= ");
410   const char *eq = control_string;
411
412   xbt_assert(*dot == '.' || (*eq != '=' && *eq != ':'), "Invalid control string '%s'", orig_control_string);
413
414   if (!strncmp(dot + 1, "threshold", (size_t) (eq - dot - 1))) {
415     int i;
416     char *neweq = xbt_strdup(eq + 1);
417     char *p = neweq - 1;
418
419     while (*(++p) != '\0') {
420       if (*p >= 'a' && *p <= 'z') {
421         *p -= 'a' - 'A';
422       }
423     }
424
425     XBT_DEBUG("New priority name = %s", neweq);
426     for (i = 0; i < xbt_log_priority_infinite; i++) {
427       if (!strncmp(xbt_log_priority_names[i], neweq, p - eq)) {
428         XBT_DEBUG("This is priority %d", i);
429         break;
430       }
431     }
432
433     if(i<XBT_LOG_STATIC_THRESHOLD){
434      fprintf(stderr,
435          "Priority '%s' (in setting '%s') is above allowed priority '%s'.\n\n"
436          "Compiling SimGrid with -DNDEBUG forbids the levels 'trace' and 'debug'\n"
437          "while -DNLOG forbids any logging, at any level.",
438              eq + 1, name, xbt_log_priority_names[XBT_LOG_STATIC_THRESHOLD]);
439      exit(1);
440     }else if (i < xbt_log_priority_infinite) {
441       set->thresh = (e_xbt_log_priority_t) i;
442     } else {
443       THROWF(arg_error, 0,
444              "Unknown priority name: %s (must be one of: trace,debug,verbose,info,warning,error,critical)", eq + 1);
445     }
446     xbt_free(neweq);
447   } else if (!strncmp(dot + 1, "add", (size_t) (eq - dot - 1)) ||
448              !strncmp(dot + 1, "additivity", (size_t) (eq - dot - 1))) {
449     char *neweq = xbt_strdup(eq + 1);
450     char *p = neweq - 1;
451
452     while (*(++p) != '\0') {
453       if (*p >= 'a' && *p <= 'z') {
454         *p -= 'a' - 'A';
455       }
456     }
457     if (!strcmp(neweq, "ON") || !strcmp(neweq, "YES") || !strcmp(neweq, "1")) {
458       set->additivity = 1;
459     } else {
460       set->additivity = 0;
461     }
462     xbt_free(neweq);
463   } else if (!strncmp(dot + 1, "app", (size_t) (eq - dot - 1)) ||
464              !strncmp(dot + 1, "appender", (size_t) (eq - dot - 1))) {
465     char *neweq = xbt_strdup(eq + 1);
466
467     if (!strncmp(neweq, "file:", 5)) {
468       set->appender = xbt_log_appender_file_new(neweq + 5);
469     }else if (!strncmp(neweq, "rollfile:", 9)) {
470       set->appender = xbt_log_appender2_file_new(neweq + 9,1);
471     }else if (!strncmp(neweq, "splitfile:", 10)) {
472       set->appender = xbt_log_appender2_file_new(neweq + 10,0);
473     } else {
474       THROWF(arg_error, 0, "Unknown appender log type: '%s'", neweq);
475     }
476     xbt_free(neweq);
477   } else if (!strncmp(dot + 1, "fmt", (size_t) (eq - dot - 1))) {
478     set->fmt = xbt_strdup(eq + 1);
479   } else {
480     char buff[512];
481     snprintf(buff, std::min<int>(512, eq - dot), "%s", dot + 1);
482     xbt_die("Unknown setting of the log category: '%s'", buff);
483   }
484   set->catname = (char *) xbt_malloc(dot - name + 1);
485
486   memcpy(set->catname, name, dot - name);
487   set->catname[dot - name] = '\0';      /* Just in case */
488   XBT_DEBUG("This is for cat '%s'", set->catname);
489
490   return set;
491 }
492
493 static xbt_log_category_t _xbt_log_cat_searchsub(xbt_log_category_t cat, char *name)
494 {
495   xbt_log_category_t child;
496   xbt_log_category_t res;
497
498   XBT_DEBUG("Search '%s' into '%s' (firstChild='%s'; nextSibling='%s')", name,
499          cat->name, (cat->firstChild ? cat->firstChild->name : "none"),
500          (cat->nextSibling ? cat->nextSibling->name : "none"));
501   if (!strcmp(cat->name, name))
502     return cat;
503
504   for (child = cat->firstChild; child != nullptr; child = child->nextSibling) {
505     XBT_DEBUG("Dig into %s", child->name);
506     res = _xbt_log_cat_searchsub(child, name);
507     if (res)
508       return res;
509   }
510
511   return nullptr;
512 }
513
514 /**
515  * @ingroup XBT_log
516  * @param control_string What to parse
517  *
518  * Typically passed a command-line argument. The string has the syntax:
519  *
520  *      ( [category] "." [keyword] ":" value (" ")... )...
521  *
522  * where [category] is one the category names (see @ref XBT_log_cats for a complete list of the ones defined in the
523  * SimGrid library) and keyword is one of the following:
524  *
525  *    - thres: category's threshold priority. Possible values:
526  *             TRACE,DEBUG,VERBOSE,INFO,WARNING,ERROR,CRITICAL
527  *    - add or additivity: whether the logging actions must be passed to the parent category.
528  *      Possible values: 0, 1, no, yes, on, off.
529  *      Default value: yes.
530  *    - fmt: the format to use. See @ref log_use_conf_fmt for more information.
531  *    - app or appender: the appender to use. See @ref log_use_conf_app for more information.
532  */
533 void xbt_log_control_set(const char *control_string)
534 {
535   xbt_log_setting_t set;
536
537   /* To split the string in commands, and the cursors */
538   xbt_dynar_t set_strings;
539   char *str;
540   unsigned int cpt;
541
542   if (!control_string)
543     return;
544   XBT_DEBUG("Parse log settings '%s'", control_string);
545
546   /* Special handling of no_loc request, which asks for any file localization to be omitted (for tesh runs) */
547   if (!strcmp(control_string, "no_loc")) {
548     xbt_log_no_loc = 1;
549     return;
550   }
551   /* some initialization if this is the first time that this get called */
552   if (xbt_log_settings == nullptr)
553     xbt_log_settings = xbt_dynar_new(sizeof(xbt_log_setting_t), _free_setting);
554
555   /* split the string, and remove empty entries */
556   set_strings = xbt_str_split_quoted(control_string);
557
558   if (xbt_dynar_is_empty(set_strings)) {     /* vicious user! */
559     xbt_dynar_free(&set_strings);
560     return;
561   }
562
563   /* Parse each entry and either use it right now (if the category was already created), or store it for further use */
564   xbt_dynar_foreach(set_strings, cpt, str) {
565     set = _xbt_log_parse_setting(str);
566     xbt_log_category_t cat = _xbt_log_cat_searchsub(&_XBT_LOGV(XBT_LOG_ROOT_CAT), set->catname);
567
568     if (cat) {
569       XBT_DEBUG("Apply directly");
570       _xbt_log_cat_apply_set(cat, set);
571       _free_setting((void *) &set);
572     } else {
573       XBT_DEBUG("Store for further application");
574       XBT_DEBUG("push %p to the settings", (void *) set);
575       xbt_dynar_push(xbt_log_settings, &set);
576     }
577   }
578   xbt_dynar_free(&set_strings);
579 }
580
581 void xbt_log_appender_set(xbt_log_category_t cat, xbt_log_appender_t app)
582 {
583   if (cat->appender) {
584     if (cat->appender->free_)
585       cat->appender->free_(cat->appender);
586     xbt_free(cat->appender);
587   }
588   cat->appender = app;
589 }
590
591 void xbt_log_layout_set(xbt_log_category_t cat, xbt_log_layout_t lay)
592 {
593   DISABLE_XBT_LOG_CAT_INIT();
594   if (!cat->appender) {
595     XBT_VERB ("No appender to category %s. Setting the file appender as default", cat->name);
596     xbt_log_appender_set(cat, xbt_log_appender_file_new(nullptr));
597   }
598   if (cat->layout) {
599     if (cat->layout->free_) {
600       cat->layout->free_(cat->layout);
601     }
602     xbt_free(cat->layout);
603   }
604   cat->layout = lay;
605   xbt_log_additivity_set(cat, 0);
606 }
607
608 void xbt_log_additivity_set(xbt_log_category_t cat, int additivity)
609 {
610   cat->additivity = additivity;
611 }
612
613 static void xbt_log_help(void)
614 {
615   printf("Description of the logging output:\n"
616          "\n"
617          "   Threshold configuration: --log=CATEGORY_NAME.thres:PRIORITY_LEVEL\n"
618          "      CATEGORY_NAME: defined in code with function 'XBT_LOG_NEW_CATEGORY'\n"
619          "      PRIORITY_LEVEL: the level to print (trace,debug,verbose,info,warning,error,critical)\n"
620          "         -> trace: enter and return of some functions\n"
621          "         -> debug: crufty output\n"
622          "         -> verbose: verbose output for the user wanting more\n"
623          "         -> info: output about the regular functioning\n"
624          "         -> warning: minor issue encountered\n"
625          "         -> error: issue encountered\n"
626          "         -> critical: major issue encountered\n"
627          "      The default priority level is 'info'.\n"
628          "\n"
629          "   Format configuration: --log=CATEGORY_NAME.fmt:FORMAT\n"
630          "      FORMAT string may contain:\n"
631          "         -> %%%%: the %% char\n"
632          "         -> %%n: platform-dependent line separator (LOG4J compatible)\n"
633          "         -> %%e: plain old space (SimGrid extension)\n"
634          "\n"
635          "         -> %%m: user-provided message\n"
636          "\n"
637          "         -> %%c: Category name (LOG4J compatible)\n"
638          "         -> %%p: Priority name (LOG4J compatible)\n"
639          "\n"
640          "         -> %%h: Hostname (SimGrid extension)\n"
641          "         -> %%P: Process name (SimGrid extension)\n"
642          "         -> %%t: Thread \"name\" (LOG4J compatible -- actually the address of the thread in memory)\n"
643          "         -> %%i: Process PID (SimGrid extension -- this is a 'i' as in 'i'dea)\n"
644          "\n"
645          "         -> %%F: file name where the log event was raised (LOG4J compatible)\n"
646          "         -> %%l: location where the log event was raised (LOG4J compatible, like '%%F:%%L' -- this is a l as "
647          "in 'l'etter)\n"
648          "         -> %%L: line number where the log event was raised (LOG4J compatible)\n"
649          "         -> %%M: function name (LOG4J compatible -- called method name here of course).\n"
650          "                 Defined only when using gcc because there is no __func__ elsewhere.\n"
651          "\n"
652          "         -> %%b: full backtrace (Called %%throwable in LOG4J). Defined only under windows or when using the "
653          "GNU libc because\n"
654          "                 backtrace() is not defined elsewhere, and we only have a fallback for windows boxes, not "
655          "mac ones for example.\n"
656          "         -> %%B: short backtrace (only the first line of the %%b). Called %%throwable{short} in LOG4J; "
657          "defined where %%b is.\n"
658          "\n"
659          "         -> %%d: date (UNIX-like epoch)\n"
660          "         -> %%r: application age (time elapsed since the beginning of the application)\n"
661          "\n"
662          "   Miscellaneous:\n"
663          "      --help-log-categories    Display the current hierarchy of log categories.\n"
664          "      --log=no_loc             Don't print file names in messages (for tesh tests).\n"
665          "\n");
666 }
667
668 static int xbt_log_cat_cmp(const void *pa, const void *pb)
669 {
670   xbt_log_category_t a = *(xbt_log_category_t *)pa;
671   xbt_log_category_t b = *(xbt_log_category_t *)pb;
672   return strcmp(a->name, b->name);
673 }
674
675 static void xbt_log_help_categories_rec(xbt_log_category_t category, const char *prefix)
676 {
677   char *this_prefix;
678   char *child_prefix;
679   unsigned i;
680   xbt_log_category_t cat;
681
682   if (!category)
683     return;
684
685   if (category->parent) {
686     this_prefix = bprintf("%s \\_ ", prefix);
687     child_prefix = bprintf("%s |  ", prefix);
688   } else {
689     this_prefix = xbt_strdup(prefix);
690     child_prefix = xbt_strdup(prefix);
691   }
692
693   xbt_dynar_t dynar = xbt_dynar_new(sizeof(xbt_log_category_t), nullptr);
694   for (cat = category; cat != nullptr; cat = cat->nextSibling)
695     xbt_dynar_push_as(dynar, xbt_log_category_t, cat);
696
697   xbt_dynar_sort(dynar, xbt_log_cat_cmp);
698
699   xbt_dynar_foreach(dynar, i, cat){
700     if (i == xbt_dynar_length(dynar) - 1 && category->parent)
701       *strrchr(child_prefix, '|') = ' ';
702     printf("%s%s: %s\n", this_prefix, cat->name, cat->description);
703     xbt_log_help_categories_rec(cat->firstChild, child_prefix);
704   }
705
706   xbt_dynar_free(&dynar);
707   xbt_free(this_prefix);
708   xbt_free(child_prefix);
709 }
710
711 static void xbt_log_help_categories(void)
712 {
713   printf("Current log category hierarchy:\n");
714   xbt_log_help_categories_rec(&_XBT_LOGV(XBT_LOG_ROOT_CAT), "   ");
715   printf("\n");
716 }