diff mbox

Log API for internal use from ODP implementation modules.

Message ID 1408980152-10343-1-git-send-email-ola.liljedahl@linaro.org
State New
Headers show

Commit Message

Ola Liljedahl Aug. 25, 2014, 3:22 p.m. UTC
Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org>
---
(This document/code contribution attached is provided under the terms of agreement LES-LTM-21309)
Log API for internal ODP use.
Logging can be compile time and run time disabled.
Compile out all log levels: -DODP_LOG_LEVEL=0
Compile in all log levels: -DODP_LOG_LEVEL=7
Enable all log levels: -DODP_LOG_DEFAULT_MASK=7 (default: fatal+error)
Run time setting: odp_log_set_level("module_name", level);
Log messages can be redirected: odp_log_set_callback(fn);

Usage:
ODP_LOG_DECLARE(my_mod); /* File-scope declaration */
ODP_LOG_REGISTER(my_mod); /* Init call */
ODP_LOG_ERROR(my_mod, "Error %u has occured\n", 242);

 platform/linux-generic/Makefile.am                |   2 +
 platform/linux-generic/include/api/odp_log.h      |  59 +++++++
 platform/linux-generic/include/odp_log_internal.h | 115 +++++++++++++
 platform/linux-generic/odp_init.c                 |  26 +--
 platform/linux-generic/odp_log.c                  | 189 ++++++++++++++++++++++
 5 files changed, 382 insertions(+), 9 deletions(-)
 create mode 100644 platform/linux-generic/include/api/odp_log.h
 create mode 100644 platform/linux-generic/include/odp_log_internal.h
 create mode 100644 platform/linux-generic/odp_log.c

Comments

Maxim Uvarov Sept. 2, 2014, 6:53 p.m. UTC | #1
Ping the list for reviewers. Please find a time to review/test/comment 
that patch.

Maxim.

