xref: /dpdk/lib/log/log_color.c (revision 259f6f78094d0fa33ce2ffe298b8df526c535f3b)
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