From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id DC3F945BC3; Fri, 25 Oct 2024 17:49:21 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 9EF7A40264; Fri, 25 Oct 2024 17:49:21 +0200 (CEST) Received: from mail-ot1-f54.google.com (mail-ot1-f54.google.com [209.85.210.54]) by mails.dpdk.org (Postfix) with ESMTP id 5B8F14021F for ; Fri, 25 Oct 2024 17:49:20 +0200 (CEST) Received: by mail-ot1-f54.google.com with SMTP id 46e09a7af769-71808e95ae2so1332497a34.0 for ; Fri, 25 Oct 2024 08:49:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=weka.io; s=google; t=1729871359; x=1730476159; darn=dpdk.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=cm5bVnVqozUbZN2zfXp3yTaR1+Z9uo2A35aaT+VTPEE=; b=SliffwP7MlRWC4hBtigag1tHVEsj5+dgU3gdOAt54ake3AV3CyC5u6+UBcSobzWsnE auJeDM+XFa3gZkCG8gqaZodoE92vUq6w5FzL197mxoNKWjNrp2V95KcMX6tb/UbDiy9n AYD0RqRRDGfSO1Gm5fYTJvgizL4Ku37F7eADs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729871359; x=1730476159; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=cm5bVnVqozUbZN2zfXp3yTaR1+Z9uo2A35aaT+VTPEE=; b=B3NWyG+IhF7WLUCf/40RG4mqgsytEmruw2akUetL6mh553VYvSwtzSfK1IWu90cdQ8 00M0/nvG1ksLldxGZ/BnptkZIbj2dQztQpcGQoR7FLIZ6XCTyVYahq7QkKuI4S6VDtJu 1+hQSiVL3Ikr3GUEElRfImZgLvmapbHnjgpvWuonrKttZ89p1PI5eRYUYAgmCHxqDSgi pUY8eRlfhmj9H/Vci8R42WVAZgRmPQfEbIWWZzJKwCRz4HQ8h18f4dEsIQKco1Uh2m/I 2v3ZBDTgoPOoJ1Q28aT285cStVZGl67zZOqtTSOy4vlNsYZQkvOX2EUvkyCn8cZb+gOQ 1zow== X-Gm-Message-State: AOJu0YzItU2JIgPTsV+DXls9OhpQJmoGy1bwtFhm1/MPj6fxbv+fwzwE krzzqMGxcrsDcs80r9YhOPNWP8l1UBc7f0F7UhJzybzgX9GtAk0FVj7kCGcRPlIza4TY2UgfOFe 5mZLWUvu9Sxfg0YBeniV592sLOoM2v1y6XedSCuWS/GVbmwFevX/WlvvNHTbFtuILvEMOyq0+Yl KaBuSCOfY6yzjTRCik3aIMlFA+z0kdW5wU/G/am/NVqPIA6Azen+HneHsMbf2GnzDbLzvzT8RTA Q== X-Google-Smtp-Source: AGHT+IHOEyo4B9JVBHJUjjH5qdG/wtoXpcbEJWebaKiEnvxuqj1SylaK3fnn5AmF19KzBgEzgR9X8HS0n8wCxKpyYlQ= X-Received: by 2002:a05:6870:5594:b0:288:a38c:fe7d with SMTP id 586e51a60fabf-28cebca3e9cmr2879215fac.23.1729871359223; Fri, 25 Oct 2024 08:49:19 -0700 (PDT) MIME-Version: 1.0 References: <20200814173441.23086-1-stephen@networkplumber.org> <20241024032026.415122-1-stephen@networkplumber.org> <20241024032026.415122-14-stephen@networkplumber.org> In-Reply-To: <20241024032026.415122-14-stephen@networkplumber.org> From: Baruch Even Date: Fri, 25 Oct 2024 18:49:08 +0300 Message-ID: Subject: Re: [PATCH v27 13/14] log: colorize log output To: Stephen Hemminger Cc: dev@dpdk.org, =?UTF-8?Q?Morten_Br=C3=B8rup?= , Bruce Richardson , Chengwen Feng , Tyler Retzlaff Content-Type: multipart/alternative; boundary="000000000000dfccf206254f0f43" X-CLOUD-SEC-AV-Sent: true X-CLOUD-SEC-AV-Info: weka,google_mail,monitor X-Gm-Spam: 0 X-Gm-Phishy: 0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org --000000000000dfccf206254f0f43 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable The code has some mentions of `--log-color=3Dnone` but only the `never` option is parsed. On Thu, Oct 24, 2024 at 6:22=E2=80=AFAM Stephen Hemminger < stephen@networkplumber.org> wrote: > Like dmesg, colorize the log output (unless redirected to file). > Timestamp is green, the subsystem is in yellow and the message > is red if urgent, boldface if an error, and normal for info and > debug messages. > > The default is to not use color since it may disturb > automatic tests and other embedded usage. > > Signed-off-by: Stephen Hemminger > Acked-by: Morten Br=C3=B8rup > Acked-by: Bruce Richardson > Acked-by: Chengwen Feng > --- > app/test/test_eal_flags.c | 24 ++++ > doc/guides/prog_guide/log_lib.rst | 24 ++++ > lib/eal/common/eal_common_options.c | 11 ++ > lib/eal/common/eal_options.h | 2 + > lib/log/log.c | 20 ++- > lib/log/log_color.c | 216 ++++++++++++++++++++++++++++ > lib/log/log_internal.h | 5 + > lib/log/log_private.h | 8 ++ > lib/log/meson.build | 1 + > lib/log/version.map | 1 + > 10 files changed, 308 insertions(+), 4 deletions(-) > create mode 100644 lib/log/log_color.c > > diff --git a/app/test/test_eal_flags.c b/app/test/test_eal_flags.c > index e24630edde..cd73711da6 100644 > --- a/app/test/test_eal_flags.c > +++ b/app/test/test_eal_flags.c > @@ -1067,6 +1067,18 @@ test_misc_flags(void) > const char * const argv25[] =3D {prgname, prefix, mp_flag, > "--log-timestamp=3Dinvalid" }; > > + /* Try running with --log-color */ > + const char * const argv26[] =3D {prgname, prefix, mp_flag, > + "--log-color" }; > + > + /* Try running with --log-color=3Dnever */ > + const char * const argv27[] =3D {prgname, prefix, mp_flag, > + "--log-color=3Dnever" }; > + > + /* Try running with --log-color=3Dinvalid */ > + const char * const argv28[] =3D {prgname, prefix, mp_flag, > + "--log-color=3Dinvalid" }; > + > > /* 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=3Dinvalid parameter\n"); > goto fail; > } > + if (launch_proc(argv26) !=3D 0) { > + printf("Error - process did not run ok with --log-color > parameter\n"); > + goto fail; > + } > + if (launch_proc(argv27) !=3D 0) { > + printf("Error - process did not run ok with > --log-color=3Dnone parameter\n"); > + goto fail; > + } > + if (launch_proc(argv28) =3D=3D 0) { > + printf("Error - process did run ok with > --log-timestamp=3Dinvalid parameter\n"); > + goto fail; > + } > > > rmdir(hugepath_dir3); > diff --git a/doc/guides/prog_guide/log_lib.rst > b/doc/guides/prog_guide/log_lib.rst > index 82d3b7be2b..a4f880037b 100644 > --- a/doc/guides/prog_guide/log_lib.rst > +++ b/doc/guides/prog_guide/log_lib.rst > @@ -57,6 +57,7 @@ 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. > > + > Using Logging APIs to Generate Log Messages > ------------------------------------------- > > @@ -137,3 +138,26 @@ To prefix all console messages with ISO format time > the syntax is:: > > Timestamp option has no effect if using syslog because the ``syslog()= `` > service already does timestamping internally. > + > + > +Color output > +~~~~~~~~~~~~ > + > +The log library will highlight important messages. > +This is controlled by the ``--log-color`` option. > +The optional argument describes when color is enabled: > + > +:never: Do not enable color. This is the default behavior. > + > +:auto: Enable color only when printing to a terminal. > + This is the same as ``--log-color`` with no argument > + > +:always: Always print color > + > +For example to enable color in logs if using terminal:: > + > + /path/to/app --log-color > + > +.. note:: > + > + Color output is never used for syslog or systemd journal logging. > diff --git a/lib/eal/common/eal_common_options.c > b/lib/eal/common/eal_common_options.c > index a70d63479a..6965666a7c 100644 > --- a/lib/eal/common/eal_common_options.c > +++ b/lib/eal/common/eal_common_options.c > @@ -73,6 +73,7 @@ eal_long_options[] =3D { > {OPT_HUGE_UNLINK, 2, NULL, OPT_HUGE_UNLINK_NUM }, > {OPT_IOVA_MODE, 1, NULL, OPT_IOVA_MODE_NUM }, > {OPT_LCORES, 1, NULL, OPT_LCORES_NUM }, > + {OPT_LOG_COLOR, 2, NULL, OPT_LOG_COLOR_NUM }, > {OPT_LOG_LEVEL, 1, NULL, OPT_LOG_LEVEL_NUM }, > {OPT_LOG_TIMESTAMP, 2, NULL, OPT_LOG_TIMESTAMP_NUM }, > {OPT_TRACE, 1, NULL, OPT_TRACE_NUM }, > @@ -1620,6 +1621,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; > @@ -1859,6 +1861,14 @@ eal_parse_common_option(int opt, const char *optar= g, > } > 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) { > @@ -2225,6 +2235,7 @@ eal_common_usage(void) > " Set specific log level\n" > " --"OPT_LOG_LEVEL"=3Dhelp Show log types and levels\n= " > " --"OPT_LOG_TIMESTAMP"[=3D] Timestamp log outpu= t\n" > + " --"OPT_LOG_COLOR"[=3D] Colorize log messages\n" > #ifndef RTE_EXEC_ENV_WINDOWS > " --"OPT_TRACE"=3D\n" > " Enable trace based on regular > expression trace name.\n" > diff --git a/lib/eal/common/eal_options.h b/lib/eal/common/eal_options.h > index e24c9eca53..87c3c32f86 100644 > --- a/lib/eal/common/eal_options.h > +++ b/lib/eal/common/eal_options.h > @@ -33,6 +33,8 @@ enum { > OPT_HUGE_UNLINK_NUM, > #define OPT_LCORES "lcores" > OPT_LCORES_NUM, > +#define OPT_LOG_COLOR "log-color" > + OPT_LOG_COLOR_NUM, > #define OPT_LOG_LEVEL "log-level" > OPT_LOG_LEVEL_NUM, > #define OPT_LOG_TIMESTAMP "log-timestamp" > diff --git a/lib/log/log.c b/lib/log/log.c > index 343f9d77b7..f1bd408bee 100644 > --- a/lib/log/log.c > +++ b/lib/log/log.c > @@ -515,10 +515,22 @@ eal_log_init(const char *id) > > if (logf) > rte_openlog_stream(logf); > - else if (log_timestamp_enabled()) > - rte_logs.print_func =3D log_print_with_timestamp; > - else > - rte_logs.print_func =3D vfprintf; > + else { > + bool is_terminal =3D isatty(fileno(stderr)); > + bool use_color =3D log_color_enabled(is_terminal); > + > + if (log_timestamp_enabled()) { > + if (use_color) > + rte_logs.print_func =3D > color_print_with_timestamp; > + else > + rte_logs.print_func =3D > log_print_with_timestamp; > + } else { > + if (use_color) > + rte_logs.print_func =3D color_print; > + else > + rte_logs.print_func =3D vfprintf; > + } > + } > > #if RTE_LOG_DP_LEVEL >=3D RTE_LOG_DEBUG > RTE_LOG(NOTICE, EAL, > diff --git a/lib/log/log_color.c b/lib/log/log_color.c > new file mode 100644 > index 0000000000..82a9ac8022 > --- /dev/null > +++ b/lib/log/log_color.c > @@ -0,0 +1,216 @@ > +/* SPDX-License-Identifier: BSD-3-Clause */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#ifdef RTE_EXEC_ENV_WINDOWS > +#include > +#endif > + > +#include "log_internal.h" > +#include "log_private.h" > + > +enum { > + LOG_COLOR_AUTO =3D 0, > + LOG_COLOR_NEVER, > + LOG_COLOR_ALWAYS, > +} log_color_mode =3D LOG_COLOR_NEVER; > + > +enum color { > + COLOR_NONE, > + COLOR_RED, > + COLOR_GREEN, > + COLOR_YELLOW, > + COLOR_BLUE, > + COLOR_MAGENTA, > + COLOR_CYAN, > + COLOR_WHITE, > + COLOR_BOLD, > + COLOR_CLEAR, > +}; > + > +enum log_field { > + LOG_FIELD_SUBSYS, > + LOG_FIELD_TIME, > + LOG_FIELD_ALERT, > + LOG_FIELD_ERROR, > + LOG_FIELD_INFO, > +}; > + > +static const enum color field_colors[] =3D { > + [LOG_FIELD_SUBSYS] =3D COLOR_YELLOW, > + [LOG_FIELD_TIME] =3D COLOR_GREEN, > + [LOG_FIELD_ALERT] =3D COLOR_RED, > + [LOG_FIELD_ERROR] =3D COLOR_BOLD, > + [LOG_FIELD_INFO] =3D COLOR_NONE, > +}; > + > +/* If set all colors are bolder */ > +static bool dark_mode; > + > +/* Standard terminal escape codes for colors and bold */ > +static const uint8_t color_esc_code[] =3D { > + [COLOR_RED] =3D 31, > + [COLOR_GREEN] =3D 32, > + [COLOR_YELLOW] =3D 33, > + [COLOR_BLUE] =3D 34, > + [COLOR_MAGENTA] =3D 35, > + [COLOR_CYAN] =3D 36, > + [COLOR_WHITE] =3D 37, > + [COLOR_BOLD] =3D 1, > +}; > + > +__rte_format_printf(4, 5) > +static int > +color_snprintf(char *buf, size_t len, enum log_field field, > + const char *fmt, ...) > +{ > + enum color color =3D field_colors[field]; > + uint8_t esc =3D color_esc_code[color]; > + va_list args; > + int ret =3D 0; > + > + va_start(args, fmt); > + if (esc =3D=3D 0) { > + ret =3D vsnprintf(buf, len, fmt, args); > + } else { > + ret =3D snprintf(buf, len, > + dark_mode ? "\e[1;%um" : "\e[%um", esc); > + ret +=3D vsnprintf(buf + ret, len - ret, fmt, args); > + ret +=3D snprintf(buf + ret, len - ret, "%s", "\e[0m"); > + } > + va_end(args); > + > + 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 =3D=3D NULL || strcmp(mode, "always") =3D=3D 0) > + log_color_mode =3D LOG_COLOR_ALWAYS; > + else if (strcmp(mode, "never") =3D=3D 0) > + log_color_mode =3D LOG_COLOR_NEVER; > + else if (strcmp(mode, "auto") =3D=3D 0) > + log_color_mode =3D LOG_COLOR_AUTO; > + else > + return -1; > + > + return 0; > +} > + > +bool > +log_color_enabled(bool is_terminal) > +{ > + char *env, *sep; > + > + /* Set dark mode using the defacto heuristics used by other > programs */ > + env =3D getenv("COLORFGBG"); > + if (env) { > + sep =3D strrchr(env, ';'); > + if (sep && > + ((sep[1] >=3D '0' && sep[1] <=3D '6') || sep[1] =3D= =3D '8') && > + sep[2] =3D=3D '\0') > + dark_mode =3D true; > + } > + > + switch (log_color_mode) { > + case LOG_COLOR_ALWAYS: > + return true; > + case LOG_COLOR_AUTO: > + return is_terminal; > + default: > + return false; > + > + } > +} > + > +/* Look ast the current message level to determine color of field */ > +static enum log_field > +color_msg_field(void) > +{ > + const int level =3D rte_log_cur_msg_loglevel(); > + > + if (level <=3D 0 || level >=3D (int)RTE_LOG_INFO) > + return LOG_FIELD_INFO; > + else if (level >=3D (int)RTE_LOG_ERR) > + return LOG_FIELD_ERROR; > + else > + return LOG_FIELD_ALERT; > +} > + > +__rte_format_printf(3, 0) > +static int > +color_fmt_msg(char *out, size_t len, const char *format, va_list ap) > +{ > + enum log_field field =3D color_msg_field(); > + char buf[LINE_MAX]; > + int ret =3D 0; > + > + /* format raw message */ > + vsnprintf(buf, sizeof(buf), format, ap); > + const char *msg =3D buf; > + > + /* > + * use convention that first part of message (up to the ':' > character) > + * is the subsystem id and should be highlighted. > + */ > + const char *cp =3D strchr(msg, ':'); > + if (cp) { > + /* print first part in yellow */ > + ret =3D color_snprintf(out, len, LOG_FIELD_SUBSYS, > + "%.*s", (int)(cp - msg + 1), msg); > + msg =3D cp + 1; > + } > + > + ret +=3D color_snprintf(out + ret, len - ret, field, "%s", msg); > + return ret; > +} > + > +__rte_format_printf(2, 0) > +int > +color_print(FILE *f, const char *format, va_list ap) > +{ > + char out[LINE_MAX]; > + > + /* format raw message */ > + int ret =3D color_fmt_msg(out, sizeof(out), format, ap); > + if (fputs(out, f) < 0) > + return -1; > + > + return ret; > +} > + > +__rte_format_printf(2, 0) > +int > +color_print_with_timestamp(FILE *f, const char *format, va_list ap) > +{ > + char out[LINE_MAX]; > + char tsbuf[128]; > + int ret =3D 0; > + > + if (log_timestamp(tsbuf, sizeof(tsbuf)) > 0) > + ret =3D color_snprintf(out, sizeof(out), > + LOG_FIELD_TIME, "[%s] ", tsbuf); > + > + ret +=3D color_fmt_msg(out + ret, sizeof(out) - ret, format, ap); > + if (fputs(out, f) < 0) > + return -1; > + > + return ret; > +} > diff --git a/lib/log/log_internal.h b/lib/log/log_internal.h > index 82fdc21ac2..bba7041ea3 100644 > --- a/lib/log/log_internal.h > +++ b/lib/log/log_internal.h > @@ -50,5 +50,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 */ > diff --git a/lib/log/log_private.h b/lib/log/log_private.h > index 625f57c0cc..42babbcac1 100644 > --- a/lib/log/log_private.h > +++ b/lib/log/log_private.h > @@ -25,4 +25,12 @@ ssize_t log_timestamp(char *tsbuf, size_t tsbuflen); > __rte_format_printf(2, 0) > int log_print_with_timestamp(FILE *f, const char *format, va_list ap); > > +bool log_color_enabled(bool is_tty); > + > +__rte_format_printf(2, 0) > +int color_print(FILE *f, const char *format, va_list ap); > + > +__rte_format_printf(2, 0) > +int color_print_with_timestamp(FILE *f, const char *format, va_list ap); > + > #endif /* LOG_PRIVATE_H */ > diff --git a/lib/log/meson.build b/lib/log/meson.build > index 86e4452b19..b3de57b9c7 100644 > --- a/lib/log/meson.build > +++ b/lib/log/meson.build > @@ -4,6 +4,7 @@ > includes +=3D global_inc > sources =3D files( > 'log.c', > + 'log_color.c', > 'log_timestamp.c', > ) > > diff --git a/lib/log/version.map b/lib/log/version.map > index 800d3943bc..09d8a4289b 100644 > --- a/lib/log/version.map > +++ b/lib/log/version.map > @@ -25,6 +25,7 @@ DPDK_25 { > INTERNAL { > global: > > + eal_log_color; > eal_log_init; > eal_log_journal; # WINDOWS_NO_EXPORT > eal_log_level2str; > -- > 2.45.2 > > --=20 Baruch Even Platform Technical Lead at WEKA E baruch@weka.io* =C2=AD*W https://www.weka.io* =C2=AD* [image: App Banner Image] --000000000000dfccf206254f0f43 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
The code has some mentions of `--log-color=3Dnone` but onl= y the `never` option is parsed.

