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