diff mbox series

Configure colors for stdout / stderr

Message ID 20200911101228.143944-1-sbabic@denx.de
State Accepted
Headers show
Series Configure colors for stdout / stderr | expand

Commit Message

Stefano Babic Sept. 11, 2020, 10:12 a.m. UTC
SWUpdate uses colors to log on the stdout / sdterr. This lets to
identify better each loglevel, but the default values can conflict with
the setup of the terminal and makes the output difficult to read. Add a
section in the configuration file and let configure the colors for each
loglevel.

Signed-off-by: Stefano Babic <sbabic@denx.de>
Reported-by: Claudius Heine <ch@denx.de>
---
 core/notifier.c                     | 143 ++++++++++++++++++++++++++--
 core/swupdate.c                     |  29 ++++++
 examples/configuration/swupdate.cfg |  20 ++++
 include/util.h                      |   1 +
 4 files changed, 183 insertions(+), 10 deletions(-)

Comments

Storm, Christian Sept. 16, 2020, 12:04 p.m. UTC | #1
Hi Stefano,

[...]
>  static int read_processes_settings(void *settings, void *data)
>  {
>  	struct swupdate_cfg *sw = (struct swupdate_cfg *)data;
> @@ -708,6 +730,13 @@ int main(int argc, char **argv)
>  		if (swcfg.globals.verbose)
>  			loglevel = TRACELEVEL;
>  
> +		if (read_module_settings(cfgfname, "logcolors",
> +			read_console_settings, &swcfg)) {
> +			fprintf(stderr,
> +				 "Error parsing configuration file, exiting.\n");
> +			exit(EXIT_FAILURE);
> +		}
> +

This makes a "logcolors" section in the configuration file mandatory.
If read_module_settings() doesn't find this section, it returns -ENODATA
and by extension an error is printed and main() is exited with EXIT_FAILURE.
Is this what you have intended?


Kind regards,
   Christian
Stefano Babic Sept. 16, 2020, 12:12 p.m. UTC | #2
On 16.09.20 14:04, Christian Storm wrote:
> Hi Stefano,
> 
> [...]
>>  static int read_processes_settings(void *settings, void *data)
>>  {
>>  	struct swupdate_cfg *sw = (struct swupdate_cfg *)data;
>> @@ -708,6 +730,13 @@ int main(int argc, char **argv)
>>  		if (swcfg.globals.verbose)
>>  			loglevel = TRACELEVEL;
>>  
>> +		if (read_module_settings(cfgfname, "logcolors",
>> +			read_console_settings, &swcfg)) {
>> +			fprintf(stderr,
>> +				 "Error parsing configuration file, exiting.\n");
>> +			exit(EXIT_FAILURE);
>> +		}
>> +
> 
> This makes a "logcolors" section in the configuration file mandatory.
> If read_module_settings() doesn't find this section, it returns -ENODATA
> and by extension an error is printed and main() is exited with EXIT_FAILURE.
> Is this what you have intended?

Of course, not. I'll fix this.

Regards,
Stefano
diff mbox series

Patch

diff --git a/core/notifier.c b/core/notifier.c
index 03fddf8..3b8a432 100644
--- a/core/notifier.c
+++ b/core/notifier.c
@@ -62,6 +62,128 @@  static int notifyfd = -1;
 static bool console_priority_prefix = false;
 static bool console_ansi_colors = false;
 
+/*
+ * Escape sequences:
+ * they are in the format <ESC>[{attr};{fg};{bg}m
+ * where :
+ * <ESC> Escape char 0x1B
+ * attr : attriibute
+ * fg : foreground
+ * bg : background
+ */
+
+enum console_attr {
+	RESET,
+	BRIGHT,
+	DIM,
+	UNDERLINE,
+	BLINK,
+	REVERSE,
+	HIDDEN
+};
+
+#define RESET		0
+#define BRIGHT 		1
+#define DIM		2
+#define UNDERLINE 	3
+#define BLINK		4
+#define REVERSE		7
+#define HIDDEN		8
+
+
+
+enum console_colors {
+	BLACK,
+	RED,
+	GREEN,
+	YELLOW,
+	BLUE,
+	MAGENTA,
+	CYAN,
+	WHITE,
+	COLOR_NONE
+};
+
+struct logcolor {
+	int attr;
+	int fg;
+	int bg;
+};
+
+#define RESET_COLOR "\x1b[0m"
+
+static const char *ascii_string_colors[] = {
+	"black",
+	"red",
+	"green",
+	"yellow",
+	"blue",
+	"magenta",
+	"cyan",
+	"white",
+	"none"
+};
+
+static const char *ascii_string_attributes[] = {
+	"normal",
+	"bright",
+	"dim",
+	"underline",
+	"underline",
+	"blink",
+	"blink",
+	"reverse",
+	"hidden"
+};
+
+struct logcolor consolecolors[] = {
+	[ERRORLEVEL] = {BRIGHT, RED, COLOR_NONE},
+	[WARNLEVEL] = {BRIGHT, YELLOW, COLOR_NONE},
+	[INFOLEVEL] = {BRIGHT, GREEN, COLOR_NONE},
+	[DEBUGLEVEL] = {BRIGHT, BLACK, COLOR_NONE},
+	[TRACELEVEL] = {BRIGHT, BLACK, COLOR_NONE}
+};
+
+static void set_console_color(int level, char *buf, size_t size) {
+	struct logcolor *attr;
+	if (level < 0 || level >= ARRAY_SIZE(consolecolors))
+		return;
+	memset(buf, 0, size);
+	attr = &consolecolors[level];
+	if (attr->fg == COLOR_NONE && attr->attr == RESET)
+		return;
+	if (attr->fg != COLOR_NONE)
+		snprintf(buf, size, "%c[%d;%dm", 0x1B, attr->attr, attr->fg + 30);
+	else
+		snprintf(buf, size, "%c[%dm", 0x1B, attr->attr);
+}
+
+void notifier_set_color(int level, char *col)
+{
+	int i;
+	char *attr;
+	if (level < ERRORLEVEL || level > LASTLOGLEVEL || !col)
+		return;
+	attr = strchr(col, ':');
+	if (attr && (strlen(col) > (attr - col + 1))) {
+		*attr = '\0';
+		attr++;
+	} else
+		attr = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(ascii_string_colors); i++) {
+		if (!strcmp(col, ascii_string_colors[i])) {
+			consolecolors[level].fg = i;
+		}
+	}
+	if (attr)
+		for (i = 0; i < ARRAY_SIZE(ascii_string_attributes); i++) {
+			if (!strcmp(attr, ascii_string_attributes[i])) {
+				consolecolors[level].attr = i;
+			}
+		}
+}
+
 /*
  * This allows to extend the list of notifier.
  * One can register a new notifier and it will
@@ -121,6 +243,7 @@  void notify(RECOVERY_STATUS status, int error, int level, const char *msg)
 static void console_notifier (RECOVERY_STATUS status, int error, int level, const char *msg)
 {
 	char current[80];
+	char color[32];
 	switch(status) {
 	case IDLE:
 		strncpy(current, "No SWUPDATE running : ", sizeof(current));
@@ -155,30 +278,30 @@  static void console_notifier (RECOVERY_STATUS status, int error, int level, cons
 		break;
 	}
 
+	if (console_ansi_colors)
+		set_console_color(level, color, sizeof(color));
+	else
+		color[0] = '0';
+
 	switch (level) {
 	case ERRORLEVEL:
-		fprintf(stderr, "%s%s[ERROR]",
-				console_ansi_colors ? "\033[01;31m" : "",
+		fprintf(stderr, "%s%s[ERROR]", color,
 				console_priority_prefix ? "<3>" : "");
 		break;
 	case WARNLEVEL:
-		fprintf(stdout, "%s%s[WARN ]",
-				console_ansi_colors ? "\033[01;33m" : "",
+		fprintf(stdout, "%s%s[WARN ]", color,
 				console_priority_prefix ? "<4>" : "");
 		break;
 	case INFOLEVEL:
-		fprintf(stdout, "%s%s[INFO ]",
-				console_ansi_colors ? "\033[01;32m" : "",
+		fprintf(stdout, "%s%s[INFO ]", color,
 				console_priority_prefix ? "<6>" : "");
 		break;
 	case DEBUGLEVEL:
-		fprintf(stdout, "%s%s[DEBUG]",
-				console_ansi_colors ? "\033[01;30m" : "",
+		fprintf(stdout, "%s%s[DEBUG]", color,
 				console_priority_prefix ? "<7>" : "");
 		break;
 	case TRACELEVEL:
-		fprintf(stdout, "%s%s[TRACE]",
-				console_ansi_colors ? "\033[01;30m" : "",
+		fprintf(stdout, "%s%s[TRACE]", color,
 				console_priority_prefix ? "<7>" : "");
 		break;
 	}
diff --git a/core/swupdate.c b/core/swupdate.c
index 0d610c4..ac0515c 100644
--- a/core/swupdate.c
+++ b/core/swupdate.c
@@ -551,6 +551,28 @@  static int read_globals_settings(void *elem, void *data)
 	return 0;
 }
 
+const char *loglevnames[] = {
+	[ERRORLEVEL] = "error",
+	[WARNLEVEL] = "warning",
+	[INFOLEVEL] = "info",
+	[DEBUGLEVEL] = "debug",
+	[TRACELEVEL] = "trace"
+};
+
+static int read_console_settings(void *elem, void __attribute__ ((__unused__)) *data)
+{
+	char tmp[SWUPDATE_GENERAL_STRING_SIZE] = "";
+	int i;
+
+	for (i = ERRORLEVEL; i <= TRACELEVEL; i++) {
+		memset(tmp, 0, sizeof(tmp));
+		GET_FIELD_STRING(LIBCFG_PARSER, elem, loglevnames[i], tmp);
+		if (tmp[0] != '\0')
+			notifier_set_color(i, tmp);
+	}
+	return 0;
+}
+
 static int read_processes_settings(void *settings, void *data)
 {
 	struct swupdate_cfg *sw = (struct swupdate_cfg *)data;
@@ -708,6 +730,13 @@  int main(int argc, char **argv)
 		if (swcfg.globals.verbose)
 			loglevel = TRACELEVEL;
 
+		if (read_module_settings(cfgfname, "logcolors",
+			read_console_settings, &swcfg)) {
+			fprintf(stderr,
+				 "Error parsing configuration file, exiting.\n");
+			exit(EXIT_FAILURE);
+		}
+
 		int ret = read_module_settings(cfgfname, "processes",
 						read_processes_settings,
 						&swcfg);
diff --git a/examples/configuration/swupdate.cfg b/examples/configuration/swupdate.cfg
index cda2466..a923795 100644
--- a/examples/configuration/swupdate.cfg
+++ b/examples/configuration/swupdate.cfg
@@ -36,7 +36,27 @@  globals :
 	syslog = true;
 	/* public-key-file = "test.pem";*/
 	mtd-blacklist = "0 1 2 3 4 5 6";
+};
 
+# logcolors : set colors for output to stdout / stderr
+#             color is set indivisually for each level
+#             each entry is in the format
+#             loglevel = color:attribute
+# where loglevel is one of:
+# 	"error","warning", "info", "debug", "trace"
+# and color is one of:
+# 	"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"
+# and attribute is one of:
+# 	"normal", "bright", "dim", "underline", "blink", "reverse", "hidden"
+#
+# Example :
+# 	error = "red";
+#
+logcolors : {
+	error = "red:blink";
+	trace = "green:normal";
+	debug = "magenta:normal";
+	warning = "yellow:underline";
 };
 
 #
diff --git a/include/util.h b/include/util.h
index b665179..9f377c3 100644
--- a/include/util.h
+++ b/include/util.h
@@ -88,6 +88,7 @@  typedef void (*notifier) (RECOVERY_STATUS status, int error, int level, const ch
 
 void notify(RECOVERY_STATUS status, int error, int level, const char *msg);
 void notify_init(void);
+void notifier_set_color(int level, char *col);
 #define swupdate_notify(status, format, level, arg...) do { \
 	if (loglevel >= level) { \
 		char tmpbuf[NOTIFY_BUF_SIZE]; \