#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
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)
{
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;
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;
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;
* 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