X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/3d0d626e19a7b79320e7d922c9c1dcf122076cea..8bcb64274105bb46c01c6486534cf0110828a00a:/src/xbt/log.c diff --git a/src/xbt/log.c b/src/xbt/log.c index eb15e532ef..83ee7ff3b0 100644 --- a/src/xbt/log.c +++ b/src/xbt/log.c @@ -2,34 +2,247 @@ /* log - a generic logging facility in the spirit of log4j */ -/* Authors: Martin Quinson */ -/* Copyright (C) 2003, 2004 Martin Quinson. */ +/* Copyright (c) 2003, 2004 Martin Quinson. All rights reserved. */ /* This program is free software; you can redistribute it and/or modify it - under the terms of the license (GNU LGPL) which comes with this package. */ + * under the terms of the license (GNU LGPL) which comes with this package. */ -#include "xbt_interface.h" -#include "gras_private.h" #include -#include +#include "gras_config.h" /* to get a workgin stdarg.h */ #include +#include "xbt_modinter.h" + +#include "xbt/misc.h" +#include "xbt/sysdep.h" +#include "xbt/log.h" +#include "xbt/error.h" +#include "xbt/dynar.h" + +/** \addtogroup XBT_log + * + * This section describes the API to the log functions used + * everywhere in this project. + +\section log_overview Overview + +This is an adaptation of the log4c project, which is dead upstream, and +which I was given the permission to fork under the LGPL licence by the +authors. log4c itself was loosely based on the Apache project's Log4J, +Log4CC, etc. project. Because C is not object oriented, a lot had to change. + +There is 3 main concepts: category, priority and appender. These three +concepts work together to enable developers to log messages according to +message type and priority, and to control at runtime how these messages are +formatted and where they are reported. + +\section log_cat Category hierarchy + +The first and foremost advantage of any logging API over plain printf() +resides in its ability to disable certain log statements while allowing +others to print unhindered. This capability assumes that the logging space, +that is, the space of all possible logging statements, is categorized +according to some developer-chosen criteria. + +This observation led to choosing category as the central concept of the +system. Every category is declared by providing a name and an optional +parent. If no parent is explicitly named, the root category, LOG_ROOT_CAT is +the category's parent. + +A category is created by a macro call at the top level of a file. A +category can be created with any one of the following macros: + + - \ref XBT_LOG_NEW_CATEGORY(MyCat); Create a new root + - \ref XBT_LOG_NEW_SUBCATEGORY(MyCat, ParentCat); + Create a new category being child of the category ParentCat + - \ref XBT_LOG_NEW_DEFAULT_CATEGORY(MyCat); + Like XBT_LOG_NEW_CATEGORY, but the new category is the default one + in this file + - \ref XBT_LOG_NEW_DEFAULT_SUBCATEGORY(MyCat, ParentCat); + Like XBT_LOG_NEW_SUBCATEGORY, but the new category is the default one + in this file + +The parent cat can be defined in the same file or in another file (in +which case you want to use the \ref XBT_LOG_EXTERNAL_CATEGORY macro to make +it visible in the current file), but each category may have only one +definition. + +Typically, there will be a Category for each module and sub-module, so you +can independently control logging for each module. + +For a list of all existing categories, please refer to the \ref XBT_log_cats section. + +\section log_pri Priority + +A category may be assigned a threshold priorty. The set of priorites are +defined by the \ref e_xbt_log_priority_t enum. All logging request under +this priority will be discarded. + +If a given category is not assigned a threshold priority, then it inherits +one from its closest ancestor with an assigned threshold. To ensure that all +categories can eventually inherit a threshold, the root category always has +an assigned threshold priority. + +Logging requests are made by invoking a logging macro on a category. All of +the macros have a printf-style format string followed by arguments. If you +compile with the -Wall option, gcc will warn you for unmatched arguments, ie +when you pass a pointer to a string where an integer was specified by the +format. This is usualy a good idea. + +Because most C compilers do not support vararg macros, there is a version of +the macro for any number of arguments from 0 to 6. The macro name ends with +the total number of arguments. + +Here is an example of the most basic type of macro. This is a logging +request with priority warning. + +CLOG5(MyCat, gras_log_priority_warning, "Values are: %d and '%s'", 5, +"oops"); + +A logging request is said to be enabled if its priority is higher than or +equal to the threshold priority of its category. Otherwise, the request is +said to be disabled. A category without an assigned priority will inherit +one from the hierarchy. + +It is possible to use any non-negative integer as a priority. If, as in the +example, one of the standard priorites is used, then there is a convenience +macro that is typically used instead. For example, the above example is +equivalent to the shorter: + +CWARN4(MyCat, "Values are: %d and '%s'", 5, "oops"); + +\subsection log_subcat Using a default category + +If \ref XBT_LOG_NEW_DEFAULT_SUBCATEGORY(MyCat, Parent) or +\ref XBT_LOG_NEW_DEFAULT_CATEGORY(MyCat) is used to create the +category, then the even shorter form can be used: + +WARN3("Values are: %d and '%s'", 5, "oops"); + +Only one default category can be created per file, though multiple +non-defaults can be created and used. + +\section log_example Example + +Here is a more complete example: + +\verbatim +#include "xbt/log.h" + +/ * create a category and a default subcategory * / +XBT_LOG_NEW_CATEGORY(VSS); +XBT_LOG_NEW_DEFAULT_SUBCATEGORY(SA, VSS); + +int main() { + / * Now set the parent's priority. (the string would typcially be a runtime option) * / + xbt_log_control_set("SA.thresh=3"); + + / * This request is enabled, because WARNING >= INFO. * / + CWARN2(VSS, "Low fuel level."); + + / * This request is disabled, because DEBUG < INFO. * / + CDEBUG2(VSS, "Starting search for nearest gas station."); + + / * The default category SA inherits its priority from VSS. Thus, + the following request is enabled because INFO >= INFO. * / + INFO1("Located nearest gas station."); + + / * This request is disabled, because DEBUG < INFO. * / + DEBUG1("Exiting gas station search"); +} +\endverbatim + +\section log_conf Configuration +Configuration is typically done during program initialization by invoking +the xbt_log_control_set() method. The control string passed to it typically +comes from the command line. Look at the documentation for that function for +the format of the control string. + +Any SimGrid program can furthermore be configured at run time by passing a +--xbt-log argument on the command line (--gras-log, --msg-log and +--surf-log are synonyms). You can provide several of those arguments to +change the setting of several categories. + +\section log_perf Performance + +Clever design insures efficiency. Except for the first invocation, a +disabled logging request requires an a single comparison of a static +variable to a constant. + +There is also compile time constant, \ref XBT_LOG_STATIC_THRESHOLD, which +causes all logging requests with a lower priority to be optimized to 0 cost +by the compiler. By setting it to gras_log_priority_infinite, all logging +requests are statically disabled and cost nothing. Released executables +might be compiled with +\verbatim-DXBT_LOG_STATIC_THRESHOLD=gras_log_priority_infinite\endverbatim + +Compiling with the \verbatim-DNLOG\endverbatim option disables all logging +requests at compilation time while the \verbatim-DNDEBUG\endverbatim disables +the requests of priority below INFO. + +\section log_app Appenders + +Each category has an optional appender. An appender is a pointer to a +structure which starts with a pointer to a doAppend() function. DoAppend() +prints a message to a log. + +When a category is passed a message by one of the logging macros, the +category performs the following actions: + + - if the category has an appender, the message is passed to the + appender's doAppend() function, + - if 'willLogToParent' is true for the category, the message is passed + to the category's parent. + +By default, only the root category have an appender, and 'willLogToParent' +is true for any other category. This situation causes all messages to be +logged by the root category's appender. + +The default appender function currently prints to stderr, and no other one +exist, even if more would be needed, like the one able to send the logs to a +remote dedicated server, or other ones offering different output formats. +This is on our TODO list for quite a while now, but your help would be +welcome here. + +\section log_misc Misc and Caveats + + - Do not use any of the macros that start with '_'. + - Log4J has a 'rolling file appender' which you can select with a run-time + option and specify the max file size. This would be a nice default for + non-kernel applications. + - Careful, category names are global variables. + +*/ + +/* +FAIRE DES ZOLIS LOGS +-------------------- +Pour utiliser les logs, tu déjà faire, non ? Tu colle sur la ligne de +commande un ou plusieurs arguments de la forme + --gras-log=" [+]" (ou sans " si t'as pas d'espace) +chaque réglage étant de la forme: + .thres= +Les différents réglages sont lus de gauche à droite. +"root.thres=debug root.thres=critical" ferme tout, normalement. + +*/ + typedef struct { char *catname; - gras_log_priority_t thresh; -} gras_log_setting_t; + e_xbt_log_priority_t thresh; +} s_xbt_log_setting_t,*xbt_log_setting_t; -static gras_dynar_t *gras_log_settings=NULL; +static xbt_dynar_t xbt_log_settings=NULL; static void _free_setting(void *s) { - gras_log_setting_t *set=(gras_log_setting_t*)s; + xbt_log_setting_t set=(xbt_log_setting_t)s; if (set) { - gras_free(set->catname); + free(set->catname); /* free(set); FIXME: uncommenting this leads to segfault when more than one chunk is passed as gras-log */ } } -const char *gras_log_priority_names[8] = { +const char *xbt_log_priority_names[8] = { "NONE", "TRACE", "DEBUG", @@ -40,64 +253,95 @@ const char *gras_log_priority_names[8] = { "CRITICAL" }; -gras_log_category_t _GRAS_LOGV(GRAS_LOG_ROOT_CAT) = { +s_xbt_log_category_t _XBT_LOGV(XBT_LOG_ROOT_CAT) = { 0, 0, 0, - "root", gras_log_priority_uninitialized, 0, + "root", xbt_log_priority_uninitialized, 0, NULL, 0 }; -GRAS_LOG_NEW_SUBCATEGORY(gras,GRAS_LOG_ROOT_CAT,"All GRAS categories"); -GRAS_LOG_NEW_SUBCATEGORY(gros,GRAS_LOG_ROOT_CAT,"All GROS categories (gras toolbox)"); -GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(log,gros,"Loggings from the logging mecanism itself"); +XBT_LOG_NEW_CATEGORY(xbt,"All XBT categories (simgrid toolbox)"); +XBT_LOG_NEW_CATEGORY(surf,"All SURF categories"); +XBT_LOG_NEW_CATEGORY(msg,"All MSG categories"); +XBT_LOG_NEW_DEFAULT_SUBCATEGORY(log,xbt,"Loggings from the logging mechanism itself"); + +void xbt_log_init(int *argc,char **argv) { + int i,j; + char *opt; + + /* Set logs and init log submodule */ + for (i=1; i<*argc; i++) { + if (!strncmp(argv[i],"--gras-log=",strlen("--gras-log=")) || + !strncmp(argv[i],"--surf-log=",strlen("--surf-log=")) || + !strncmp(argv[i],"--msg-log=",strlen("--msg-log=")) || + !strncmp(argv[i],"--xbt-log=",strlen("--xbt-log="))) { + opt=strchr(argv[i],'='); + opt++; + xbt_log_control_set(opt); + DEBUG1("Did apply '%s' as log setting",opt); + /*remove this from argv*/ + for (j=i+1; j<*argc; j++) { + argv[j-1] = argv[j]; + } + argv[j-1] = NULL; + (*argc)--; + i--; /* compensate effect of next loop incrementation */ + } + } +} +void xbt_log_exit(void) { + VERB0("Exiting log"); + xbt_dynar_free(&xbt_log_settings); + VERB0("Exited log"); +} -static void _apply_control(gras_log_category_t* cat) { +static void _apply_control(xbt_log_category_t cat) { int cursor; - gras_log_setting_t *setting=NULL; + xbt_log_setting_t setting=NULL; int found = 0; - if (!gras_log_settings) + if (!xbt_log_settings) return; - gras_assert0(cat,"NULL category"); - gras_assert(cat->name); + xbt_assert0(cat,"NULL category"); + xbt_assert(cat->name); - gras_dynar_foreach(gras_log_settings,cursor,setting) { - gras_assert0(setting,"Damnit, NULL cat in the list"); - gras_assert1(setting->catname,"NULL setting(=%p)->catname",(void*)setting); + xbt_dynar_foreach(xbt_log_settings,cursor,setting) { + xbt_assert0(setting,"Damnit, NULL cat in the list"); + xbt_assert1(setting->catname,"NULL setting(=%p)->catname",(void*)setting); if (!strcmp(setting->catname,cat->name)) { found = 1; - gras_log_threshold_set(cat, setting->thresh); - gras_dynar_cursor_rm(gras_log_settings,&cursor); + xbt_log_threshold_set(cat, setting->thresh); + xbt_dynar_cursor_rm(xbt_log_settings,&cursor); - if (cat->threshold <= gras_log_priority_verbose) { - gras_log_event_t _log_ev = - {cat,gras_log_priority_verbose,__FILE__,__FUNCTION__,__LINE__}; - _gras_log_event_log(&_log_ev, + if (cat->threshold <= xbt_log_priority_debug) { + s_xbt_log_event_t _log_ev = + {cat,xbt_log_priority_debug,__FILE__,_XBT_FUNCTION,__LINE__}; + _xbt_log_event_log(&_log_ev, "Apply settings for category '%s': set threshold to %s (=%d)", cat->name, - gras_log_priority_names[cat->threshold], cat->threshold); + xbt_log_priority_names[cat->threshold], cat->threshold); } } } - if (!found && cat->threshold <= gras_log_priority_verbose) { - gras_log_event_t _log_ev = - {cat,gras_log_priority_verbose,__FILE__,__FUNCTION__,__LINE__}; - _gras_log_event_log(&_log_ev, + if (!found && cat->threshold <= xbt_log_priority_verbose) { + s_xbt_log_event_t _log_ev = + {cat,xbt_log_priority_verbose,__FILE__,_XBT_FUNCTION,__LINE__}; + _xbt_log_event_log(&_log_ev, "Category '%s': inherited threshold = %s (=%d)", cat->name, - gras_log_priority_names[cat->threshold], cat->threshold); + xbt_log_priority_names[cat->threshold], cat->threshold); } } -void _gras_log_event_log( gras_log_event_t* ev, const char *fmt, ...) { - gras_log_category_t* cat = ev->cat; +void _xbt_log_event_log( xbt_log_event_t ev, const char *fmt, ...) { + xbt_log_category_t cat = ev->cat; va_start(ev->ap, fmt); while(1) { - gras_log_appender_t* appender = cat->appender; + xbt_log_appender_t appender = cat->appender; if (appender != NULL) { appender->do_append(appender, ev, fmt); } @@ -109,12 +353,12 @@ void _gras_log_event_log( gras_log_event_t* ev, const char *fmt, ...) { va_end(ev->ap); } -static void _cat_init(gras_log_category_t* category) { - if (category == &_GRAS_LOGV(GRAS_LOG_ROOT_CAT)) { - category->threshold = gras_log_priority_info; - category->appender = gras_log_default_appender; +static void _cat_init(xbt_log_category_t category) { + if (category == &_XBT_LOGV(XBT_LOG_ROOT_CAT)) { + category->threshold = xbt_log_priority_info; + category->appender = xbt_log_default_appender; } else { - gras_log_parent_set(category, category->parent); + xbt_log_parent_set(category, category->parent); } _apply_control(category); } @@ -124,27 +368,27 @@ static void _cat_init(gras_log_category_t* category) { * initialization. * Also resets threshold to inherited! */ -int _gras_log_cat_init(gras_log_priority_t priority, - gras_log_category_t* category) { +int _xbt_log_cat_init(e_xbt_log_priority_t priority, + xbt_log_category_t category) { _cat_init(category); return priority >= category->threshold; } -void gras_log_parent_set(gras_log_category_t* cat, - gras_log_category_t* parent) { +void xbt_log_parent_set(xbt_log_category_t cat, + xbt_log_category_t parent) { - gras_assert0(cat,"NULL category to be given a parent"); - gras_assert1(parent,"The parent category of %s is NULL",cat->name); + xbt_assert0(cat,"NULL category to be given a parent"); + xbt_assert1(parent,"The parent category of %s is NULL",cat->name); /* unlink from current parent */ - if (cat->threshold != gras_log_priority_uninitialized) { - gras_log_category_t** cpp = &parent->firstChild; + if (cat->threshold != xbt_log_priority_uninitialized) { + xbt_log_category_t* cpp = &parent->firstChild; while(*cpp != cat && *cpp != NULL) { cpp = &(*cpp)->nextSibling; } - assert(*cpp == cat); + xbt_assert(*cpp == cat); *cpp = cat->nextSibling; } @@ -154,7 +398,7 @@ void gras_log_parent_set(gras_log_category_t* cat, parent->firstChild = cat; /* Make sure parent is initialized */ - if (parent->threshold == gras_log_priority_uninitialized) { + if (parent->threshold == xbt_log_priority_uninitialized) { _cat_init(parent); } @@ -163,28 +407,28 @@ void gras_log_parent_set(gras_log_category_t* cat, cat->isThreshInherited = 1; } /* log_setParent */ -static void _set_inherited_thresholds(gras_log_category_t* cat) { - gras_log_category_t* child = cat->firstChild; +static void _set_inherited_thresholds(xbt_log_category_t cat) { + xbt_log_category_t child = cat->firstChild; for( ; child != NULL; child = child->nextSibling) { if (child->isThreshInherited) { - if (cat != &_GRAS_LOGV(log)) + if (cat != &_XBT_LOGV(log)) VERB3("Set category threshold of %s to %s (=%d)", - child->name,gras_log_priority_names[cat->threshold],cat->threshold); + child->name,xbt_log_priority_names[cat->threshold],cat->threshold); child->threshold = cat->threshold; _set_inherited_thresholds(child); } } } -void gras_log_threshold_set(gras_log_category_t* cat, - gras_log_priority_t threshold) { +void xbt_log_threshold_set(xbt_log_category_t cat, + e_xbt_log_priority_t threshold) { cat->threshold = threshold; cat->isThreshInherited = 0; _set_inherited_thresholds(cat); } -static void _gras_log_parse_setting(const char* control_string, - gras_log_setting_t *set) { +static void _xbt_log_parse_setting(const char* control_string, + xbt_log_setting_t set) { const char *name, *dot, *eq; set->catname=NULL; @@ -200,12 +444,12 @@ static void _gras_log_parse_setting(const char* control_string, eq = control_string; control_string += strcspn(control_string, " "); - gras_assert1(*dot == '.' && *eq == '=', + xbt_assert1(*dot == '.' && *eq == '=', "Invalid control string '%s'",control_string); if (!strncmp(dot + 1, "thresh", min(eq - dot - 1,strlen("thresh")))) { int i; - char *neweq=gras_strdup(eq+1); + char *neweq=xbt_strdup(eq+1); char *p=neweq-1; while (*(++p) != '\0') { @@ -215,40 +459,41 @@ static void _gras_log_parse_setting(const char* control_string, } DEBUG1("New priority name = %s",neweq); - for (i=0; ithresh=i; } else { - gras_assert1(FALSE,"Unknown priority name: %s",eq+1); + xbt_assert1(FALSE,"Unknown priority name: %s",eq+1); } - gras_free(neweq); + free(neweq); } else { char buff[512]; snprintf(buff,min(512,eq - dot - 1),"%s",dot+1); - gras_assert1(FALSE,"Unknown setting of the log category: %s",buff); + xbt_assert1(FALSE,"Unknown setting of the log category: %s",buff); } - set->catname=(char*)gras_malloc(dot - name+1); + set->catname=(char*)xbt_malloc(dot - name+1); strncpy(set->catname,name,dot-name); set->catname[dot-name]='\0'; /* Just in case */ DEBUG1("This is for cat '%s'", set->catname); } -static gras_error_t _gras_log_cat_searchsub(gras_log_category_t *cat,char *name,gras_log_category_t**whereto) { - gras_error_t errcode; - gras_log_category_t *child; +static xbt_error_t _xbt_log_cat_searchsub(xbt_log_category_t cat,char *name, + /*OUT*/xbt_log_category_t*whereto) { + xbt_error_t errcode; + xbt_log_category_t child; if (!strcmp(cat->name,name)) { *whereto=cat; return no_error; } for(child=cat->firstChild ; child != NULL; child = child->nextSibling) { - errcode=_gras_log_cat_searchsub(child,name,whereto); + errcode=_xbt_log_cat_searchsub(child,name,whereto); if (errcode==no_error) return no_error; } @@ -288,8 +533,8 @@ static void _cleanup_double_spaces(char *s) { } /** - * gras_log_control_set: - * @cs: What to parse + * \ingroup XBT_log + * \param control_string What to parse * * Typically passed a command-line argument. The string has the syntax: * @@ -298,16 +543,16 @@ static void _cleanup_double_spaces(char *s) { * where [category] is one the category names and keyword is one of the * following: * - * thresh value is an integer priority level. Sets the category's + * thres value is an integer priority level. Sets the category's * threshold priority. * - * @warning + * \warning * This routine may only be called once and that must be before any other * logging command! Typically, this is done from main(). */ -void gras_log_control_set(const char* control_string) { - gras_error_t errcode; - gras_log_setting_t *set; +void xbt_log_control_set(const char* control_string) { + xbt_error_t errcode; + xbt_log_setting_t set; char *cs; char *p; int done = 0; @@ -315,17 +560,17 @@ void gras_log_control_set(const char* control_string) { DEBUG1("Parse log settings '%s'",control_string); if (control_string == NULL) return; - if (gras_log_settings == NULL) - gras_dynar_new(&gras_log_settings,sizeof(gras_log_setting_t*), - _free_setting); + if (xbt_log_settings == NULL) + xbt_log_settings = xbt_dynar_new(sizeof(xbt_log_setting_t), + _free_setting); - set = gras_new(gras_log_setting_t,1); - cs=gras_strdup(control_string); + set = xbt_new(s_xbt_log_setting_t,1); + cs=xbt_strdup(control_string); _cleanup_double_spaces(cs); while (!done) { - gras_log_category_t *cat; + xbt_log_category_t cat; p=strrchr(cs,' '); if (p) { @@ -335,31 +580,26 @@ void gras_log_control_set(const char* control_string) { p=cs; done = 1; } - _gras_log_parse_setting(p,set); + _xbt_log_parse_setting(p,set); - errcode = _gras_log_cat_searchsub(&_GRAS_LOGV(root),set->catname,&cat); + errcode = _xbt_log_cat_searchsub(&_XBT_LOGV(root),set->catname,&cat); if (errcode == mismatch_error) { DEBUG0("Store for further application"); DEBUG1("push %p to the settings",(void*)set); - gras_dynar_push(gras_log_settings,&set); + xbt_dynar_push(xbt_log_settings,&set); /* malloc in advance the next slot */ - set = gras_new(gras_log_setting_t,1); + set = xbt_new(s_xbt_log_setting_t,1); } else { DEBUG0("Apply directly"); - gras_free(set->catname); - gras_log_threshold_set(cat,set->thresh); + free(set->catname); + xbt_log_threshold_set(cat,set->thresh); } } - gras_free(set); - gras_free(cs); + free(set); + free(cs); } -void gras_log_appender_set(gras_log_category_t* cat, gras_log_appender_t* app) { +void xbt_log_appender_set(xbt_log_category_t cat, xbt_log_appender_t app) { cat->appender = app; } -void gras_log_exit(void) { - VERB0("Exiting log"); - gras_dynar_free(gras_log_settings); - VERB0("Exited log"); -}