@@ -1067,6 +1067,18 @@ test_misc_flags(void)
const char * const argv25[] = {prgname, prefix, mp_flag,
"--log-timestamp=invalid" };
+ /* Try running with --log-color */
+ const char * const argv26[] = {prgname, prefix, mp_flag,
+ "--log-color" };
+
+ /* Try running with --log-color=never */
+ const char * const argv27[] = {prgname, prefix, mp_flag,
+ "--log-color=never" };
+
+ /* Try running with --log-color=invalid */
+ const char * const argv28[] = {prgname, prefix, mp_flag,
+ "--log-color=invalid" };
+
/* run all tests also applicable to FreeBSD first */
@@ -1187,6 +1199,18 @@ test_misc_flags(void)
printf("Error - process did run ok with --log-timestamp=invalid parameter\n");
goto fail;
}
+ if (launch_proc(argv26) != 0) {
+ printf("Error - process did not run ok with --log-color parameter\n");
+ goto fail;
+ }
+ if (launch_proc(argv27) != 0) {
+ printf("Error - process did not run ok with --log-color=none parameter\n");
+ goto fail;
+ }
+ if (launch_proc(argv28) == 0) {
+ printf("Error - process did run ok with --log-timestamp=invalid parameter\n");
+ goto fail;
+ }
rmdir(hugepath_dir3);
@@ -59,6 +59,21 @@ For example::
Within an application, the same result can be got using the ``rte_log_set_level_pattern()`` or ``rte_log_set_level_regex()`` APIs.
+Color output
+~~~~~~~~~~~~
+
+The log library will highlight important messages.
+This is controlled by the ``--log-color`` option.
+he optional argument ``when`` can be ``auto``, ``never``, or ``always``.
+The default setting is ``auto`` which enables color when the output to
+``stderr`` is a terminal.
+If the ``when`` argument is omitted, it defaults to ``always``.
+
+For example to turn off all coloring::
+
+ /path/to/app --log-color=none
+
+
Log timestamp
~~~~~~~~~~~~~
@@ -101,7 +116,6 @@ option. There are three possible settings for this option:
If ``--syslog`` option is not specified, then only console (stderr) will be used.
-
Using Logging APIs to Generate Log Messages
-------------------------------------------
@@ -75,6 +75,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, 2, 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 },
@@ -1618,6 +1619,7 @@ eal_log_level_parse(int argc, char * const argv[])
case OPT_LOG_LEVEL_NUM:
case OPT_SYSLOG_NUM:
case OPT_LOG_TIMESTAMP_NUM:
+ case OPT_LOG_COLOR_NUM:
if (eal_parse_common_option(opt, optarg, internal_conf) < 0)
return -1;
break;
@@ -1862,6 +1864,14 @@ 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;
+
#ifndef RTE_EXEC_ENV_WINDOWS
case OPT_TRACE_NUM: {
if (eal_trace_args_save(optarg) < 0) {
@@ -2229,6 +2239,7 @@ eal_common_usage(void)
" Set specific log level\n"
" --"OPT_LOG_LEVEL"=help Show log types and levels\n"
" --"OPT_LOG_TIMESTAMP"[=<format>] Timestamp log output\n"
+ " --"OPT_LOG_COLOR"[=<when>] Colorize log messages\n"
#ifndef RTE_EXEC_ENV_WINDOWS
" --"OPT_TRACE"=<regex-match>\n"
" Enable trace based on regular expression trace name.\n"
@@ -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"
@@ -23,6 +23,7 @@
#include <sys/un.h>
#endif
+#include <rte_common.h>
#include <rte_log.h>
#include <rte_per_lcore.h>
@@ -49,6 +50,12 @@ enum eal_log_syslog {
EAL_LOG_SYSLOG_BOTH, /* log to both syslog and stderr */
};
+enum eal_log_color {
+ EAL_LOG_COLOR_AUTO = 0, /* default */
+ EAL_LOG_COLOR_NEVER,
+ EAL_LOG_COLOR_ALWAYS,
+};
+
typedef int (*log_print_t)(FILE *f, uint32_t level, const char *fmt, va_list ap);
static int log_print(FILE *f, uint32_t level, const char *format, va_list ap);
@@ -64,6 +71,7 @@ static struct rte_logs {
#endif
log_print_t print_func;
+ enum eal_log_color color_mode;
enum eal_log_time_format time_format;
struct timespec started; /* when log was initialized */
struct timespec previous; /* when last msg was printed */
@@ -715,6 +723,75 @@ format_timestamp(char *tsbuf, size_t tsbuflen)
return 0;
}
+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]);
+ va_end(args);
+
+ return ret;
+}
+
+static ssize_t
+color_log_write(FILE *f, int level, char *msg)
+{
+ char *cp;
+ ssize_t ret = 0;
+
+ /*
+ * use convention that first part of message (up to the ':' character)
+ * is the subsystem id and should be highlighted.
+ */
+ cp = strchr(msg, ':');
+ if (cp) {
+ /* print first part in yellow */
+ ret = color_fprintf(stderr, COLOR_YELLOW, "%.*s",
+ (int)(cp - msg + 1), msg);
+ msg = cp + 1;
+ }
+
+ if (level <= 0 || level >= (int)RTE_LOG_INFO)
+ ret += fprintf(f, "%s", msg);
+ else if (level >= (int)RTE_LOG_ERR)
+ ret += color_fprintf(f, COLOR_BOLD, "%s", msg);
+ else
+ ret += color_fprintf(f, COLOR_RED, "%s", msg);
+
+ return ret;
+}
+
/* default log print function */
__rte_format_printf(3, 0)
static int
@@ -930,15 +1007,84 @@ log_open_syslog(const char *id, bool is_terminal)
}
#endif
+__rte_format_printf(3, 0)
+static int
+color_print(FILE *f, uint32_t level, const char *format, va_list ap)
+{
+ char *buf = NULL;
+ int ret;
+
+ /* need to make temporary buffer for color scan */
+ ret = vasprintf(&buf, format, ap);
+ if (ret > 0) {
+ ret = color_log_write(f, level, buf);
+ free(buf);
+ return ret;
+ }
+
+ /* if vasprintf fails, print without color */
+ return log_print(f, level, format, ap);
+}
+
+__rte_format_printf(3, 0)
+static int
+color_print_with_timestamp(FILE *f, uint32_t level,
+ const char *format, va_list ap)
+{
+ char tsbuf[128];
+
+ if (format_timestamp(tsbuf, sizeof(tsbuf)) > 0)
+ color_fprintf(f, COLOR_GREEN, "[%s] ", tsbuf);
+
+ return color_print(f, level, format, ap);
+}
+
+/*
+ * 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)
+ rte_logs.color_mode = EAL_LOG_COLOR_ALWAYS;
+ else if (strcmp(mode, "never") == 0)
+ rte_logs.color_mode = EAL_LOG_COLOR_NEVER;
+ else if (strcmp(mode, "auto") == 0)
+ rte_logs.color_mode = EAL_LOG_COLOR_AUTO;
+ else
+ return -1;
+
+ return 0;
+}
+
+static inline bool
+use_color(bool is_terminal)
+{
+ switch (rte_logs.color_mode) {
+ default:
+ case EAL_LOG_COLOR_NEVER:
+ return false;
+ case EAL_LOG_COLOR_ALWAYS:
+ return true;
+ case EAL_LOG_COLOR_AUTO:
+ return is_terminal;
+ }
+
+}
+
/* Choose how log output is directed */
static void
log_output_selection(const char *id)
{
+ bool is_terminal = isatty(STDERR_FILENO);
+
#ifdef RTE_EXEC_ENV_WINDOWS
RTE_SET_USED(id);
#else
- bool is_terminal = isatty(STDERR_FILENO);
-
/* If stderr is redirected to systemd journal then upgrade */
if (!is_terminal && is_journal(STDERR_FILENO)) {
int jfd = open_journal(id);
@@ -957,8 +1103,19 @@ log_output_selection(const char *id)
return;
}
#endif
- if (rte_logs.time_format != EAL_LOG_TIMESTAMP_NONE)
- rte_logs.print_func = log_print_with_timestamp;
+
+ if (use_color(is_terminal)) {
+ if (rte_logs.time_format == EAL_LOG_TIMESTAMP_NONE)
+ rte_logs.print_func = color_print;
+ else
+ rte_logs.print_func = color_print_with_timestamp;
+ } else {
+ if (rte_logs.time_format == EAL_LOG_TIMESTAMP_NONE)
+ rte_logs.print_func = log_print;
+ else
+ rte_logs.print_func = log_print_with_timestamp;
+ }
+
}
/*
@@ -47,5 +47,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 */
@@ -25,6 +25,7 @@ DPDK_24 {
INTERNAL {
global:
+ eal_log_color;
eal_log_init;
eal_log_level2str;
eal_log_save_pattern;