From 74b59a3a38eb2e24c4ff3641fc2680d698f2fade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 12 Jun 2013 21:50:59 +0200 Subject: [PATCH] Implement setting log mask string on a destination. Improve logging docs --- include/ucore/logging.h | 53 ++++++++++++++++++++++++- src/logging.c | 88 +++++++++++++++++++++++++++++++++++++---- test/logging.c | 14 +++++-- test/test_logging.c | 56 ++++++++++++++++++++++++-- 4 files changed, 194 insertions(+), 17 deletions(-) diff --git a/include/ucore/logging.h b/include/ucore/logging.h index b018bd4..c7ca11e 100644 --- a/include/ucore/logging.h +++ b/include/ucore/logging.h @@ -58,6 +58,13 @@ * And the resulting log line will include the short_name * modules[DB].short_name * + * Log levels of the messages logged are checked first + * by the log_level of the uc_log_destination, and then + * by the log_level of the module for that destination. + * + * Thus for a message to actually be logged the level + * must be above the overall log level for the destination and + * the log level of the module. */ #ifdef __cplusplus @@ -93,7 +100,11 @@ enum UC_LOG_DESTINATION { struct uc_log_module { /** The id of the log module. * This is the id one specifies in the various log - * functions/macros*/ + * functions/macros. + * Within the struct uc_log_modules, this id must start + * at 0 and be incremented consecutively, being equal + * to the index within struct uc_log_modules.mods + */ int id; /** A short name for the module. @@ -103,7 +114,8 @@ struct uc_log_module { const char *long_name; /** The log level of this module. Only * log statements with a level >= the log_level - * are actually logged */ + * are actually logged, but the log_level of a destination + * can restrict the logging further*/ int log_level; }; @@ -202,6 +214,43 @@ void uc_log_destination_set_log_location(struct uc_log_destination *dest, int lo void uc_log_delete_destination(struct uc_log_destination *dest); +/** Set the log level for a particular module on this destination + * + * @param dest destination to chang + * @param moudle the module id to change + * @param log_level new log level to set for the module + */ +void uc_log_destination_set_module_loglevel( + struct uc_log_destination *dest, + int module, + enum UC_LOG_LEVEL log_level +); + +/** Set the log mask for the given destination. + * The log_mask is a string specifying the log levels for + * the modules logged by this destination. + * The log_level of the destination itself takes priority + * over the log_level of the modules. + * The format of the log_mask string is. + * + * SHORT_NAME_1=LEVEL:SHORT_NAME_2=LEVEL:SHORT_NAME_N=LEVEL + * + * The SHORT_NAME_N is the .short_name within a struct uc_log_module + * LEVEL is one of DEBUG,INFO, WARNING or ERROR + * Example: + * DB=DEBUG:IO=ERROR:POLLER=INFO + * + * Modules not mentioned in the string retain their default value. + * No whitespaces must be specifed before/after the : or = character + * + * @param dest destination to set the log mask on + * @parama log_mask the log mask as described above to set. + */ +void uc_log_destination_set_mask( + struct uc_log_destination *dest, + const char *log_mask +); + /** Closes and re-opens all the UC_LDEST_FILE log destinations. * Intended for use when a log file is rotated externally. * diff --git a/src/logging.c b/src/logging.c index 057d63d..0784165 100644 --- a/src/logging.c +++ b/src/logging.c @@ -67,12 +67,12 @@ static const struct uc_log_module g_unknown_module = { static SLIST_HEAD(, uc_log_destination) g_destinations; static int uc_log_reopen_file(struct uc_log_destination *dest); //Uses enum UC_LOG_LEVEL as index -static const char *const g_debug_levels[] = { +static const char *const g_log_levels[] = { "NONE", "DEBUG", "UNKNOWN_2", "INFO", - "UNKNOWN_4" + "UNKNOWN_4", "WARNING", "UNKNOWN_6", "ERROR" @@ -80,20 +80,20 @@ static const char *const g_debug_levels[] = { inline const char *uc_ll_2_str(enum UC_LOG_LEVEL l) { - if (l < ARRAY_SIZE(g_debug_levels)) { - return g_debug_levels[l]; + if (l < ARRAY_SIZE(g_log_levels)) { + return g_log_levels[l]; } else { return "???"; } } //returns if the string is not a known log level -static int uc_str_2_ll(const char *str) +static int uc_str_2_ll(const char *str, size_t len) { size_t i; - for (i = 0; i < ARRAY_SIZE(g_debug_levels); i++) { - if (strcmp(str, g_debug_levels[i]) == 0) { + for (i = 0; i < ARRAY_SIZE(g_log_levels); i++) { + if (strncasecmp(str, g_log_levels[i], len) == 0) { return (int)i; } } @@ -411,7 +411,6 @@ static inline void uc_vlog_file(const struct uc_log_args *args, const char *fmt, time_buf[sizeof time_buf -1] = 0; - if (dest->log_location) { fprintf(dest->type.file.file, "[%-7s] %s %s:%d [%s] : ", uc_ll_2_str(args->log_level), time_buf, args->file, @@ -467,6 +466,79 @@ log: } } +void uc_log_destination_set_module_loglevel( + struct uc_log_destination *dest, + int module, + enum UC_LOG_LEVEL log_level +) +{ + if (dest == NULL) + return; + + pthread_mutex_lock(&g_log_lock); + + if (module >= 0 && module < g_modules.cnt) + dest->module_settings[module].log_level = log_level; + + pthread_mutex_unlock(&g_log_lock); +} + + +void uc_log_destination_set_mask( + struct uc_log_destination *dest, + const char *log_mask +) +{ + size_t module_len; + + if (dest == NULL || log_mask == NULL) + return; + + pthread_mutex_lock(&g_log_lock); + + while ((module_len = strcspn(log_mask, "=")) != 0) { + int i; + int level_len; + const char *level_str; + int log_level; + const char *module_str = log_mask; + + log_mask += module_len; + if (log_mask[0] == '=') + log_mask++; + + level_len = strcspn(log_mask, ":"); + if (level_len == 0) + continue; + + level_str = log_mask; + log_mask += level_len; + if (log_mask[0] == ':') + log_mask++; + + log_level = uc_str_2_ll(level_str, level_len); + if (log_level == -1) + continue; + + for (i = 0; i < g_modules.cnt; i++) { + size_t short_name_len;; + + if (g_modules.mods[i].short_name == NULL) + continue; + + short_name_len = strlen(g_modules.mods[i].short_name); + module_len = UC_MAX(module_len, short_name_len); + + if (strncasecmp(g_modules.mods[i].short_name, + module_str, module_len) == 0) { + dest->module_settings[i].log_level = log_level; + } + } + } + + pthread_mutex_unlock(&g_log_lock); +} + void uc_logf(int log_level, int module, int raw, const char *file, int line, const char *fmt, ...) { diff --git a/test/logging.c b/test/logging.c index 8305ace..2556b2b 100644 --- a/test/logging.c +++ b/test/logging.c @@ -37,25 +37,31 @@ int main(int argc, char *argv[]) { struct uc_log_destination *dest; uc_log_init(&modules); - dest = uc_log_new_stderr(UC_LL_INFO, 0); - uc_log_add_destination(dest); dest = uc_log_new_file("test.log", UC_LL_INFO, 1); uc_log_add_destination(dest); dest = uc_log_new_syslog("log.test", LOG_DAEMON, UC_LL_INFO, 0); uc_log_add_destination(dest); + dest = uc_log_new_stderr(UC_LL_INFO, 0); + uc_log_add_destination(dest); UC_LOGF( UC_LL_INFO, CC, "Test cc info %d\n", 2); UC_LOGF( UC_LL_WARNING, HO, "Test HO warning%d\n", 2); UC_LOGF( UC_LL_INFO, MAIN, "Test MAIN info \n" ); UC_LOGF( UC_LL_WARNING, MAIN, "Test MAIN warning\n" ); - uc_log_module_set_loglevel(MAIN, UC_LL_INFO); + uc_log_destination_set_module_loglevel(dest, MAIN, UC_LL_INFO); UC_LOGF( UC_LL_INFO, MAIN, "Test MAIN INFO 2\n" ); UC_LOGFR(UC_LL_INFO, MAIN, "Test RAW logging 1\n" ); - uc_log_module_set_loglevel(MAIN, UC_LL_WARNING); + uc_log_destination_set_module_loglevel(dest,MAIN, UC_LL_WARNING); UC_LOGF( UC_LL_INFO, MAIN, "Test MAIN INFO 3\n" ); + uc_log_destination_set_loglevel(dest, UC_LL_DEBUG); + UC_LOGF( UC_LL_DEBUG, MAIN, "Test MAIN DEBUG SHOULD NOT SHOW\n" ); + //uc_log_destination_set_module_loglevel(dest, MAIN, UC_LL_INFO); + uc_log_destination_set_mask(dest, "HO=ERROR:MAIN=DEBUG:HO=DEBUG"); + UC_LOGF( UC_LL_DEBUG, MAIN, "Test MAIN DEBUG SHOULD SHOW\n" ); + uc_log_delete_destination(dest); diff --git a/test/test_logging.c b/test/test_logging.c index 97ce98d..d0ad57f 100644 --- a/test/test_logging.c +++ b/test/test_logging.c @@ -51,12 +51,15 @@ START_TEST (test_logfile_location) fail_if(dest == NULL); uc_log_add_destination(dest); - UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson %d\n", 1); - UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson 2\n"); + UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson %d\n", 1); + UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson 2\n"); + UC_LOGF(UC_LL_INFO, 0, "Lorum ipson 3\n"); + UC_LOGF(UC_LL_WARNING, 0, "Lorum ipson 4\n"); + UC_LOGF(UC_LL_ERROR, 0, "Lorum ipson 5\n"); rc = stat(FILE_NAME, &st); fail_if(rc != 0, "stat failed: %s\n", strerror(errno)); - fail_if(st.st_size != 76*2, "was %ld\n", (long)st.st_size); + fail_if(st.st_size != 380, "was %ld\n", (long)st.st_size); END_TEST START_TEST (test_logfile_no_location) @@ -361,6 +364,52 @@ START_TEST (test_logfile_raw) fail_if(st.st_size != 14*2, "was %ld\n", (long)st.st_size); END_TEST +START_TEST (test_logfile_dest_mask) + + struct uc_log_module m[] = { + { + .id = 0, + .short_name = "MOD1", + .long_name = "Module 1", + .log_level = UC_LL_WARNING, + }, + { + .id = 1, + .short_name = "MOD2", + .long_name = "Module 2", + .log_level = UC_LL_WARNING, + } + }; + struct uc_log_modules mods = { + .mods = m, + .cnt = 2, + }; + struct stat st; + struct uc_log_destination *dest; + int rc; + + uc_log_init(&mods); + + dest = uc_log_new_file(FILE_NAME, UC_LL_DEBUG, 1); + fail_if(dest == NULL); + uc_log_add_destination(dest); + + UC_LOGFR(UC_LL_DEBUG, 0, "Lorum ipson %d\n", 1); + UC_LOGFR(UC_LL_DEBUG, 0, "Lorum ipson %d\n", 1); + + rc = stat(FILE_NAME, &st); + fail_if(rc != 0, "stat failed: %s\n", strerror(errno)); + fail_if(st.st_size != 0, "was %ld\n", (long)st.st_size); + + uc_log_destination_set_mask(dest, "MOD1=DEBUG:mod2=Debug"); + UC_LOGFR(UC_LL_DEBUG, 0, "Lorum ipson %d\n", 1); + UC_LOGFR(UC_LL_DEBUG, 1, "Lorum ipson %d\n", 1); + + rc = stat(FILE_NAME, &st); + fail_if(rc != 0, "stat failed: %s\n", strerror(errno)); + fail_if(st.st_size != 14*2, "was %ld\n", (long)st.st_size); +END_TEST + Suite *logging_suite(void) { @@ -374,6 +423,7 @@ Suite *logging_suite(void) tcase_add_test(tc1, test_logfile_loglevel_surpressed_module); tcase_add_test(tc1, test_logfile_reopen_files); tcase_add_test(tc1, test_logfile_remove_dest); + tcase_add_test(tc1, test_logfile_dest_mask); if (access("/dev/full", R_OK) == 0) { tcase_add_test(tc1, test_logfile_full_filesystem);