On 08/25/2014 07:22 PM, Ola Liljedahl wrote:
> Signed-off-by: Ola Liljedahl <ola.liljedahl@linaro.org>
> ---
> (This document/code contribution attached is provided under the terms of agreement LES-LTM-21309)
> Log API for internal ODP use.
> Logging can be compile time and run time disabled.
> Compile out all log levels: -DODP_LOG_LEVEL=0
> Compile in all log levels: -DODP_LOG_LEVEL=7
> Enable all log levels: -DODP_LOG_DEFAULT_MASK=7 (default: fatal+error)
> Run time setting: odp_log_set_level("module_name", level);
> Log messages can be redirected: odp_log_set_callback(fn);
>
> Usage:
> ODP_LOG_DECLARE(my_mod); /* File-scope declaration */
> ODP_LOG_REGISTER(my_mod); /* Init call */
> ODP_LOG_ERROR(my_mod, "Error %u has occured\n", 242);
>
>   platform/linux-generic/Makefile.am                |   2 +
>   platform/linux-generic/include/api/odp_log.h      |  59 +++++++
>   platform/linux-generic/include/odp_log_internal.h | 115 +++++++++++++
>   platform/linux-generic/odp_init.c                 |  26 +--
>   platform/linux-generic/odp_log.c                  | 189 ++++++++++++++++++++++
>   5 files changed, 382 insertions(+), 9 deletions(-)
>   create mode 100644 platform/linux-generic/include/api/odp_log.h
>   create mode 100644 platform/linux-generic/include/odp_log_internal.h
>   create mode 100644 platform/linux-generic/odp_log.c
>
> diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
> index f4dfdc1..f30c6ff 100644
> --- a/platform/linux-generic/Makefile.am
> +++ b/platform/linux-generic/Makefile.am
> @@ -20,6 +20,7 @@ include_HEADERS = \
>   		  $(top_srcdir)/platform/linux-generic/include/api/odp_debug.h \
>   		  $(top_srcdir)/platform/linux-generic/include/api/odp_hints.h \
>   		  $(top_srcdir)/platform/linux-generic/include/api/odp_init.h \
> +		  $(top_srcdir)/platform/linux-generic/include/api/odp_log.h \
>   		  $(top_srcdir)/platform/linux-generic/include/api/odp_packet_flags.h \
>   		  $(top_srcdir)/platform/linux-generic/include/api/odp_packet.h \
>   		  $(top_srcdir)/platform/linux-generic/include/api/odp_packet_io.h \
> @@ -58,6 +59,7 @@ __LIB__libodp_la_SOURCES = \
>   			   odp_crypto.c \
>   			   odp_init.c \
>   			   odp_linux.c \
> +			   odp_log.c \
>   			   odp_packet.c \
>   			   odp_packet_flags.c \
>   			   odp_packet_io.c \
> diff --git a/platform/linux-generic/include/api/odp_log.h b/platform/linux-generic/include/api/odp_log.h
> new file mode 100644
> index 0000000..b8c49e3
> --- /dev/null
> +++ b/platform/linux-generic/include/api/odp_log.h
> @@ -0,0 +1,59 @@
> +/* Copyright (c) 2014, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +
> +/**
> + * @file
> + *
> + * ODP internal logging facility - public management API
> + */
> +
> +#ifndef ODP_LOG_H_
> +#define ODP_LOG_H_
> +
> +#include <stdarg.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/** Fatal error for function, may not return to caller */
> +#define ODP_LOG_LVL_FATAL   1U
> +/** Error prevented function to succeed but caller should be able to recover */
> +#define ODP_LOG_LVL_ERROR   2U
> +/** Problem detected in function but recovered locally */
> +#define ODP_LOG_LVL_WARNING 3U
> +/** Informational message intended for user */
> +#define ODP_LOG_LVL_INFORM  4U
> +/** Debug message intended for developer */
> +#define ODP_LOG_LVL_DEBUG   5U
> +
> +#define ODP_LOG_LVL_NONE 0U  /**< Disable all log levels */
> +#define ODP_LOG_LVL_ALL  7U /**< Enable all log levels */
> +
> +typedef volatile uint32_t odp_log_t;
> +#define ODP_LOG_INVALID 0U
> +
> +/** Call-back type definition for user-defined log facility */
> +typedef void (*odp_log_callback_t)(const char *name,
> +				   unsigned level,
> +				   const char *fmt,
> +				   va_list ap);
> +
> +/** Set the log level for a source identified by name */
> +int odp_log_set_level(const char *name, unsigned level);
> +/** Install a user-defined call-back for handling log messages */
> +void odp_log_set_callback(odp_log_callback_t cb);
> +/** Iterate through all registered log sources */
> +int odp_log_next(odp_log_t *phdl, char *name, size_t namelen, unsigned *level);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> diff --git a/platform/linux-generic/include/odp_log_internal.h b/platform/linux-generic/include/odp_log_internal.h
> new file mode 100644
> index 0000000..7ac3ab1
> --- /dev/null
> +++ b/platform/linux-generic/include/odp_log_internal.h
> @@ -0,0 +1,115 @@
> +/* Copyright (c) 2014, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +
> +/**
> + * @file
> + *
> + * ODP internal logging facility - client API
> + */
> +
> +#ifndef ODP_LOG_INTERNAL_H_
> +#define ODP_LOG_INTERNAL_H_
> +
> +#include <stdint.h>
> +#include <odp_log.h>
> +#include <odp_hints.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#ifndef ODP_LOG_LEVEL
> +#define ODP_LOG_LEVEL ODP_LOG_LVL_ALL
> +#endif
> +
> +/* Report a message to the log */
> +#define ODP_LOG_MSG(_lvl, _hdl, _fmt, ...) \
> +do { \
> +	unsigned __lvl = (_lvl); \
> +	odp_log_t __hdl = (_hdl); \
> +	const char *__fmt = (_fmt); \
> +	if (odp_unlikely((__hdl & 0x7) >= __lvl)) \
> +		odp_log_printf(__lvl, __hdl, __fmt, ##__VA_ARGS__); \
> +} while (0)
> +
> +#if ODP_LOG_LEVEL >= ODP_LOG_LVL_FATAL
> +#define ODP_LOG_FATAL(_src, _fmt, ...) \
> +ODP_LOG_MSG(ODP_LOG_LVL_FATAL, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
> +#else
> +#define ODP_LOG_FATAL(_src, _fmt, ...) (void)0
> +#endif
> +
> +#if ODP_LOG_LEVEL >= ODP_LOG_LVL_ERROR
> +#define ODP_LOG_ERROR(_src, _fmt, ...) \
> +ODP_LOG_MSG(ODP_LOG_LVL_ERROR, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
> +#else
> +#define ODP_LOG_ERROR(_src, _fmt, ...) (void)0
> +#endif
> +
> +#if ODP_LOG_LEVEL >= ODP_LOG_LVL_WARNING
> +#define ODP_LOG_WARNING(_src, _fmt, ...) \
> +ODP_LOG_MSG(ODP_LOG_LVL_WARNING, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
> +#else
> +#define ODP_LOG_WARNING(_src, _fmt, ...) (void)0
> +#endif
> +
> +#if ODP_LOG_LEVEL >= ODP_LOG_LVL_INFORM
> +#define ODP_LOG_INFORM(_src, _fmt, ...) \
> +ODP_LOG_MSG(ODP_LOG_LVL_INFORM, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
> +#else
> +#define ODP_LOG_INFORM(_src, _fmt, ...) (void)0
> +#endif
> +
> +#if ODP_LOG_LEVEL >= ODP_LOG_LVL_DEBUG
> +#define ODP_LOG_DEBUG(_src, _fmt, ...) \
> +ODP_LOG_MSG(ODP_LOG_LVL_DEBUG, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
> +#else
> +#define ODP_LOG_DEBUG(_src, _fmt, ...) (void)0
> +#endif
> +
> +
> +#define ODP_LOG_DECLARE(src) odp_log_t _odp_log_src_ ## src
> +#define ODP_LOG_REGISTER(src) odp_log_register(&_odp_log_src_ ## src, #src)
> +
> +/* Calls indirectly used by log clients. Use macros above! */
> +/* Register a log source with the specified name */
> +void odp_log_register(odp_log_t *hdl, const char *name);
> +/* Report a log message */
> +void odp_log_printf(unsigned lvl, odp_log_t hdl, const char *fmt, ...);
> +
> +/* Init log module, specify call-back (or NULL for default behaviour) */
> +void odp_log_init(odp_log_callback_t cb);
> +
> +
> +/* Example usage:
> +
> +ODP_LOG_DECLARE(odp_queue);
> +
> +//Use default output mechanism
> +odp_log_init(NULL);
> +
> +//Register log source and set default log level
> +ODP_LOG_REGISTER(odp_queue);
> +
> +//Log a message with severity error
> +ODP_LOG_ERROR(odp_queue, "Failed to create queue, param=%u\n", 242);
> +
> +//Set log level for a source to log everything
> +(void)odp_log_set_level("odp_queue", ODP_LOG_LVL_ALL);
> +
> +//Install a custom vprintf function to handle log messages
> +odp_log_set_callback(my_vprintf);
> +
> +//Log a message that will be forwarded to the user-specified log
> +ODP_LOG_INFORM(odp_queue, "Message printed using user-defined call-back\n");
> +
> +*/
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +#endif
> diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c
> index 5b7e192..3677f1b 100644
> --- a/platform/linux-generic/odp_init.c
> +++ b/platform/linux-generic/odp_init.c
> @@ -7,46 +7,54 @@
>   #include <odp_init.h>
>   #include <odp_internal.h>
>   #include <odp_debug.h>
> +#include <odp_log_internal.h>
>   
> +ODP_LOG_DECLARE(odp_init);
>   
>   int odp_init_global(void)
>   {
> +	/* Init log mechanism first so that it can be used by all other
> +	   modules (including init) */
> +	odp_log_init(NULL); /* NULL => Use default output mechanism */
> +
> +	ODP_LOG_REGISTER(odp_init);
> +
>   	odp_thread_init_global();
>   
>   	odp_system_info_init();
>   
>   	if (odp_shm_init_global()) {
> -		ODP_ERR("ODP shm init failed.\n");
> +		ODP_LOG_FATAL(odp_init, "ODP shm init failed.\n");
>   		return -1;
>   	}
>   
>   	if (odp_buffer_pool_init_global()) {
> -		ODP_ERR("ODP buffer pool init failed.\n");
> +		ODP_LOG_FATAL(odp_init, "ODP buffer pool init failed.\n");
>   		return -1;
>   	}
>   
>   	if (odp_queue_init_global()) {
> -		ODP_ERR("ODP queue init failed.\n");
> +		ODP_LOG_FATAL(odp_init, "ODP queue init failed.\n");
>   		return -1;
>   	}
>   
>   	if (odp_schedule_init_global()) {
> -		ODP_ERR("ODP schedule init failed.\n");
> +		ODP_LOG_FATAL(odp_init, "ODP schedule init failed.\n");
>   		return -1;
>   	}
>   
>   	if (odp_pktio_init_global()) {
> -		ODP_ERR("ODP packet io init failed.\n");
> +		ODP_LOG_FATAL(odp_init, "ODP packet io init failed.\n");
>   		return -1;
>   	}
>   
>   	if (odp_timer_init_global()) {
> -		ODP_ERR("ODP timer init failed.\n");
> +		ODP_LOG_FATAL(odp_init, "ODP timer init failed.\n");
>   		return -1;
>   	}
>   
>   	if (odp_crypto_init_global()) {
> -		ODP_ERR("ODP crypto init failed.\n");
> +		ODP_LOG_FATAL(odp_init, "ODP crypto init failed.\n");
>   		return -1;
>   	}
>   
> @@ -59,12 +67,12 @@ int odp_init_local(int thr_id)
>   	odp_thread_init_local(thr_id);
>   
>   	if (odp_pktio_init_local()) {
> -		ODP_ERR("ODP packet io local init failed.\n");
> +		ODP_LOG_FATAL(odp_init, "ODP packet io local init failed.\n");
>   		return -1;
>   	}
>   
>   	if (odp_schedule_init_local()) {
> -		ODP_ERR("ODP schedule local init failed.\n");
> +		ODP_LOG_FATAL(odp_init, "ODP schedule local init failed.\n");
>   		return -1;
>   	}
>   
> diff --git a/platform/linux-generic/odp_log.c b/platform/linux-generic/odp_log.c
> new file mode 100644
> index 0000000..99b2803
> --- /dev/null
> +++ b/platform/linux-generic/odp_log.c
> @@ -0,0 +1,189 @@
> +/* Copyright (c) 2014, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +
> +/**
> + * @file
> + *
> + * ODP logging utility
> + */
> +
> +#include <errno.h>
> +#include <limits.h>
> +#include <stdarg.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <strings.h>
> +#include <odp_debug.h>
> +#include <odp_spinlock.h>
> +#include <odp_sync.h>
> +#include <odp_log.h>
> +#include <odp_log_internal.h>
> +
> +#ifndef ODP_LOG_DEFAULT_MASK
> +#define ODP_LOG_DEFAULT_MASK (ODP_LOG_LVL_FATAL | ODP_LOG_LVL_ERROR)
> +#endif
> +
> +/* Maximum number of log sources */
> +#ifndef ODP_LOG_MAX
> +#define ODP_LOG_MAX 64
> +#endif
> +
> +ODP_STATIC_ASSERT(ODP_LOG_LVL_ALL == 7U, "ODP_LOG_LVL_ALL_ERROR");
> +
> +#define HDL_TO_LVL(hdl) ((hdl) & 0x7U)
> +#define HDL_TO_SLOT(hdl) (((hdl) >> 3U) - 1U)
> +#define SLOT_LVL_TO_HDL(slot, lvl) ((((slot) + 1U) << 3U) | (lvl))
> +#define INVALID_SLOT ~0U
> +
> +struct source {
> +	odp_log_t *phdl;	/* Pointer to the user's odp_log_t variable */
> +	const char *name;
> +};
> +
> +static odp_spinlock_t lock;
> +static volatile odp_log_callback_t callback;
> +ODP_LOG_DECLARE(odp_log);
> +static struct source sources[ODP_LOG_MAX];
> +
> +void
> +odp_log_init(odp_log_callback_t cb)
> +{
> +	odp_spinlock_init(&lock);
> +	callback = cb;
> +	memset(sources, 0, sizeof(sources));
> +	odp_sync_stores();
> +	ODP_LOG_REGISTER(odp_log);
> +	odp_log_set_level("odp_log", ODP_LOG_LVL_ALL);
> +}
> +
> +/* Register a log source with the specified name */
> +void
> +odp_log_register(odp_log_t *phdl, const char *name)
> +{
> +	unsigned i;
> +	unsigned slot = INVALID_SLOT;
> +	odp_spinlock_lock(&lock);
> +	*phdl = ODP_LOG_INVALID;
> +	for (i = 0; i < ODP_LOG_MAX; i++) {
> +		/* Check for unused slot */
> +		if (sources[i].phdl == NULL && slot == INVALID_SLOT) {
> +			/* Found first unused slot */
> +			slot = i;
> +		}
> +		/* Check for source already present */
> +		if (sources[i].name != NULL &&
> +		    strcmp(sources[i].name, name) == 0) {
> +			odp_spinlock_unlock(&lock);
> +			ODP_LOG_ERROR(odp_log,
> +				      "Attempt to re-register source %s\n",
> +				      name);
> +			return;
> +		}
> +	}
> +	if (slot != INVALID_SLOT) {
> +		sources[slot].phdl = phdl;
> +		sources[slot].name = name;
> +		*phdl = SLOT_LVL_TO_HDL(slot, ODP_LOG_DEFAULT_MASK);
> +	}
> +	odp_spinlock_unlock(&lock);
> +	if (slot != INVALID_SLOT)
> +		ODP_LOG_DEBUG(odp_log, "Registered source %s level %u\n",
> +			      name, ODP_LOG_DEFAULT_MASK);
> +	else
> +		ODP_LOG_FATAL(odp_log, "Failed to register source %s\n", name);
> +}
> +
> +/* Set the log level for a source identified by name */
> +int
> +odp_log_set_level(const char *name, unsigned level)
> +{
> +	unsigned i;
> +	bool found = false;
> +	if (level > ODP_LOG_LVL_ALL)
> +		level = ODP_LOG_LVL_ALL;
> +	odp_spinlock_lock(&lock);
> +	for (i = 0; i < ODP_LOG_MAX; i++) {
> +		if (name == NULL ||
> +		    (sources[i].name != NULL &&
> +		     strcmp(sources[i].name, name) == 0)) {
> +			*sources[i].phdl = SLOT_LVL_TO_HDL(i, level);
> +			found = true;
> +			break;
> +		}
> +	}
> +	odp_spinlock_unlock(&lock);
> +	if (found) {
> +		if (name == NULL)
> +			ODP_LOG_DEBUG(odp_log,
> +				      "Set log level for all sources to %u\n",
> +				      level);
> +		else
> +			ODP_LOG_DEBUG(odp_log,
> +				      "Set log level for source %s to %u\n",
> +				      name, level);
> +	} else {
> +		errno = ENOENT;
> +	}
> +	return found ? 0 : -1;
> +}
> +
> +/* Report a log message */
> +void
> +odp_log_printf(unsigned lvl, odp_log_t hdl, const char *fmt, ...)
> +{
> +	unsigned slot = HDL_TO_SLOT(hdl);
> +	if (odp_likely(slot < ODP_LOG_MAX)) {
> +		const char *name = sources[slot].name;
> +		va_list ap;
> +		odp_log_callback_t cb;
> +		va_start(ap, fmt);
> +		cb = callback;
> +		if (cb != NULL) {
> +			cb(name, lvl, fmt, ap);
> +		} else {
> +			fprintf(stderr, "%s/%c: ", name,
> +				" FEWID67"[lvl & 0x7U]);
> +			vfprintf(stderr, fmt, ap);
> +		}
> +		va_end(ap);
> +	}
> +	/* Else ignore call with invalid handle */
> +}
> +
> +/* Install a user-defined call-back for handling log messages */
> +void
> +odp_log_set_callback(odp_log_callback_t cb)
> +{
> +	callback = cb;
> +	odp_sync_stores();
> +}
> +
> +/* Iterate through all registered log sources */
> +int
> +odp_log_next(odp_log_t *phdl, char *name, size_t namelen, unsigned *plvl)
> +{
> +	int another;
> +	unsigned slot;
> +	odp_spinlock_lock(&lock);
> +	if (*phdl == ODP_LOG_INVALID)
> +		slot = 0;	/* First slot */
> +	else
> +		slot = HDL_TO_SLOT(*phdl) + 1U;	/* Next slot */
> +	if (slot < ODP_LOG_MAX && sources[slot].phdl != NULL) {
> +		*phdl = SLOT_LVL_TO_HDL(slot, 0);
> +		strncpy(name, sources[slot].name, namelen - 1U);
> +		name[namelen - 1U] = 0;
> +		*plvl = HDL_TO_LVL(*sources[slot].phdl);
> +		another = 1;	/* true */
> +	} else {
> +		another = 0;	/* false */
> +	}
> +	odp_spinlock_unlock(&lock);
> +	return another;
> +}
diff mbox

Patch

diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index f4dfdc1..f30c6ff 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -20,6 +20,7 @@  include_HEADERS = \
 		  $(top_srcdir)/platform/linux-generic/include/api/odp_debug.h \
 		  $(top_srcdir)/platform/linux-generic/include/api/odp_hints.h \
 		  $(top_srcdir)/platform/linux-generic/include/api/odp_init.h \
+		  $(top_srcdir)/platform/linux-generic/include/api/odp_log.h \
 		  $(top_srcdir)/platform/linux-generic/include/api/odp_packet_flags.h \
 		  $(top_srcdir)/platform/linux-generic/include/api/odp_packet.h \
 		  $(top_srcdir)/platform/linux-generic/include/api/odp_packet_io.h \
@@ -58,6 +59,7 @@  __LIB__libodp_la_SOURCES = \
 			   odp_crypto.c \
 			   odp_init.c \
 			   odp_linux.c \
+			   odp_log.c \
 			   odp_packet.c \
 			   odp_packet_flags.c \
 			   odp_packet_io.c \
diff --git a/platform/linux-generic/include/api/odp_log.h b/platform/linux-generic/include/api/odp_log.h
new file mode 100644
index 0000000..b8c49e3
--- /dev/null
+++ b/platform/linux-generic/include/api/odp_log.h
@@ -0,0 +1,59 @@ 
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP internal logging facility - public management API
+ */
+
+#ifndef ODP_LOG_H_
+#define ODP_LOG_H_
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Fatal error for function, may not return to caller */
+#define ODP_LOG_LVL_FATAL   1U
+/** Error prevented function to succeed but caller should be able to recover */
+#define ODP_LOG_LVL_ERROR   2U
+/** Problem detected in function but recovered locally */
+#define ODP_LOG_LVL_WARNING 3U
+/** Informational message intended for user */
+#define ODP_LOG_LVL_INFORM  4U
+/** Debug message intended for developer */
+#define ODP_LOG_LVL_DEBUG   5U
+
+#define ODP_LOG_LVL_NONE 0U  /**< Disable all log levels */
+#define ODP_LOG_LVL_ALL  7U /**< Enable all log levels */
+
+typedef volatile uint32_t odp_log_t;
+#define ODP_LOG_INVALID 0U
+
+/** Call-back type definition for user-defined log facility */
+typedef void (*odp_log_callback_t)(const char *name,
+				   unsigned level,
+				   const char *fmt,
+				   va_list ap);
+
+/** Set the log level for a source identified by name */
+int odp_log_set_level(const char *name, unsigned level);
+/** Install a user-defined call-back for handling log messages */
+void odp_log_set_callback(odp_log_callback_t cb);
+/** Iterate through all registered log sources */
+int odp_log_next(odp_log_t *phdl, char *name, size_t namelen, unsigned *level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_log_internal.h b/platform/linux-generic/include/odp_log_internal.h
new file mode 100644
index 0000000..7ac3ab1
--- /dev/null
+++ b/platform/linux-generic/include/odp_log_internal.h
@@ -0,0 +1,115 @@ 
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP internal logging facility - client API
+ */
+
+#ifndef ODP_LOG_INTERNAL_H_
+#define ODP_LOG_INTERNAL_H_
+
+#include <stdint.h>
+#include <odp_log.h>
+#include <odp_hints.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ODP_LOG_LEVEL
+#define ODP_LOG_LEVEL ODP_LOG_LVL_ALL
+#endif
+
+/* Report a message to the log */
+#define ODP_LOG_MSG(_lvl, _hdl, _fmt, ...) \
+do { \
+	unsigned __lvl = (_lvl); \
+	odp_log_t __hdl = (_hdl); \
+	const char *__fmt = (_fmt); \
+	if (odp_unlikely((__hdl & 0x7) >= __lvl)) \
+		odp_log_printf(__lvl, __hdl, __fmt, ##__VA_ARGS__); \
+} while (0)
+
+#if ODP_LOG_LEVEL >= ODP_LOG_LVL_FATAL
+#define ODP_LOG_FATAL(_src, _fmt, ...) \
+ODP_LOG_MSG(ODP_LOG_LVL_FATAL, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
+#else
+#define ODP_LOG_FATAL(_src, _fmt, ...) (void)0
+#endif
+
+#if ODP_LOG_LEVEL >= ODP_LOG_LVL_ERROR
+#define ODP_LOG_ERROR(_src, _fmt, ...) \
+ODP_LOG_MSG(ODP_LOG_LVL_ERROR, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
+#else
+#define ODP_LOG_ERROR(_src, _fmt, ...) (void)0
+#endif
+
+#if ODP_LOG_LEVEL >= ODP_LOG_LVL_WARNING
+#define ODP_LOG_WARNING(_src, _fmt, ...) \
+ODP_LOG_MSG(ODP_LOG_LVL_WARNING, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
+#else
+#define ODP_LOG_WARNING(_src, _fmt, ...) (void)0
+#endif
+
+#if ODP_LOG_LEVEL >= ODP_LOG_LVL_INFORM
+#define ODP_LOG_INFORM(_src, _fmt, ...) \
+ODP_LOG_MSG(ODP_LOG_LVL_INFORM, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
+#else
+#define ODP_LOG_INFORM(_src, _fmt, ...) (void)0
+#endif
+
+#if ODP_LOG_LEVEL >= ODP_LOG_LVL_DEBUG
+#define ODP_LOG_DEBUG(_src, _fmt, ...) \
+ODP_LOG_MSG(ODP_LOG_LVL_DEBUG, (_odp_log_src_ ## _src), (_fmt), ##__VA_ARGS__)
+#else
+#define ODP_LOG_DEBUG(_src, _fmt, ...) (void)0
+#endif
+
+
+#define ODP_LOG_DECLARE(src) odp_log_t _odp_log_src_ ## src
+#define ODP_LOG_REGISTER(src) odp_log_register(&_odp_log_src_ ## src, #src)
+
+/* Calls indirectly used by log clients. Use macros above! */
+/* Register a log source with the specified name */
+void odp_log_register(odp_log_t *hdl, const char *name);
+/* Report a log message */
+void odp_log_printf(unsigned lvl, odp_log_t hdl, const char *fmt, ...);
+
+/* Init log module, specify call-back (or NULL for default behaviour) */
+void odp_log_init(odp_log_callback_t cb);
+
+
+/* Example usage:
+
+ODP_LOG_DECLARE(odp_queue);
+
+//Use default output mechanism
+odp_log_init(NULL);
+
+//Register log source and set default log level
+ODP_LOG_REGISTER(odp_queue);
+
+//Log a message with severity error
+ODP_LOG_ERROR(odp_queue, "Failed to create queue, param=%u\n", 242);
+
+//Set log level for a source to log everything
+(void)odp_log_set_level("odp_queue", ODP_LOG_LVL_ALL);
+
+//Install a custom vprintf function to handle log messages
+odp_log_set_callback(my_vprintf);
+
+//Log a message that will be forwarded to the user-specified log
+ODP_LOG_INFORM(odp_queue, "Message printed using user-defined call-back\n");
+
+*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c
index 5b7e192..3677f1b 100644
--- a/platform/linux-generic/odp_init.c
+++ b/platform/linux-generic/odp_init.c
@@ -7,46 +7,54 @@ 
 #include <odp_init.h>
 #include <odp_internal.h>
 #include <odp_debug.h>
+#include <odp_log_internal.h>
 
+ODP_LOG_DECLARE(odp_init);
 
 int odp_init_global(void)
 {
+	/* Init log mechanism first so that it can be used by all other
+	   modules (including init) */
+	odp_log_init(NULL); /* NULL => Use default output mechanism */
+
+	ODP_LOG_REGISTER(odp_init);
+
 	odp_thread_init_global();
 
 	odp_system_info_init();
 
 	if (odp_shm_init_global()) {
-		ODP_ERR("ODP shm init failed.\n");
+		ODP_LOG_FATAL(odp_init, "ODP shm init failed.\n");
 		return -1;
 	}
 
 	if (odp_buffer_pool_init_global()) {
-		ODP_ERR("ODP buffer pool init failed.\n");
+		ODP_LOG_FATAL(odp_init, "ODP buffer pool init failed.\n");
 		return -1;
 	}
 
 	if (odp_queue_init_global()) {
-		ODP_ERR("ODP queue init failed.\n");
+		ODP_LOG_FATAL(odp_init, "ODP queue init failed.\n");
 		return -1;
 	}
 
 	if (odp_schedule_init_global()) {
-		ODP_ERR("ODP schedule init failed.\n");
+		ODP_LOG_FATAL(odp_init, "ODP schedule init failed.\n");
 		return -1;
 	}
 
 	if (odp_pktio_init_global()) {
-		ODP_ERR("ODP packet io init failed.\n");
+		ODP_LOG_FATAL(odp_init, "ODP packet io init failed.\n");
 		return -1;
 	}
 
 	if (odp_timer_init_global()) {
-		ODP_ERR("ODP timer init failed.\n");
+		ODP_LOG_FATAL(odp_init, "ODP timer init failed.\n");
 		return -1;
 	}
 
 	if (odp_crypto_init_global()) {
-		ODP_ERR("ODP crypto init failed.\n");
+		ODP_LOG_FATAL(odp_init, "ODP crypto init failed.\n");
 		return -1;
 	}
 
@@ -59,12 +67,12 @@  int odp_init_local(int thr_id)
 	odp_thread_init_local(thr_id);
 
 	if (odp_pktio_init_local()) {
-		ODP_ERR("ODP packet io local init failed.\n");
+		ODP_LOG_FATAL(odp_init, "ODP packet io local init failed.\n");
 		return -1;
 	}
 
 	if (odp_schedule_init_local()) {
-		ODP_ERR("ODP schedule local init failed.\n");
+		ODP_LOG_FATAL(odp_init, "ODP schedule local init failed.\n");
 		return -1;
 	}
 
diff --git a/platform/linux-generic/odp_log.c b/platform/linux-generic/odp_log.c
new file mode 100644
index 0000000..99b2803
--- /dev/null
+++ b/platform/linux-generic/odp_log.c
@@ -0,0 +1,189 @@ 
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP logging utility
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <odp_debug.h>
+#include <odp_spinlock.h>
+#include <odp_sync.h>
+#include <odp_log.h>
+#include <odp_log_internal.h>
+
+#ifndef ODP_LOG_DEFAULT_MASK
+#define ODP_LOG_DEFAULT_MASK (ODP_LOG_LVL_FATAL | ODP_LOG_LVL_ERROR)
+#endif
+
+/* Maximum number of log sources */
+#ifndef ODP_LOG_MAX
+#define ODP_LOG_MAX 64
+#endif
+
+ODP_STATIC_ASSERT(ODP_LOG_LVL_ALL == 7U, "ODP_LOG_LVL_ALL_ERROR");
+
+#define HDL_TO_LVL(hdl) ((hdl) & 0x7U)
+#define HDL_TO_SLOT(hdl) (((hdl) >> 3U) - 1U)
+#define SLOT_LVL_TO_HDL(slot, lvl) ((((slot) + 1U) << 3U) | (lvl))
+#define INVALID_SLOT ~0U
+
+struct source {
+	odp_log_t *phdl;	/* Pointer to the user's odp_log_t variable */
+	const char *name;
+};
+
+static odp_spinlock_t lock;
+static volatile odp_log_callback_t callback;
+ODP_LOG_DECLARE(odp_log);
+static struct source sources[ODP_LOG_MAX];
+
+void
+odp_log_init(odp_log_callback_t cb)
+{
+	odp_spinlock_init(&lock);
+	callback = cb;
+	memset(sources, 0, sizeof(sources));
+	odp_sync_stores();
+	ODP_LOG_REGISTER(odp_log);
+	odp_log_set_level("odp_log", ODP_LOG_LVL_ALL);
+}
+
+/* Register a log source with the specified name */
+void
+odp_log_register(odp_log_t *phdl, const char *name)
+{
+	unsigned i;
+	unsigned slot = INVALID_SLOT;
+	odp_spinlock_lock(&lock);
+	*phdl = ODP_LOG_INVALID;
+	for (i = 0; i < ODP_LOG_MAX; i++) {
+		/* Check for unused slot */
+		if (sources[i].phdl == NULL && slot == INVALID_SLOT) {
+			/* Found first unused slot */
+			slot = i;
+		}
+		/* Check for source already present */
+		if (sources[i].name != NULL &&
+		    strcmp(sources[i].name, name) == 0) {
+			odp_spinlock_unlock(&lock);
+			ODP_LOG_ERROR(odp_log,
+				      "Attempt to re-register source %s\n",
+				      name);
+			return;
+		}
+	}
+	if (slot != INVALID_SLOT) {
+		sources[slot].phdl = phdl;
+		sources[slot].name = name;
+		*phdl = SLOT_LVL_TO_HDL(slot, ODP_LOG_DEFAULT_MASK);
+	}
+	odp_spinlock_unlock(&lock);
+	if (slot != INVALID_SLOT)
+		ODP_LOG_DEBUG(odp_log, "Registered source %s level %u\n",
+			      name, ODP_LOG_DEFAULT_MASK);
+	else
+		ODP_LOG_FATAL(odp_log, "Failed to register source %s\n", name);
+}
+
+/* Set the log level for a source identified by name */
+int
+odp_log_set_level(const char *name, unsigned level)
+{
+	unsigned i;
+	bool found = false;
+	if (level > ODP_LOG_LVL_ALL)
+		level = ODP_LOG_LVL_ALL;
+	odp_spinlock_lock(&lock);
+	for (i = 0; i < ODP_LOG_MAX; i++) {
+		if (name == NULL ||
+		    (sources[i].name != NULL &&
+		     strcmp(sources[i].name, name) == 0)) {
+			*sources[i].phdl = SLOT_LVL_TO_HDL(i, level);
+			found = true;
+			break;
+		}
+	}
+	odp_spinlock_unlock(&lock);
+	if (found) {
+		if (name == NULL)
+			ODP_LOG_DEBUG(odp_log,
+				      "Set log level for all sources to %u\n",
+				      level);
+		else
+			ODP_LOG_DEBUG(odp_log,
+				      "Set log level for source %s to %u\n",
+				      name, level);
+	} else {
+		errno = ENOENT;
+	}
+	return found ? 0 : -1;
+}
+
+/* Report a log message */
+void
+odp_log_printf(unsigned lvl, odp_log_t hdl, const char *fmt, ...)
+{
+	unsigned slot = HDL_TO_SLOT(hdl);
+	if (odp_likely(slot < ODP_LOG_MAX)) {
+		const char *name = sources[slot].name;
+		va_list ap;
+		odp_log_callback_t cb;
+		va_start(ap, fmt);
+		cb = callback;
+		if (cb != NULL) {
+			cb(name, lvl, fmt, ap);
+		} else {
+			fprintf(stderr, "%s/%c: ", name,
+				" FEWID67"[lvl & 0x7U]);
+			vfprintf(stderr, fmt, ap);
+		}
+		va_end(ap);
+	}
+	/* Else ignore call with invalid handle */
+}
+
+/* Install a user-defined call-back for handling log messages */
+void
+odp_log_set_callback(odp_log_callback_t cb)
+{
+	callback = cb;
+	odp_sync_stores();
+}
+
+/* Iterate through all registered log sources */
+int
+odp_log_next(odp_log_t *phdl, char *name, size_t namelen, unsigned *plvl)
+{
+	int another;
+	unsigned slot;
+	odp_spinlock_lock(&lock);
+	if (*phdl == ODP_LOG_INVALID)
+		slot = 0;	/* First slot */
+	else
+		slot = HDL_TO_SLOT(*phdl) + 1U;	/* Next slot */
+	if (slot < ODP_LOG_MAX && sources[slot].phdl != NULL) {
+		*phdl = SLOT_LVL_TO_HDL(slot, 0);
+		strncpy(name, sources[slot].name, namelen - 1U);
+		name[namelen - 1U] = 0;
+		*plvl = HDL_TO_LVL(*sources[slot].phdl);
+		another = 1;	/* true */
+	} else {
+		another = 0;	/* false */
+	}
+	odp_spinlock_unlock(&lock);
+	return another;
+}