1*259f6f78SStephen Hemminger /* SPDX-License-Identifier: BSD-3-Clause */ 2*259f6f78SStephen Hemminger 3*259f6f78SStephen Hemminger #include <limits.h> 4*259f6f78SStephen Hemminger #include <stdbool.h> 5*259f6f78SStephen Hemminger #include <stdio.h> 6*259f6f78SStephen Hemminger #include <stdint.h> 7*259f6f78SStephen Hemminger #include <stdarg.h> 8*259f6f78SStephen Hemminger #include <stdlib.h> 9*259f6f78SStephen Hemminger #include <string.h> 10*259f6f78SStephen Hemminger 11*259f6f78SStephen Hemminger #include <rte_common.h> 12*259f6f78SStephen Hemminger #include <rte_log.h> 13*259f6f78SStephen Hemminger 14*259f6f78SStephen Hemminger #ifdef RTE_EXEC_ENV_WINDOWS 15*259f6f78SStephen Hemminger #include <rte_os_shim.h> 16*259f6f78SStephen Hemminger #endif 17*259f6f78SStephen Hemminger 18*259f6f78SStephen Hemminger #include "log_internal.h" 19*259f6f78SStephen Hemminger #include "log_private.h" 20*259f6f78SStephen Hemminger 21*259f6f78SStephen Hemminger enum { 22*259f6f78SStephen Hemminger LOG_COLOR_AUTO = 0, 23*259f6f78SStephen Hemminger LOG_COLOR_NEVER, 24*259f6f78SStephen Hemminger LOG_COLOR_ALWAYS, 25*259f6f78SStephen Hemminger } log_color_mode = LOG_COLOR_NEVER; 26*259f6f78SStephen Hemminger 27*259f6f78SStephen Hemminger enum color { 28*259f6f78SStephen Hemminger COLOR_NONE, 29*259f6f78SStephen Hemminger COLOR_RED, 30*259f6f78SStephen Hemminger COLOR_GREEN, 31*259f6f78SStephen Hemminger COLOR_YELLOW, 32*259f6f78SStephen Hemminger COLOR_BLUE, 33*259f6f78SStephen Hemminger COLOR_MAGENTA, 34*259f6f78SStephen Hemminger COLOR_CYAN, 35*259f6f78SStephen Hemminger COLOR_WHITE, 36*259f6f78SStephen Hemminger COLOR_BOLD, 37*259f6f78SStephen Hemminger COLOR_CLEAR, 38*259f6f78SStephen Hemminger }; 39*259f6f78SStephen Hemminger 40*259f6f78SStephen Hemminger enum log_field { 41*259f6f78SStephen Hemminger LOG_FIELD_SUBSYS, 42*259f6f78SStephen Hemminger LOG_FIELD_TIME, 43*259f6f78SStephen Hemminger LOG_FIELD_ALERT, 44*259f6f78SStephen Hemminger LOG_FIELD_ERROR, 45*259f6f78SStephen Hemminger LOG_FIELD_INFO, 46*259f6f78SStephen Hemminger }; 47*259f6f78SStephen Hemminger 48*259f6f78SStephen Hemminger static const enum color field_colors[] = { 49*259f6f78SStephen Hemminger [LOG_FIELD_SUBSYS] = COLOR_YELLOW, 50*259f6f78SStephen Hemminger [LOG_FIELD_TIME] = COLOR_GREEN, 51*259f6f78SStephen Hemminger [LOG_FIELD_ALERT] = COLOR_RED, 52*259f6f78SStephen Hemminger [LOG_FIELD_ERROR] = COLOR_BOLD, 53*259f6f78SStephen Hemminger [LOG_FIELD_INFO] = COLOR_NONE, 54*259f6f78SStephen Hemminger }; 55*259f6f78SStephen Hemminger 56*259f6f78SStephen Hemminger /* If set all colors are bolder */ 57*259f6f78SStephen Hemminger static bool dark_mode; 58*259f6f78SStephen Hemminger 59*259f6f78SStephen Hemminger /* Standard terminal escape codes for colors and bold */ 60*259f6f78SStephen Hemminger static const uint8_t color_esc_code[] = { 61*259f6f78SStephen Hemminger [COLOR_RED] = 31, 62*259f6f78SStephen Hemminger [COLOR_GREEN] = 32, 63*259f6f78SStephen Hemminger [COLOR_YELLOW] = 33, 64*259f6f78SStephen Hemminger [COLOR_BLUE] = 34, 65*259f6f78SStephen Hemminger [COLOR_MAGENTA] = 35, 66*259f6f78SStephen Hemminger [COLOR_CYAN] = 36, 67*259f6f78SStephen Hemminger [COLOR_WHITE] = 37, 68*259f6f78SStephen Hemminger [COLOR_BOLD] = 1, 69*259f6f78SStephen Hemminger }; 70*259f6f78SStephen Hemminger 71*259f6f78SStephen Hemminger __rte_format_printf(4, 5) 72*259f6f78SStephen Hemminger static int 73*259f6f78SStephen Hemminger color_snprintf(char *buf, size_t len, enum log_field field, 74*259f6f78SStephen Hemminger const char *fmt, ...) 75*259f6f78SStephen Hemminger { 76*259f6f78SStephen Hemminger enum color color = field_colors[field]; 77*259f6f78SStephen Hemminger uint8_t esc = color_esc_code[color]; 78*259f6f78SStephen Hemminger va_list args; 79*259f6f78SStephen Hemminger int ret = 0; 80*259f6f78SStephen Hemminger 81*259f6f78SStephen Hemminger va_start(args, fmt); 82*259f6f78SStephen Hemminger if (esc == 0) { 83*259f6f78SStephen Hemminger ret = vsnprintf(buf, len, fmt, args); 84*259f6f78SStephen Hemminger } else { 85*259f6f78SStephen Hemminger ret = snprintf(buf, len, 86*259f6f78SStephen Hemminger dark_mode ? "\033[1;%um" : "\033[%um", esc); 87*259f6f78SStephen Hemminger ret += vsnprintf(buf + ret, len - ret, fmt, args); 88*259f6f78SStephen Hemminger ret += snprintf(buf + ret, len - ret, "%s", "\033[0m"); 89*259f6f78SStephen Hemminger } 90*259f6f78SStephen Hemminger va_end(args); 91*259f6f78SStephen Hemminger 92*259f6f78SStephen Hemminger return ret; 93*259f6f78SStephen Hemminger } 94*259f6f78SStephen Hemminger 95*259f6f78SStephen Hemminger /* 96*259f6f78SStephen Hemminger * Controls whether color is enabled: 97*259f6f78SStephen Hemminger * modes are: 98*259f6f78SStephen Hemminger * always - enable color output regardless 99*259f6f78SStephen Hemminger * auto - enable if stderr is a terminal 100*259f6f78SStephen Hemminger * never - color output is disabled. 101*259f6f78SStephen Hemminger */ 102*259f6f78SStephen Hemminger int 103*259f6f78SStephen Hemminger eal_log_color(const char *mode) 104*259f6f78SStephen Hemminger { 105*259f6f78SStephen Hemminger if (mode == NULL || strcmp(mode, "always") == 0) 106*259f6f78SStephen Hemminger log_color_mode = LOG_COLOR_ALWAYS; 107*259f6f78SStephen Hemminger else if (strcmp(mode, "never") == 0) 108*259f6f78SStephen Hemminger log_color_mode = LOG_COLOR_NEVER; 109*259f6f78SStephen Hemminger else if (strcmp(mode, "auto") == 0) 110*259f6f78SStephen Hemminger log_color_mode = LOG_COLOR_AUTO; 111*259f6f78SStephen Hemminger else 112*259f6f78SStephen Hemminger return -1; 113*259f6f78SStephen Hemminger 114*259f6f78SStephen Hemminger return 0; 115*259f6f78SStephen Hemminger } 116*259f6f78SStephen Hemminger 117*259f6f78SStephen Hemminger bool 118*259f6f78SStephen Hemminger log_color_enabled(bool is_terminal) 119*259f6f78SStephen Hemminger { 120*259f6f78SStephen Hemminger char *env, *sep; 121*259f6f78SStephen Hemminger 122*259f6f78SStephen Hemminger /* Set dark mode using the defacto heuristics used by other programs */ 123*259f6f78SStephen Hemminger env = getenv("COLORFGBG"); 124*259f6f78SStephen Hemminger if (env) { 125*259f6f78SStephen Hemminger sep = strrchr(env, ';'); 126*259f6f78SStephen Hemminger if (sep && 127*259f6f78SStephen Hemminger ((sep[1] >= '0' && sep[1] <= '6') || sep[1] == '8') && 128*259f6f78SStephen Hemminger sep[2] == '\0') 129*259f6f78SStephen Hemminger dark_mode = true; 130*259f6f78SStephen Hemminger } 131*259f6f78SStephen Hemminger 132*259f6f78SStephen Hemminger if (log_color_mode == LOG_COLOR_ALWAYS) 133*259f6f78SStephen Hemminger return true; 134*259f6f78SStephen Hemminger else if (log_color_mode == LOG_COLOR_AUTO) 135*259f6f78SStephen Hemminger return is_terminal; 136*259f6f78SStephen Hemminger else 137*259f6f78SStephen Hemminger return false; 138*259f6f78SStephen Hemminger } 139*259f6f78SStephen Hemminger 140*259f6f78SStephen Hemminger /* Look ast the current message level to determine color of field */ 141*259f6f78SStephen Hemminger static enum log_field 142*259f6f78SStephen Hemminger color_msg_field(void) 143*259f6f78SStephen Hemminger { 144*259f6f78SStephen Hemminger const int level = rte_log_cur_msg_loglevel(); 145*259f6f78SStephen Hemminger 146*259f6f78SStephen Hemminger if (level <= 0 || level >= (int)RTE_LOG_INFO) 147*259f6f78SStephen Hemminger return LOG_FIELD_INFO; 148*259f6f78SStephen Hemminger else if (level >= (int)RTE_LOG_ERR) 149*259f6f78SStephen Hemminger return LOG_FIELD_ERROR; 150*259f6f78SStephen Hemminger else 151*259f6f78SStephen Hemminger return LOG_FIELD_ALERT; 152*259f6f78SStephen Hemminger } 153*259f6f78SStephen Hemminger 154*259f6f78SStephen Hemminger __rte_format_printf(3, 0) 155*259f6f78SStephen Hemminger static int 156*259f6f78SStephen Hemminger color_fmt_msg(char *out, size_t len, const char *format, va_list ap) 157*259f6f78SStephen Hemminger { 158*259f6f78SStephen Hemminger enum log_field field = color_msg_field(); 159*259f6f78SStephen Hemminger char buf[LINE_MAX]; 160*259f6f78SStephen Hemminger int ret = 0; 161*259f6f78SStephen Hemminger 162*259f6f78SStephen Hemminger /* format raw message */ 163*259f6f78SStephen Hemminger vsnprintf(buf, sizeof(buf), format, ap); 164*259f6f78SStephen Hemminger const char *msg = buf; 165*259f6f78SStephen Hemminger 166*259f6f78SStephen Hemminger /* 167*259f6f78SStephen Hemminger * use convention that first part of message (up to the ':' character) 168*259f6f78SStephen Hemminger * is the subsystem id and should be highlighted. 169*259f6f78SStephen Hemminger */ 170*259f6f78SStephen Hemminger const char *cp = strchr(msg, ':'); 171*259f6f78SStephen Hemminger if (cp) { 172*259f6f78SStephen Hemminger /* print first part in yellow */ 173*259f6f78SStephen Hemminger ret = color_snprintf(out, len, LOG_FIELD_SUBSYS, 174*259f6f78SStephen Hemminger "%.*s", (int)(cp - msg + 1), msg); 175*259f6f78SStephen Hemminger /* skip the first part */ 176*259f6f78SStephen Hemminger msg = cp + 1; 177*259f6f78SStephen Hemminger } 178*259f6f78SStephen Hemminger 179*259f6f78SStephen Hemminger ret += color_snprintf(out + ret, len - ret, field, "%s", msg); 180*259f6f78SStephen Hemminger return ret; 181*259f6f78SStephen Hemminger } 182*259f6f78SStephen Hemminger 183*259f6f78SStephen Hemminger __rte_format_printf(2, 0) 184*259f6f78SStephen Hemminger int 185*259f6f78SStephen Hemminger color_print(FILE *f, const char *format, va_list ap) 186*259f6f78SStephen Hemminger { 187*259f6f78SStephen Hemminger char out[LINE_MAX]; 188*259f6f78SStephen Hemminger 189*259f6f78SStephen Hemminger /* format raw message */ 190*259f6f78SStephen Hemminger int ret = color_fmt_msg(out, sizeof(out), format, ap); 191*259f6f78SStephen Hemminger if (fputs(out, f) < 0) 192*259f6f78SStephen Hemminger return -1; 193*259f6f78SStephen Hemminger 194*259f6f78SStephen Hemminger return ret; 195*259f6f78SStephen Hemminger } 196*259f6f78SStephen Hemminger 197*259f6f78SStephen Hemminger __rte_format_printf(2, 0) 198*259f6f78SStephen Hemminger int 199*259f6f78SStephen Hemminger color_print_with_timestamp(FILE *f, const char *format, va_list ap) 200*259f6f78SStephen Hemminger { 201*259f6f78SStephen Hemminger char out[LINE_MAX]; 202*259f6f78SStephen Hemminger char tsbuf[128]; 203*259f6f78SStephen Hemminger int ret = 0; 204*259f6f78SStephen Hemminger 205*259f6f78SStephen Hemminger if (log_timestamp(tsbuf, sizeof(tsbuf)) > 0) 206*259f6f78SStephen Hemminger ret = color_snprintf(out, sizeof(out), 207*259f6f78SStephen Hemminger LOG_FIELD_TIME, "[%s] ", tsbuf); 208*259f6f78SStephen Hemminger 209*259f6f78SStephen Hemminger ret += color_fmt_msg(out + ret, sizeof(out) - ret, format, ap); 210*259f6f78SStephen Hemminger if (fputs(out, f) < 0) 211*259f6f78SStephen Hemminger return -1; 212*259f6f78SStephen Hemminger 213*259f6f78SStephen Hemminger return ret; 214*259f6f78SStephen Hemminger } 215