On Thu, Oct 24, 2024 at 6:22=E2=80=AFAM = Stephen Hemminger <stephen= @networkplumber.org> wrote:
Like dmesg, colorize the log output (unless redirected t= o file).
Timestamp is green, the subsystem is in yellow and the message
is red if urgent, boldface if an error, and normal for info and
debug messages.

The default is to not use color since it may disturb
automatic tests and other embedded usage.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Morten Br=C3=B8rup <mb@smartsharesystems.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
Acked-by: Chengwen Feng <fengchengwen@huawei.com>
---
=C2=A0app/test/test_eal_flags.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|= =C2=A0 24 ++++
=C2=A0doc/guides/prog_guide/log_lib.rst=C2=A0 =C2=A0|=C2=A0 24 ++++
=C2=A0lib/eal/common/eal_common_options.c |=C2=A0 11 ++
=C2=A0lib/eal/common/eal_options.h=C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 =C2= =A02 +
=C2=A0lib/log/log.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 20 ++-
=C2=A0lib/log/log_color.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0| 216 ++++++++++++++++++++++++++++
=C2=A0lib/log/log_internal.h=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 |=C2=A0 =C2=A05 +
=C2=A0lib/log/log_private.h=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0|=C2=A0 =C2=A08 ++
=C2=A0lib/log/meson.build=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0|=C2=A0 =C2=A01 +
=C2=A0lib/log/version.map=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0|=C2=A0 =C2=A01 +
=C2=A010 files changed, 308 insertions(+), 4 deletions(-)
=C2=A0create mode 100644 lib/log/log_color.c

