From cb9b7fc3acc31706225e938fd4526418584d44b0 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 19 Feb 2017 00:42:55 +0100 Subject: [PATCH] add support for individual log level per facility --- src/ebusd/main.cpp | 63 +++++++++++++++++++++++++---- src/ebusd/main.h | 4 ++ src/ebusd/mainloop.cpp | 28 +++++-------- src/lib/utils/log.cpp | 92 +++++++++++++++++++++++------------------- src/lib/utils/log.h | 59 ++++++++++++++++++--------- 5 files changed, 162 insertions(+), 84 deletions(-) diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index fefbbbcf..951786e8 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -103,6 +103,10 @@ static struct options opt = { "/var/" PACKAGE "/html", // htmlPath PACKAGE_LOGFILE, // logFile + -1, // logAreas + ll_COUNT, // logLevel + false, // multiLog + false, // logRaw PACKAGE_LOGFILE, // logRawFile 100, // logRawSize @@ -141,7 +145,8 @@ static const char argpdoc[] = #define O_LOCAL (O_PIDFIL+1) #define O_HTTPPT (O_LOCAL+1) #define O_HTMLPA (O_HTTPPT+1) -#define O_LOGARE (O_HTMLPA+1) +#define O_LOG (O_HTMLPA+1) +#define O_LOGARE (O_LOG+1) #define O_LOGLEV (O_LOGARE+1) #define O_RAW (O_LOGLEV+1) #define O_RAWFIL (O_RAW+1) @@ -191,10 +196,12 @@ static const struct argp_option argpoptions[] = { {NULL, 0, NULL, 0, "Log options:", 5 }, {"logfile", 'l', "FILE", 0, "Write log to FILE (only for daemon) [" PACKAGE_LOGFILE "]", 0 }, - {"logareas", O_LOGARE, "AREAS", 0, "Only write log for matching AREA(S): main,network,bus,update,all " - "[all]", 0 }, - {"loglevel", O_LOGLEV, "LEVEL", 0, "Only write log below or equal to LEVEL: error/notice/info/debug " - "[notice]", 0 }, + {"log", O_LOG, "AREAS LEVEL", 0, "Only write log for matching AREA(S) below or equal to LEVEL" + " (alternative to --logareas/--logevel, may be used multiple times) [all notice]", 0 }, + {"logareas", O_LOGARE, "AREAS", 0, "Only write log for matching AREA(S): main|network|bus|update|all" + " [all]", 0 }, + {"loglevel", O_LOGLEV, "LEVEL", 0, "Only write log below or equal to LEVEL: error|notice|info|debug" + " [notice]", 0 }, {NULL, 0, NULL, 0, "Raw logging options:", 6 }, {"lograwdata", O_RAW, NULL, 0, "Log each received/sent byte on the bus", 0 }, @@ -431,17 +438,53 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) { } opt->logFile = arg; break; + case O_LOG: // --log=area(s) level + { + char* pos = strchr(arg, ' '); + if (pos == NULL) { + argp_error(state, "invalid log"); + return EINVAL; + } + *pos = 0; + int facilities = parseLogFacilities(arg); + if (facilities == -1) { + argp_error(state, "invalid log: areas"); + return EINVAL; + } + LogLevel level = parseLogLevel(pos + 1); + if (level == ll_COUNT) { + argp_error(state, "invalid log: level"); + return EINVAL; + } + if (opt->logAreas != -1 || opt->logLevel != ll_COUNT) { + argp_error(state, "invalid log (combined with logareas or loglevel)"); + return EINVAL; + } + setFacilitiesLogLevel(facilities, level); + opt->multiLog = true; + } + break; case O_LOGARE: // --logareas=all - if (!setLogFacilities(arg)) { + opt->logAreas = parseLogFacilities(arg); + if (opt->logAreas == -1) { argp_error(state, "invalid logareas"); return EINVAL; } + if (opt->multiLog) { + argp_error(state, "invalid logareas (combined with log)"); + return EINVAL; + } break; case O_LOGLEV: // --loglevel=notice - if (!setLogLevel(arg)) { + opt->logLevel = parseLogLevel(arg); + if (opt->logLevel == ll_COUNT) { argp_error(state, "invalid loglevel"); return EINVAL; } + if (opt->multiLog) { + argp_error(state, "invalid loglevel (combined with log)"); + return EINVAL; + } break; // Raw logging options: @@ -1015,11 +1058,17 @@ int main(int argc, char* argv[]) { struct argp aargp = { argpoptions, parse_opt, NULL, argpdoc, datahandler_getargs(), NULL, NULL }; int arg_index = -1; setenv("ARGP_HELP_FMT", "no-dup-args-note", 0); + if (argp_parse(&aargp, argc, argv, ARGP_IN_ORDER, &arg_index, &opt) != 0) { logError(lf_main, "invalid arguments"); return EINVAL; } + if (opt.logAreas != -1 || opt.logLevel != ll_COUNT) { + setFacilitiesLogLevel(LF_ALL, ll_none); + setFacilitiesLogLevel(opt.logAreas, opt.logLevel); + } + s_messageMap = new MessageMap(opt.checkConfig && opt.scanConfig && arg_index >= argc); if (opt.checkConfig) { logNotice(lf_main, PACKAGE_STRING "." REVISION " performing configuration check..."); diff --git a/src/ebusd/main.h b/src/ebusd/main.h index ba784524..4bed3bec 100644 --- a/src/ebusd/main.h +++ b/src/ebusd/main.h @@ -24,6 +24,7 @@ #include "lib/ebus/data.h" #include "lib/ebus/message.h" #include "lib/ebus/result.h" +#include "lib/utils/log.h" /** \file main.h * The main entry method doing all the startup handling. @@ -68,6 +69,9 @@ struct options { const char* htmlPath; //!< path for HTML files served by the HTTP port [/var/ebusd/html] const char* logFile; //!< log file name [/var/log/ebusd.log] + int logAreas; //!< log areas [all] + LogLevel logLevel; //!< log level [notice] + bool multiLog; //!< multiple log levels adjusted with --log=... bool logRaw; //!< raw log each received/sent byte on the bus const char* logRawFile; //!< name of raw log file [/var/log/ebusd.log] diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index 8f362bde..144932d8 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -1295,31 +1295,25 @@ string MainLoop::executeScan(vector &args, string levels) { string MainLoop::executeLog(vector &args) { if (args.size() == 1) { ostringstream ret; - char str[48]; - if (getLogFacilities(str)) { - ret << str << ' '; + for (int val = 0; val < lf_COUNT; val++) { + LogFacility facility = (LogFacility)val; + ret << getLogFacilityStr(facility) << ": " << getLogLevelStr(getFacilityLogLevel(facility)) << "\n"; } - ret << getLogLevel(); return ret.str(); } - bool result; - // old format: log areas AREA[,AREA]*, log level LEVEL - if ((args.size() == 3 || args.size() == 2) && strcasecmp(args[1].c_str(), "AREAS") == 0) { - result = setLogFacilities(args.size() == 3 ? args[2].c_str() : ""); - } else if (args.size() == 3 && strcasecmp(args[1].c_str(), "LEVEL") == 0) { - result = setLogLevel(args[2].c_str()); - } else if (args.size() == 2) { - result = setLogLevel(args[1].c_str()) || setLogFacilities(args[1].c_str()); - } else if (args.size() == 3) { - result = setLogFacilities(args[1].c_str()) && setLogLevel(args[2].c_str()); - } else { + if (args.size() != 3) { return "usage: log [AREA[,AREA]*] [LEVEL]\n" " Set log area(s) and/or log level or get current settings.\n" " AREA log area to include (main|network|bus|update|all)\n" " LEVEL log level to set (error|notice|info|debug)"; } - if (result) { - return getResultCode(RESULT_OK); + int facilities = parseLogFacilities(args[1].c_str()); + LogLevel level = parseLogLevel(args[2].c_str()); + if (facilities != -1 && level != ll_COUNT) { + if (setFacilitiesLogLevel(facilities, level)) { + return getResultCode(RESULT_OK); + } + return "same"; } return getResultCode(RESULT_ERR_INVALID_ARG); } diff --git a/src/lib/utils/log.cpp b/src/lib/utils/log.cpp index 4937dfd2..5f852a10 100644 --- a/src/lib/utils/log.cpp +++ b/src/lib/utils/log.cpp @@ -46,16 +46,28 @@ static const char* levelNames[] = { NULL }; -/** the bit combination of currently active log facilities (1 << @a LogFacility). */ -static int s_logFacilites = LF_ALL; - -/** the current log level. */ -static LogLevel s_logLevel = ll_notice; +/** the current log level by log facility. */ +static LogLevel s_facilityLogLevel[] = { ll_notice, ll_notice, ll_notice, ll_notice, ll_notice, }; /** the current log FILE. */ static FILE* s_logFile = stdout; -bool setLogFacilities(const char* facilities) { +LogFacility parseLogFacility(const char* facility) { + if (!facility) { + return lf_COUNT; + } + char *input = strdup(facility); + char *opt = reinterpret_cast(input), *value = NULL; + int val = getsubopt(&opt, (char *const *)facilityNames, &value); + if (val < 0 || val >= lf_COUNT || value || *opt) { + free(input); + return lf_COUNT; + } + free(input); + return (LogFacility)val; +} + +int parseLogFacilities(const char* facilities) { char *input = strdup(facilities); char *opt = reinterpret_cast(input), *value = NULL; int newFacilites = 0; @@ -63,7 +75,7 @@ bool setLogFacilities(const char* facilities) { int val = getsubopt(&opt, (char *const *)facilityNames, &value); if (val < 0 || val > lf_COUNT || value) { free(input); - return false; + return -1; } if (val == lf_COUNT) { newFacilites = LF_ALL; @@ -71,49 +83,46 @@ bool setLogFacilities(const char* facilities) { newFacilites |= 1 << val; } } - s_logFacilites = newFacilites; free(input); - return true; + return newFacilites; } -bool getLogFacilities(char* buffer) { - if (s_logFacilites == LF_ALL) { - return snprintf(buffer, 48, "%s", facilityNames[lf_COUNT]) != 0; +LogLevel parseLogLevel(const char* level) { + if (!level) { + return ll_COUNT; } - *buffer = 0; // for strcat to work - bool found = false; - size_t len = 0; - for (int val = 0; val < lf_COUNT; val++) { - if (s_logFacilites&(1 << val)) { - if (found) { - len += snprintf(buffer+len, 48-len, ","); - } - found = true; - len += snprintf(buffer+len, 48-len, "%s", facilityNames[val]); - } - } - return true; -} - -bool setLogLevel(const char* level) { char *input = strdup(level); char *opt = reinterpret_cast(input), *value = NULL; - int newLevel = 0; - if (*opt) { - int val = getsubopt(&opt, (char *const *)levelNames, &value); - if (val < 0 || val >= ll_COUNT || value || *opt) { - free(input); - return false; - } - newLevel = val; + int val = getsubopt(&opt, (char *const *)levelNames, &value); + if (val < 0 || val >= ll_COUNT || value || *opt) { + free(input); + return ll_COUNT; } - s_logLevel = (LogLevel)newLevel; free(input); - return true; + return (LogLevel)val; } -const char* getLogLevel() { - return levelNames[s_logLevel]; +const char* getLogFacilityStr(LogFacility facility) { + return facilityNames[facility]; +} + +const char* getLogLevelStr(LogLevel level) { + return levelNames[level]; +} + +bool setFacilitiesLogLevel(int facilities, LogLevel level) { + bool changed = false; + for (int val = 0; val < lf_COUNT && facilities != 0; val++) { + if ((facilities & (1 << val)) != 0 && s_facilityLogLevel[(LogFacility)val] != level) { + s_facilityLogLevel[(LogFacility)val] = level; + changed = true; + } + } + return changed; +} + +LogLevel getFacilityLogLevel(LogFacility facility) { + return s_facilityLogLevel[facility]; } bool setLogFile(const char* filename) { @@ -136,8 +145,7 @@ void closeLogFile() { } bool needsLog(const LogFacility facility, const LogLevel level) { - return ((s_logFacilites & (1 << facility)) != 0) - && (s_logLevel >= level); + return s_facilityLogLevel[facility] >= level; } void logWrite(const char* facility, const char* level, const char* message, va_list ap) { diff --git a/src/lib/utils/log.h b/src/lib/utils/log.h index 20ec877c..4baf46a4 100644 --- a/src/lib/utils/log.h +++ b/src/lib/utils/log.h @@ -31,45 +31,68 @@ enum LogFacility { lf_COUNT = 5 //!< number of available log facilities }; -/** macro for enabling all log facilities. */ +/** macro for all log facilities. */ #define LF_ALL ((1 << lf_main) | (1 << lf_network) | (1 << lf_bus) | (1 << lf_update) | (1 << lf_other)) /** the available log levels. */ enum LogLevel { ll_none = 0, //!< no level at all - ll_error, //!< error message - ll_notice, //!< important message - ll_info, //!< informational message - ll_debug, //!< debugging message (normally suppressed) + ll_error, //!< error message + ll_notice, //!< important message + ll_info, //!< informational message + ll_debug, //!< debugging message (normally suppressed) ll_COUNT = 5 //!< number of available log levels }; /** - * Set the log facilities from the string. - * @param facilities the string to parse the facilities from (separated by comma). - * @return true on success, false on error. + * Parse the log facility from the string. + * @param facility the string to parse the singular facility from. + * @return the @a LogFacility, or @a lf_COUNT on error. */ -bool setLogFacilities(const char* facilities); +LogFacility parseLogFacility(const char* facility); /** - * Get the log facilities. - * @param buffer the buffer into which the facilities are written to (separated by comma, buffer needs to be at last 48 characters long). - * @return true on success, false on error. + * Parse the log facilities from the string. + * @param facilities the string to parse the list of facilities from (separated by comma). + * @return the @a LogFacility list as bit mask (1 << facility), or -1 on error. */ -bool getLogFacilities(char* buffer); +int parseLogFacilities(const char* facilities); + +/** + * Get the log facility as string. + * @param level the @a LogFacility. + * @return the log facility as string. + */ +const char* getLogFacilityStr(LogFacility facility); /** * Parse the log level from the string. * @param level the level as string. - * @return true on success, false on error. + * @return the @a LogLevel, or @a ll_COUNT on error. */ -bool setLogLevel(const char* level); +LogLevel parseLogLevel(const char* level); /** - * Get the log level. - * @return the level as string. + * Get the log level as string. + * @param level the @a LogLevel. + * @return the log level as string. */ -const char* getLogLevel(); +const char* getLogLevelStr(LogLevel level); + +/** + * Set the log level for the specified facilities. + * @param facilities the log facilities as bit mask (1 << facility). + * @param level the @a LogLevel to set. + * @return true when a level was changed for a facility, false when no level was changed at all. + */ +bool setFacilitiesLogLevel(int facilities, LogLevel level); + +/** + * Get the log level for the specified facility. + * @param facility the @a LogFacility. + * @return the @a LogLevel. + */ +LogLevel getFacilityLogLevel(LogFacility facility); /** * Set the log file to use.