log
authorOlivier Matz <zer0@droids-corp.org>
Thu, 9 Nov 2017 18:35:26 +0000 (19:35 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 9 Nov 2017 18:35:26 +0000 (19:35 +0100)
lib/Makefile
lib/ecoli_log.c
lib/ecoli_log.h
lib/ecoli_malloc.c
lib/main.c

index 4bd4c94..d6a4e80 100644 (file)
@@ -42,6 +42,7 @@ LDFLAGS += --coverage
 
 
 srcs :=
+srcs += ecoli_assert.c
 srcs += ecoli_completed.c
 srcs += ecoli_keyval.c
 srcs += ecoli_log.c
index 3b9d69e..92e2145 100644 (file)
 #include <ecoli_malloc.h>
 #include <ecoli_log.h>
 
-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;
index e117af1..eed1a00 100644 (file)
  * 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 <stdarg.h>
 
+#include <ecoli_assert.h>
+
+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
index 0768cab..7c6f489 100644 (file)
@@ -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;
index 56bc9d9..aaf12b3 100644 (file)
@@ -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);