diff --git a/app/test/test_eal_flags.c b/app/test/test_eal_flags.c
index e24630edde..cd73711da6 100644
--- a/app/test/test_eal_flags.c
+++ b/app/test/test_eal_flags.c
@@ -1067,6 +1067,18 @@ test_misc_flags(void)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 const char * const argv25[] =3D {prgname, prefi= x, mp_flag,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"--l= og-timestamp=3Dinvalid" };

+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Try running with --log-color */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0const char * const argv26[] =3D {prgname, prefi= x, mp_flag,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "--log-col= or" };
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Try running with --log-color=3Dnever */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0const char * const argv27[] =3D {prgname, prefi= x, mp_flag,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "--log-col= or=3Dnever" };
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Try running with --log-color=3Dinvalid */ +=C2=A0 =C2=A0 =C2=A0 =C2=A0const char * const argv28[] =3D {prgname, prefi= x, mp_flag,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "--log-col= or=3Dinvalid" };
+

=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* run all tests also applicable to FreeBSD fir= st */

@@ -1187,6 +1199,18 @@ test_misc_flags(void)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 printf("Error = - process did run ok with --log-timestamp=3Dinvalid parameter\n");
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto fail;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (launch_proc(argv26) !=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("Error = - process did not run ok with --log-color parameter\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto fail;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (launch_proc(argv27) !=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("Error = - process did not run ok with --log-color=3Dnone parameter\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto fail;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (launch_proc(argv28) =3D=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("Error = - process did run ok with --log-timestamp=3Dinvalid parameter\n");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto fail;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}


=C2=A0 =C2=A0 =C2=A0 =C2=A0 rmdir(hugepath_dir3);
diff --git a/doc/guides/prog_guide/log_lib.rst b/doc/guides/prog_guide/log_= lib.rst
index 82d3b7be2b..a4f880037b 100644
--- a/doc/guides/prog_guide/log_lib.rst
+++ b/doc/guides/prog_guide/log_lib.rst
@@ -57,6 +57,7 @@ For example::

=C2=A0Within an application, the same result can be got using the ``rte_log= _set_level_pattern()`` or ``rte_log_set_level_regex()`` APIs.

+
=C2=A0Using Logging APIs to Generate Log Messages
=C2=A0-------------------------------------------

@@ -137,3 +138,26 @@ To prefix all console messages with ISO format time th= e syntax is::

=C2=A0 =C2=A0 Timestamp option has no effect if using syslog because the ``= syslog()``
=C2=A0 =C2=A0 service already does timestamping internally.
+
+
+Color output
+~~~~~~~~~~~~
+
+The log library will highlight important messages.
+This is controlled by the ``--log-color`` option.
+The optional argument describes when color is enabled:
+
+:never: Do not enable color. This is the default behavior.
+
+:auto: Enable color only when printing to a terminal.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0This is the same as ``--log-color`` with no arg= ument
+
+:always: Always print color
+
+For example to enable color in logs if using terminal::
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/path/to/app --log-color
+
+.. note::
+
+=C2=A0 =C2=A0Color output is never used for syslog or systemd journal logg= ing.
diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_commo= n_options.c
index a70d63479a..6965666a7c 100644
--- a/lib/eal/common/eal_common_options.c
+++ b/lib/eal/common/eal_common_options.c
@@ -73,6 +73,7 @@ eal_long_options[] =3D {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {OPT_HUGE_UNLINK,=C2=A0 =C2=A0 =C2=A0 =C2=A02, = NULL, OPT_HUGE_UNLINK_NUM=C2=A0 =C2=A0 =C2=A0 },
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {OPT_IOVA_MODE,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A01, NULL, OPT_IOVA_MODE_NUM=C2=A0 =C2=A0 =C2=A0 =C2=A0 },
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {OPT_LCORES,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 1, NULL, OPT_LCORES_NUM=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}, +=C2=A0 =C2=A0 =C2=A0 =C2=A0{OPT_LOG_COLOR,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A02, NULL, OPT_LOG_COLOR_NUM=C2=A0 =C2=A0 =C2=A0 =C2=A0 },
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {OPT_LOG_LEVEL,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A01, NULL, OPT_LOG_LEVEL_NUM=C2=A0 =C2=A0 =C2=A0 =C2=A0 },
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {OPT_LOG_TIMESTAMP,=C2=A0 =C2=A0 =C2=A02, NULL,= OPT_LOG_TIMESTAMP_NUM=C2=A0 =C2=A0 },
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {OPT_TRACE,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A01, NULL, OPT_TRACE_NUM=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 },
@@ -1620,6 +1621,7 @@ eal_log_level_parse(int argc, char * const argv[]) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case OPT_LOG_LEVEL_= NUM:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case OPT_SYSLOG_NUM= :
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case OPT_LOG_TIMEST= AMP_NUM:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case OPT_LOG_COLOR_= NUM:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 if (eal_parse_common_option(opt, optarg, internal_conf) < 0)<= br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -1;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 break;
@@ -1859,6 +1861,14 @@ eal_parse_common_option(int opt, const char *optarg,=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;

+=C2=A0 =C2=A0 =C2=A0 =C2=A0case OPT_LOG_COLOR_NUM:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (eal_log_color(o= ptarg) < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0EAL_LOG(ERR, "invalid parameters for --"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0OPT_LOG_COLOR);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0return -1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
+
=C2=A0#ifndef RTE_EXEC_ENV_WINDOWS
=C2=A0 =C2=A0 =C2=A0 =C2=A0 case OPT_TRACE_NUM: {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (eal_trace_args_= save(optarg) < 0) {
@@ -2225,6 +2235,7 @@ eal_common_usage(void)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Set specific= log level\n"
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"=C2=A0 --"= ;OPT_LOG_LEVEL"=3Dhelp=C2=A0 =C2=A0 Show log types and levels\n"<= br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"=C2=A0 --"= ;OPT_LOG_TIMESTAMP"[=3D<format>]=C2=A0 Timestamp log output\n&qu= ot;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "=C2=A0 --"OPT_= LOG_COLOR"[=3D<when>] Colorize log messages\n"
=C2=A0#ifndef RTE_EXEC_ENV_WINDOWS
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"=C2=A0 --"= ;OPT_TRACE"=3D<regex-match>\n"
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Enable trace= based on regular expression trace name.\n"
diff --git a/lib/eal/common/eal_options.h b/lib/eal/common/eal_options.h index e24c9eca53..87c3c32f86 100644
--- a/lib/eal/common/eal_options.h
+++ b/lib/eal/common/eal_options.h
@@ -33,6 +33,8 @@ enum {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 OPT_HUGE_UNLINK_NUM,
=C2=A0#define OPT_LCORES=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "lco= res"
=C2=A0 =C2=A0 =C2=A0 =C2=A0 OPT_LCORES_NUM,
+#define OPT_LOG_COLOR=C2=A0 =C2=A0 =C2=A0 =C2=A0 "log-color"
+=C2=A0 =C2=A0 =C2=A0 =C2=A0OPT_LOG_COLOR_NUM,
=C2=A0#define OPT_LOG_LEVEL=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"log-leve= l"
=C2=A0 =C2=A0 =C2=A0 =C2=A0 OPT_LOG_LEVEL_NUM,
=C2=A0#define OPT_LOG_TIMESTAMP=C2=A0 =C2=A0 =C2=A0"log-timestamp"= ;
diff --git a/lib/log/log.c b/lib/log/log.c
index 343f9d77b7..f1bd408bee 100644
--- a/lib/log/log.c
+++ b/lib/log/log.c
@@ -515,10 +515,22 @@ eal_log_init(const char *id)

=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (logf)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rte_openlog_stream(= logf);
-=C2=A0 =C2=A0 =C2=A0 =C2=A0else if (log_timestamp_enabled())
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_logs.print_func= =3D log_print_with_timestamp;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0else
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_logs.print_func= =3D vfprintf;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0bool is_terminal = =3D isatty(fileno(stderr));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0bool use_color =3D = log_color_enabled(is_terminal);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (log_timestamp_e= nabled()) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0if (use_color)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_logs.print_func =3D color_print_w= ith_timestamp;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0else
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_logs.print_func =3D log_print_wit= h_timestamp;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0if (use_color)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_logs.print_func =3D color_print;<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0else
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0rte_logs.print_func =3D vfprintf;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}

=C2=A0#if RTE_LOG_DP_LEVEL >=3D RTE_LOG_DEBUG
=C2=A0 =C2=A0 =C2=A0 =C2=A0 RTE_LOG(NOTICE, EAL,
diff --git a/lib/log/log_color.c b/lib/log/log_color.c
new file mode 100644
index 0000000000..82a9ac8022
--- /dev/null
+++ b/lib/log/log_color.c
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_log.h>
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+#include <rte_os_shim.h>
+#endif
+
+#include "log_internal.h"
+#include "log_private.h"
+
+enum=C2=A0 {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0LOG_COLOR_AUTO =3D 0,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0LOG_COLOR_NEVER,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0LOG_COLOR_ALWAYS,
+} log_color_mode =3D LOG_COLOR_NEVER;
+
+enum color {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_NONE,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_RED,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_GREEN,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_YELLOW,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_BLUE,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_MAGENTA,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_CYAN,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_WHITE,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_BOLD,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0COLOR_CLEAR,
+};
+
+enum log_field {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0LOG_FIELD_SUBSYS,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0LOG_FIELD_TIME,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0LOG_FIELD_ALERT,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0LOG_FIELD_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0LOG_FIELD_INFO,
+};
+
+static const enum color field_colors[] =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[LOG_FIELD_SUBSYS] =3D COLOR_YELLOW,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[LOG_FIELD_TIME]=C2=A0 =C2=A0=3D COLOR_GREEN, +=C2=A0 =C2=A0 =C2=A0 =C2=A0[LOG_FIELD_ALERT]=C2=A0 =3D COLOR_RED,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[LOG_FIELD_ERROR]=C2=A0 =3D COLOR_BOLD,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[LOG_FIELD_INFO]=C2=A0 =C2=A0=3D COLOR_NONE, +};
+
+/* If set all colors are bolder */
+static bool dark_mode;
+
+/* Standard terminal escape codes for colors and bold */
+static const uint8_t color_esc_code[] =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[COLOR_RED]=C2=A0 =C2=A0 =C2=A0=3D 31,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[COLOR_GREEN]=C2=A0 =C2=A0=3D 32,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[COLOR_YELLOW]=C2=A0 =3D 33,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[COLOR_BLUE]=C2=A0 =C2=A0 =3D 34,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[COLOR_MAGENTA] =3D 35,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[COLOR_CYAN]=C2=A0 =C2=A0 =3D 36,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[COLOR_WHITE]=C2=A0 =C2=A0=3D 37,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0[COLOR_BOLD]=C2=A0 =C2=A0 =3D 1,
+};
+
+__rte_format_printf(4, 5)
+static int
+color_snprintf(char *buf, size_t len, enum log_field field,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 const char *fmt, ...)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0enum color color =3D field_colors[field];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0uint8_t esc =3D color_esc_code[color];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0va_list args;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0int ret =3D 0;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0va_start(args, fmt);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (esc =3D=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D vsnprintf(b= uf, len, fmt, args);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0} else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D snprintf(bu= f, len,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dark_mode ? "\e[1;%um" : "\e= [%um", esc);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret +=3D vsnprintf(= buf + ret, len - ret, fmt, args);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret +=3D snprintf(b= uf + ret,=C2=A0 len - ret, "%s", "\e[0m");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0va_end(args);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return ret;
+}
+
+/*
+ * Controls whether color is enabled:
+ * modes are:
+ *=C2=A0 =C2=A0always - enable color output regardless
+ *=C2=A0 =C2=A0auto - enable if stderr is a terminal
+ *=C2=A0 =C2=A0never - color output is disabled.
+ */
+int
+eal_log_color(const char *mode)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (mode =3D=3D NULL || strcmp(mode, "alwa= ys") =3D=3D 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0log_color_mode =3D = LOG_COLOR_ALWAYS;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0else if (strcmp(mode, "never") =3D=3D= 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0log_color_mode =3D = LOG_COLOR_NEVER;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0else if (strcmp(mode, "auto") =3D=3D = 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0log_color_mode =3D = LOG_COLOR_AUTO;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0else
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
+}
+
+bool
+log_color_enabled(bool is_terminal)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0char *env, *sep;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* Set dark mode using the defacto heuristics u= sed by other programs */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0env =3D getenv("COLORFGBG");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (env) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sep =3D strrchr(env= , ';');
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (sep &&<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0((sep= [1] >=3D '0' && sep[1] <=3D '6') || sep[1] = =3D=3D '8') &&
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sep[2= ] =3D=3D '\0')
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0dark_mode =3D true;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0switch (log_color_mode) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0case LOG_COLOR_ALWAYS:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return true;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0case LOG_COLOR_AUTO:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return is_terminal;=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return false;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+}
+
+/* Look ast the current message level to determine color of field */
+static enum log_field
+color_msg_field(void)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0const int level =3D rte_log_cur_msg_loglevel();=
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (level <=3D 0 || level >=3D (int)RTE_L= OG_INFO)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return LOG_FIELD_IN= FO;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0else if (level >=3D (int)RTE_LOG_ERR)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return LOG_FIELD_ER= ROR;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0else
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return LOG_FIELD_AL= ERT;
+}
+
+__rte_format_printf(3, 0)
+static int
+color_fmt_msg(char *out, size_t len, const char *format, va_list ap)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0enum log_field field =3D color_msg_field();
+=C2=A0 =C2=A0 =C2=A0 =C2=A0char buf[LINE_MAX];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0int ret =3D 0;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* format raw message */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0vsnprintf(buf, sizeof(buf), format, ap);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0const char *msg =3D buf;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 * use convention that first part of message (u= p to the ':' character)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 * is the subsystem id and should be highlighte= d.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0const char *cp =3D strchr(msg, ':'); +=C2=A0 =C2=A0 =C2=A0 =C2=A0if (cp) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* print first part= in yellow */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D color_snpri= ntf(out, len, LOG_FIELD_SUBSYS,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "%.*s", (int= )(cp - msg + 1), msg);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0msg =3D cp + 1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0ret +=3D color_snprintf(out + ret, len - ret, f= ield, "%s", msg);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return ret;
+}
+
+__rte_format_printf(2, 0)
+int
+color_print(FILE *f, const char *format, va_list ap)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0char out[LINE_MAX];
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0/* format raw message */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0int ret =3D color_fmt_msg(out, sizeof(out), for= mat, ap);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (fputs(out, f) < 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return ret;
+}
+
+__rte_format_printf(2, 0)
+int
+color_print_with_timestamp(FILE *f, const char *format, va_list ap)
+{
+=C2=A0 =C2=A0 =C2=A0 =C2=A0char out[LINE_MAX];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0char tsbuf[128];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0int ret =3D 0;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (log_timestamp(tsbuf, sizeof(tsbuf)) > 0)=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D color_snpri= ntf(out, sizeof(out),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 LOG_FIELD_TIME, "= [%s] ", tsbuf);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0ret +=3D color_fmt_msg(out + ret, sizeof(out) -= ret, format, ap);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0if (fputs(out, f) < 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -1;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0return ret;
+}
diff --git a/lib/log/log_internal.h b/lib/log/log_internal.h
index 82fdc21ac2..bba7041ea3 100644
--- a/lib/log/log_internal.h
+++ b/lib/log/log_internal.h
@@ -50,5 +50,10 @@ void rte_eal_log_cleanup(void);
=C2=A0__rte_internal
=C2=A0int eal_log_timestamp(const char *fmt);

+/*
+ * Enable or disable color in log messages
+ */
+__rte_internal
+int eal_log_color(const char *mode);

=C2=A0#endif /* LOG_INTERNAL_H */
diff --git a/lib/log/log_private.h b/lib/log/log_private.h
index 625f57c0cc..42babbcac1 100644
--- a/lib/log/log_private.h
+++ b/lib/log/log_private.h
@@ -25,4 +25,12 @@ ssize_t log_timestamp(char *tsbuf, size_t tsbuflen);
=C2=A0__rte_format_printf(2, 0)
=C2=A0int log_print_with_timestamp(FILE *f, const char *format, va_list ap)= ;

+bool log_color_enabled(bool is_tty);
+
+__rte_format_printf(2, 0)
+int color_print(FILE *f, const char *format, va_list ap);
+
+__rte_format_printf(2, 0)
+int color_print_with_timestamp(FILE *f, const char *format, va_list ap); +
=C2=A0#endif /* LOG_PRIVATE_H */
diff --git a/lib/log/meson.build b/lib/log/meson.build
index 86e4452b19..b3de57b9c7 100644
--- a/lib/log/meson.build
+++ b/lib/log/meson.build
@@ -4,6 +4,7 @@
=C2=A0includes +=3D global_inc
=C2=A0sources =3D files(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'log.c',
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 'log_color.c',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'log_timestamp.c',
=C2=A0)

diff --git a/lib/log/version.map b/lib/log/version.map
index 800d3943bc..09d8a4289b 100644
--- a/lib/log/version.map
+++ b/lib/log/version.map
@@ -25,6 +25,7 @@ DPDK_25 {
=C2=A0INTERNAL {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 global:

+=C2=A0 =C2=A0 =C2=A0 =C2=A0eal_log_color;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 eal_log_init;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 eal_log_journal; # WINDOWS_NO_EXPORT
=C2=A0 =C2=A0 =C2=A0 =C2=A0 eal_log_level2str;
--
2.45.2



--
=
Baruch Even
Platform Technical Lead= at=C2=A0WEKA
E=C2=A0baruch@weka.io=E2=80= =85=C2=ADW=C2=A0https://www.weka.io=E2=80=85=C2=AD

= 3D"App

= =C2=A0
--000000000000dfccf206254f0f43--