@@ -78,6 +78,7 @@ eal_long_options[] = {
{OPT_LCORES, 1, NULL, OPT_LCORES_NUM },
{OPT_LOG_LEVEL, 1, NULL, OPT_LOG_LEVEL_NUM },
{OPT_LOG_TIMESTAMP, 2, NULL, OPT_LOG_TIMESTAMP_NUM },
+ {OPT_LOG_COLOR, 1, NULL, OPT_LOG_COLOR_NUM },
{OPT_TRACE, 1, NULL, OPT_TRACE_NUM },
{OPT_TRACE_DIR, 1, NULL, OPT_TRACE_DIR_NUM },
{OPT_TRACE_BUF_SIZE, 1, NULL, OPT_TRACE_BUF_SIZE_NUM },
@@ -1910,6 +1911,15 @@ eal_parse_common_option(int opt, const char *optarg,
}
break;
+
+ case OPT_LOG_COLOR_NUM:
+ if (eal_log_color(optarg) < 0) {
+ EAL_LOG(ERR, "invalid parameters for --"
+ OPT_LOG_COLOR);
+ return -1;
+ }
+ break;
+
case OPT_TRACE_NUM: {
if (eal_trace_args_save(optarg) < 0) {
EAL_LOG(ERR, "invalid parameters for --"
@@ -37,6 +37,8 @@ enum {
OPT_LOG_LEVEL_NUM,
#define OPT_LOG_TIMESTAMP "log-timestamp"
OPT_LOG_TIMESTAMP_NUM,
+#define OPT_LOG_COLOR "log-color"
+ OPT_LOG_COLOR_NUM,
#define OPT_TRACE "trace"
OPT_TRACE_NUM,
#define OPT_TRACE_DIR "trace-dir"
@@ -52,5 +52,10 @@ void rte_eal_log_cleanup(void);
__rte_internal
int eal_log_timestamp(const char *fmt);
+/*
+ * Enable or disable color in log messages
+ */
+__rte_internal
+int eal_log_color(const char *mode);
#endif /* LOG_INTERNAL_H */
@@ -11,6 +11,7 @@
#include <time.h>
#include <unistd.h>
+#include <rte_common.h>
#include <rte_log.h>
#include "log_internal.h"
@@ -24,15 +25,113 @@ enum eal_log_time_format {
EAL_LOG_TIMESTAMP_ISO,
};
+enum eal_log_color {
+ EAL_LOG_COLOR_AUTO = 0, /* default */
+ EAL_LOG_COLOR_NEVER,
+ EAL_LOG_COLOR_ALWAYS,
+};
+
static struct {
+ enum eal_log_color color_mode;
enum eal_log_time_format time_format;
+ bool show_color;
struct timespec start_time;
struct timespec last_time;
struct tm last_tm;
} log = {
+ .color_mode = EAL_LOG_COLOR_AUTO,
.time_format = EAL_LOG_TIMESTAMP_NONE,
};
+enum color {
+ COLOR_NONE,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_YELLOW,
+ COLOR_BLUE,
+ COLOR_MAGENTA,
+ COLOR_CYAN,
+ COLOR_WHITE,
+ COLOR_BOLD,
+ COLOR_CLEAR
+};
+
+static const char * const color_code[] = {
+ [COLOR_NONE] = "",
+ [COLOR_RED] = "\e[31m",
+ [COLOR_GREEN] = "\e[32m",
+ [COLOR_YELLOW] = "\e[33m",
+ [COLOR_BLUE] = "\e[34m",
+ [COLOR_MAGENTA] = "\e[35m",
+ [COLOR_CYAN] = "\e[36m",
+ [COLOR_WHITE] = "\e[37m",
+ [COLOR_BOLD] = "\e[1m",
+ [COLOR_CLEAR] = "\e[0m",
+};
+
+__rte_format_printf(3, 4)
+static int color_fprintf(FILE *out, enum color color, const char *fmt, ...)
+{
+ va_list args;
+ int ret = 0;
+
+ va_start(args, fmt);
+ ret = fprintf(out, "%s", color_code[color]);
+ ret += vfprintf(out, fmt, args);
+ ret += fprintf(out, "%s", color_code[COLOR_CLEAR]);
+
+ return ret;
+}
+
+static ssize_t
+color_log_write(FILE *f, int level, const char *msg, size_t size)
+{
+ size_t i;
+ ssize_t ret = 0;
+
+ /* find first : delimiter in message */
+ for (i = 0; i < size; i++) {
+ if (msg[i] == ':') {
+ ++i; /* put colon in the color */
+ ret = color_fprintf(stderr, COLOR_YELLOW, "%.*s", (int)i, msg);
+ msg += i;
+ size -= i;
+ break;
+ }
+ }
+
+ if (level <= 0 || level >= (int)RTE_LOG_INFO)
+ ret += fprintf(f, "%.*s", (int)size, msg);
+ else if (level >= (int)RTE_LOG_ERR)
+ ret += color_fprintf(f, COLOR_BOLD, "%.*s", (int)size, msg);
+ else
+ ret += color_fprintf(f, COLOR_RED, "%.*s", (int)size, msg);
+
+ return ret;
+}
+
+/*
+ * Controls whether color is enabled:
+ * modes are:
+ * always - enable color output regardless
+ * auto - enable if stderr is a terminal
+ * never - color output is disabled.
+ */
+int
+eal_log_color(const char *mode)
+{
+ if (mode == NULL || strcmp(mode, "always") == 0)
+ log.color_mode = EAL_LOG_COLOR_ALWAYS;
+ else if (strcmp(mode, "never") == 0)
+ log.color_mode = EAL_LOG_COLOR_NEVER;
+ else if (strcmp(mode, "auto") == 0)
+ log.color_mode = EAL_LOG_COLOR_AUTO;
+ else
+ return -1;
+
+ return 0;
+}
+
int
eal_log_timestamp(const char *str)
{
@@ -153,18 +252,28 @@ console_log_timestamp(char *tsbuf, size_t tsbuflen)
static ssize_t
console_log_write(__rte_unused void *c, const char *msg, size_t size)
{
- char buf[128];
+ int level = rte_log_cur_msg_loglevel();
+ char tsbuf[128] = "";
ssize_t ret;
- ret = console_log_timestamp(buf, sizeof(buf));
- if (ret == 0)
- ret = fwrite(msg, 1, size, stderr);
- else
- ret = fprintf(stderr, "[%s] %.*s", buf, (int)size, msg);
+ ret = console_log_timestamp(tsbuf, sizeof(tsbuf));
+ if (ret == 0) {
+ if (log.show_color)
+ ret = color_log_write(stderr, level, msg, size);
+ else
+ ret = fwrite(msg, 1, size, stderr);
+ } else {
+ if (log.show_color) {
+ color_fprintf(stderr, COLOR_GREEN, "[%s] ", tsbuf);
+ ret = color_log_write(stderr, level, msg, size);
+ } else {
+ ret = fprintf(stderr, "[%s] %.*s", tsbuf, (int)size, msg);
+ }
+ }
fflush(stderr);
/* Syslog error levels are from 0 to 7, so subtract 1 to convert */
- syslog(rte_log_cur_msg_loglevel() - 1, "%.*s", (int)size, msg);
+ syslog(level - 1, "%.*s", (int)size, msg);
return ret;
}
@@ -190,13 +299,16 @@ eal_log_init(const char *id, int facility)
{
FILE *log_stream;
- clock_gettime(CLOCK_MONOTONIC, &log.start_time);
- log.last_time = log.start_time;
-
/* skip if user has already setup a log stream */
if (eal_log_get_default())
return 0;
+ log.show_color = (log.color_mode == EAL_LOG_COLOR_ALWAYS) ||
+ (log.color_mode == EAL_LOG_COLOR_AUTO && isatty(STDERR_FILENO));
+
+ clock_gettime(CLOCK_MONOTONIC, &log.start_time);
+ log.last_time = log.start_time;
+
log_stream = fopencookie(NULL, "w+", console_log_func);
if (log_stream == NULL)
return -1;
@@ -12,6 +12,12 @@ eal_log_timestamp(__rte_unused const char *fmt)
return -1; /* not implemented */
}
+int
+eal_log_color(__rte_unused const char *mode)
+{
+ return -1; /* not implemented */
+}
+
/* set the log to default function, called during eal init process. */
int
eal_log_init(__rte_unused const char *id, __rte_unused int facility)
@@ -25,6 +25,7 @@ DPDK_24 {
INTERNAL {
global:
+ eal_log_color;
eal_log_get_default;
eal_log_init;
eal_log_level2str;