From db0e63b8c415918f70b93d1d15caa933247f1595 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 9 Nov 2017 19:35:26 +0100 Subject: [PATCH] log --- lib/Makefile | 1 + lib/ecoli_log.c | 74 +++++++++------ lib/ecoli_log.h | 232 ++++++++++++++++++++++++++++++++++++++++----- lib/ecoli_malloc.c | 2 + lib/main.c | 7 +- 5 files changed, 261 insertions(+), 55 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 4bd4c94..d6a4e80 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -42,6 +42,7 @@ LDFLAGS += --coverage srcs := +srcs += ecoli_assert.c srcs += ecoli_completed.c srcs += ecoli_keyval.c srcs += ecoli_log.c diff --git a/lib/ecoli_log.c b/lib/ecoli_log.c index 3b9d69e..92e2145 100644 --- a/lib/ecoli_log.c +++ b/lib/ecoli_log.c @@ -34,38 +34,59 @@ #include #include -static ec_log_t ec_log_fct = ec_log_default; +static ec_log_t ec_log_fct = ec_log_default_cb; static void *ec_log_opaque; struct ec_log_type { char *name; - unsigned int level; + enum ec_log_level level; }; static struct ec_log_type *log_types; static size_t log_types_len; +static enum ec_log_level global_level = EC_LOG_WARNING; -int ec_log_default(int type, unsigned int level, void *opaque, const char *str) +int ec_log_level_set(enum ec_log_level level) { - (void)opaque; + if (level < 0 || level > EC_LOG_DEBUG) + return -1; + global_level = level; - return printf("[%d] %-12s %s", level, ec_log_name(type), str); + return 0; } -int ec_log_fct_register(ec_log_t usr_log, void *opaque) +enum ec_log_level ec_log_level_get(void) { - if (usr_log == NULL) - return -1; + return global_level; +} + +int ec_log_default_cb(int type, enum ec_log_level level, void *opaque, + const char *str) +{ + (void)opaque; - ec_log_fct = usr_log; - ec_log_opaque = opaque; + if (level > ec_log_level_get()) + return 0; + + if (fprintf(stderr, "[%d] %-12s %s", level, ec_log_name(type), str) < 0) + return -1; return 0; } -void ec_log_fct_unregister(void) +int ec_log_fct_register(ec_log_t usr_log, void *opaque) { - ec_log_fct = NULL; + errno = 0; + + if (usr_log == NULL) { + ec_log_fct = ec_log_default_cb; + ec_log_opaque = NULL; + } else { + ec_log_fct = usr_log; + ec_log_opaque = opaque; + } + + return 0; } static int @@ -83,14 +104,6 @@ ec_log_lookup(const char *name) return -1; } -const char * -ec_log_name(int type) -{ - if (type < 0 || (unsigned int)type >= log_types_len) - return "unknown"; - return log_types[type].name; -} - int ec_log_type_register(const char *name) { @@ -105,12 +118,12 @@ ec_log_type_register(const char *name) new_types = ec_realloc(log_types, sizeof(*new_types) * (log_types_len + 1)); if (new_types == NULL) - return -ENOMEM; + return -1; /* errno is set */ log_types = new_types; copy = ec_strdup(name); if (copy == NULL) - return -ENOMEM; + return -1; /* errno is set */ id = log_types_len++; log_types[id].name = copy; @@ -119,16 +132,19 @@ ec_log_type_register(const char *name) return id; } -int ec_vlog(int type, unsigned int level, const char *format, va_list ap) +const char * +ec_log_name(int type) +{ + if (type < 0 || (unsigned int)type >= log_types_len) + return "unknown"; + return log_types[type].name; +} + +int ec_vlog(int type, enum ec_log_level level, const char *format, va_list ap) { char *s; int ret; - if (ec_log_fct == NULL) { - errno = ENODEV; - return -1; - } - ret = vasprintf(&s, format, ap); if (ret < 0) return ret; @@ -139,7 +155,7 @@ int ec_vlog(int type, unsigned int level, const char *format, va_list ap) return ret; } -int ec_log(int type, unsigned int level, const char *format, ...) +int ec_log(int type, enum ec_log_level level, const char *format, ...) { va_list ap; int ret; diff --git a/lib/ecoli_log.h b/lib/ecoli_log.h index e117af1..eed1a00 100644 --- a/lib/ecoli_log.h +++ b/lib/ecoli_log.h @@ -25,53 +25,237 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** + * Logging API + * + * This file provide logging helpers: + * - logging functions, supporting printf-like format + * - several debug level (similar to syslog) + * - named log types + * - redirection of log to a user functions (default logs nothing) + */ + #ifndef ECOLI_LOG_ #define ECOLI_LOG_ -#define EC_LOG_EMERG 0 /* system is unusable */ -#define EC_LOG_ALERT 1 /* action must be taken immediately */ -#define EC_LOG_CRIT 2 /* critical conditions */ -#define EC_LOG_ERR 3 /* error conditions */ -#define EC_LOG_WARNING 4 /* warning conditions */ -#define EC_LOG_NOTICE 5 /* normal but significant condition */ -#define EC_LOG_INFO 6 /* informational */ -#define EC_LOG_DEBUG 7 /* debug-level messages */ - #include +#include + +enum ec_log_level { + EC_LOG_EMERG = 0, /* system is unusable */ + EC_LOG_ALERT = 1, /* action must be taken immediately */ + EC_LOG_CRIT = 2, /* critical conditions */ + EC_LOG_ERR = 3, /* error conditions */ + EC_LOG_WARNING = 4, /* warning conditions */ + EC_LOG_NOTICE = 5, /* normal but significant condition */ + EC_LOG_INFO = 6, /* informational */ + EC_LOG_DEBUG = 7, /* debug-level messages */ +}; + +/** + * Register a log type. + * + * This macro defines a function that will be called at startup (using + * the "constructor" attribute). This function register the named type + * passed as argument, and sets a static global variable + * "ec_log_local_type". This variable is used as the default log type + * for this file when using EC_LOG() or EC_VLOG(). + * + * This macro can be present several times in a file. In this case, the + * local log type is set to the last registered type. + * + * On error, the function aborts. + * + * @param name + * The name of the log to be registered. + */ #define EC_LOG_TYPE_REGISTER(name) \ static int name##_log_type; \ - static int local_log_type; \ + static int ec_log_local_type; \ __attribute__((constructor, used)) \ static void ec_log_register_##name(void) \ { \ - local_log_type = ec_log_type_register(#name); \ - name##_log_type = local_log_type; \ + ec_log_local_type = ec_log_type_register(#name); \ + ec_assert_print(ec_log_local_type >= 0, \ + "cannot register log type.\n"); \ + name##_log_type = ec_log_local_type; \ } - -/* return -1 on error, len(s) on success */ -typedef int (*ec_log_t)(int type, unsigned int level, void *opaque, +/** + * User log function type. + * + * It is advised that a user-defined log function drops all messages + * that are at least as critical as ec_log_level_get(), as done by the + * default handler. + * + * @param type + * The log type identifier. + * @param level + * The log level. + * @param opaque + * The opaque pointer that was passed to ec_log_fct_register(). + * @param str + * The string to log. + * @return + * 0 on success, -1 on error (errno is set). + */ +typedef int (*ec_log_t)(int type, enum ec_log_level level, void *opaque, const char *str); +/** + * Register a user log function. + * + * @param usr_log + * Function pointer that will be invoked for each log call. + * If the parameter is NULL, ec_log_default_cb() is used. + * @param opaque + * Opaque pointer passed to the log function. + * @return + * 0 on success, -1 on error (errno is set). + */ int ec_log_fct_register(ec_log_t usr_log, void *opaque); -void ec_log_fct_unregister(void); +/** + * Register a named log type. + * + * Register a new log type, which is identified by its name. The + * function returns a log identifier associated to the log name. If the + * name is already registered, the function just returns its identifier. + * + * @param name + * The name of the log type. + * @return + * The log type identifier on success (positive or zero), -1 on + * error (errno is set). + */ int ec_log_type_register(const char *name); +/** + * Return the log name associated to the log type identifier. + * + * @param type + * The log type identifier. + * @return + * The name associated to the log type, or "unknown". It always return + * a valid string (never NULL). + */ const char *ec_log_name(int type); -/* same api than printf */ -int ec_log(int type, unsigned int level, const char *format, ...) +/** + * Log a formatted string. + * + * @param type + * The log type identifier. + * @param level + * The log level. + * @param format + * The format string, followed by optional arguments. + * @return + * 0 on success, -1 on error (errno is set). + */ +int ec_log(int type, enum ec_log_level level, const char *format, ...) __attribute__((format(__printf__, 3, 4))); -int ec_vlog(int type, unsigned int level, const char *format, va_list ap); +/** + * Log a formatted string. + * + * @param type + * The log type identifier. + * @param level + * The log level. + * @param format + * The format string. + * @param ap + * The list of arguments. + * @return + * 0 on success, -1 on error (errno is set). + */ +int ec_vlog(int type, enum ec_log_level level, const char *format, va_list ap); + +/** + * Log a formatted string using the local log type. + * + * This macro requires that a log type is previously register with + * EC_LOG_TYPE_REGISTER() since it uses the "ec_log_local_type" + * variable. + * + * @param level + * The log level. + * @param format + * The format string, followed by optional arguments. + * @return + * 0 on success, -1 on error (errno is set). + */ +#define EC_LOG(level, args...) ec_log(ec_log_local_type, level, args) + +/** + * Log a formatted string using the local log type. + * + * This macro requires that a log type is previously register with + * EC_LOG_TYPE_REGISTER() since it uses the "ec_log_local_type" + * variable. + * + * @param level + * The log level. + * @param format + * The format string. + * @param ap + * The list of arguments. + * @return + * 0 on success, -1 on error (errno is set). + */ +#define EC_VLOG(level, fmt, ap) ec_vlog(ec_log_local_type, level, fmt, ap) + +/** + * Default log handler. + * + * This is the default log function that is used by the library. By + * default, it prints all logs whose level is WARNING or more critical. + * This level can be changed with ec_log_level_set(). + * + * @param type + * The log type identifier. + * @param level + * The log level. + * @param opaque + * Unused. + * @param str + * The string to be logged. + * @return + * 0 on success, -1 on error (errno is set). + */ +int ec_log_default_cb(int type, enum ec_log_level level, void *opaque, + const char *str); -/* to use the macros, the user must have called EC_LOG_TYPE_REGISTER */ -#define EC_LOG(level, args...) ec_log(local_log_type, level, args) -#define EC_VLOG(level, fmt, ap) ec_vlog(local_log_type, level, fmt, ap) +/** + * Set the global log level. + * + * This level is used by the default log handler, ec_log_default_cb(). + * All messages that are at least as critical as the default level are + * displayed. + * + * It is advised + * + * @param level + * The log level to be set. + * @return + * 0 on success, -1 on error. + */ +int ec_log_level_set(enum ec_log_level level); -/* default log handler for the library, use printf */ -int ec_log_default(int type, unsigned int level, void *opaque, const char *str); +/** + * Get the global log level. + * + * This level is used by the default log handler, ec_log_default_cb(). + * All messages that are at least as critical as the default level are + * displayed. + * + * @param level + * The log level to be set. + * @return + * 0 on success, -1 on error. + */ +enum ec_log_level ec_log_level_get(void); #endif diff --git a/lib/ecoli_malloc.c b/lib/ecoli_malloc.c index 0768cab..7c6f489 100644 --- a/lib/ecoli_malloc.c +++ b/lib/ecoli_malloc.c @@ -49,6 +49,7 @@ int ec_malloc_register(ec_malloc_t usr_malloc, ec_free_t usr_free, void ec_malloc_unregister(void) { + /* XXX handlers must set errno on error */ ec_malloc_handler.malloc = NULL; ec_malloc_handler.free = NULL; ec_malloc_handler.realloc = NULL; @@ -80,6 +81,7 @@ void *__ec_calloc(size_t nmemb, size_t size, const char *file, void *ptr; size_t total; + /* check overflow */ total = size * nmemb; if (nmemb != 0 && size != (total / nmemb)) { errno = ENOMEM; diff --git a/lib/main.c b/lib/main.c index 56bc9d9..aaf12b3 100644 --- a/lib/main.c +++ b/lib/main.c @@ -365,7 +365,10 @@ static int debug_log(int type, unsigned int level, void *opaque, if (level > (unsigned int)log_level) return 0; - return printf("%s", str); + if (printf("%s", str) < 0) + return -1; + + return 0; } int main(int argc, char **argv) @@ -381,7 +384,7 @@ int main(int argc, char **argv) srandom(seed); - if (0) ec_log_fct_register(debug_log, NULL); + ec_log_fct_register(debug_log, NULL); /* register a new malloc to track memleaks */ TAILQ_INIT(&debug_alloc_hdr_list); -- 2.20.1