From 11018ebcc1cb1c002fc3488d247a43774ca408d7 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Fri, 16 Jan 2009 00:19:22 +0100 Subject: [PATCH] Implement colored logging for para_server. --- color.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++ color.h | 9 ++++ configure.ac | 4 +- error.h | 1 + server.c | 105 ++++++++++++++++++++++++++++++++++++---- server.ggo | 18 +++++++ 6 files changed, 257 insertions(+), 12 deletions(-) create mode 100644 color.c create mode 100644 color.h diff --git a/color.c b/color.c new file mode 100644 index 00000000..bd09446c --- /dev/null +++ b/color.c @@ -0,0 +1,132 @@ +/** \file color.c Functions for printing colored messages. */ + +/* + * Mostly taken from the git source tree, version 1.6.1.76, January 2008. + */ + +#include "para.h" +#include "color.h" + +static int parse_color(const char *name, int len) +{ + static const char * const color_names[] = { + "normal", "black", "red", "green", "yellow", + "blue", "magenta", "cyan", "white" + }; + char *end; + int i; + for (i = 0; i < ARRAY_SIZE(color_names); i++) { + const char *str = color_names[i]; + if (!strncasecmp(name, str, len) && !str[len]) + return i - 1; + } + i = strtol(name, &end, 10); + if (end - name == len && i >= -1 && i <= 255) + return i; + return -2; +} + +static int parse_attr(const char *name, int len) +{ + static const int attr_values[] = { 1, 2, 4, 5, 7 }; + static const char * const attr_names[] = { + "bold", "dim", "ul", "blink", "reverse" + }; + int i; + for (i = 0; i < ARRAY_SIZE(attr_names); i++) { + const char *str = attr_names[i]; + if (!strncasecmp(name, str, len) && !str[len]) + return attr_values[i]; + } + return -1; +} + +/** + * Create an escape sequence for colored output. + * + * \param value Human-readable color spec. + * \param dst Result pointer for the escape sequence. + * + * \return -1 on errors, 1 on success. + * + * Format of \a value: [fg [bg]] [attr]. + */ +int color_parse(const char *value, char *dst) +{ + const char *ptr = value; + int attr = -1; + int fg = -2; + int bg = -2; + + if (!strcasecmp(value, "reset")) { + strcpy(dst, COLOR_RESET); + return 1; + } + + /* [fg [bg]] [attr] */ + while (*ptr) { + const char *word = ptr; + int val, len = 0; + + while (word[len] && !isspace(word[len])) + len++; + + ptr = word + len; + while (*ptr && isspace(*ptr)) + ptr++; + + val = parse_color(word, len); + if (val >= -1) { + if (fg == -2) { + fg = val; + continue; + } + if (bg == -2) { + bg = val; + continue; + } + goto bad; + } + val = parse_attr(word, len); + if (val < 0 || attr != -1) + goto bad; + attr = val; + } + + if (attr >= 0 || fg >= 0 || bg >= 0) { + int sep = 0; + + *dst++ = '\033'; + *dst++ = '['; + if (attr >= 0) { + *dst++ = '0' + attr; + sep++; + } + if (fg >= 0) { + if (sep++) + *dst++ = ';'; + if (fg < 8) { + *dst++ = '3'; + *dst++ = '0' + fg; + } else { + dst += sprintf(dst, "38;5;%d", fg); + } + } + if (bg >= 0) { + if (sep++) + *dst++ = ';'; + if (bg < 8) { + *dst++ = '4'; + *dst++ = '0' + bg; + } else { + dst += sprintf(dst, "48;5;%d", bg); + } + } + *dst++ = 'm'; + } + *dst = 0; + return 1; +bad: + PARA_ERROR_LOG("bad color value '%s'\n", value); + return -1; +} diff --git a/color.h b/color.h new file mode 100644 index 00000000..10b4c28a --- /dev/null +++ b/color.h @@ -0,0 +1,9 @@ +/** \file color.h Exported functions from color.c. */ + + +/** "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ +#define COLOR_MAXLEN 24 + +#define COLOR_RESET "\033[m" + +int color_parse(const char *value, char *dst); diff --git a/configure.ac b/configure.ac index eb1cea63..776478c5 100644 --- a/configure.ac +++ b/configure.ac @@ -84,7 +84,7 @@ dccp_send fd user_list chunk_queue afs osl aft mood score attribute blob ringbuf playlist sha1 rbtree sched audiod grab_client filter_common wav_filter compress_filter http_recv dccp_recv recv_common write_common file_write audiod_command client_common recv stdout filter stdin audioc write client fsck exec send_common ggo -udp_recv udp_send" +udp_recv udp_send color" all_executables="server recv filter audioc write client fsck afh" @@ -123,7 +123,7 @@ server_cmdline_objs="server.cmdline server_command_list afs_command_list" server_errlist_objs="server afh_common mp3_afh vss command net string signal time daemon stat crypt http_send close_on_fork ipc dccp_send fd user_list chunk_queue afs osl aft mood score attribute - blob playlist sha1 rbtree sched acl send_common udp_send" + blob playlist sha1 rbtree sched acl send_common udp_send color" server_ldflags="" server_audio_formats=" mp3" diff --git a/error.h b/error.h index 2a8a6b5a..0a5fb217 100644 --- a/error.h +++ b/error.h @@ -29,6 +29,7 @@ DEFINE_ERRLIST_OBJECT_ENUM; #define DCCP_SEND_ERRORS #define HTTP_SEND_ERRORS #define GGO_ERRORS +#define COLOR_ERRORS extern const char **para_errlist[]; diff --git a/server.c b/server.c index c9400e9c..1fadf727 100644 --- a/server.c +++ b/server.c @@ -85,6 +85,7 @@ #include "sched.h" #include "signal.h" #include "user_list.h" +#include "color.h" /** Define the array of error lists needed by para_server. */ INIT_SERVER_ERRLISTS; @@ -131,6 +132,77 @@ struct server_command_task { struct task task; }; +static int want_colors(void) +{ + if (conf.color_arg == color_arg_no) + return 0; + if (conf.color_arg == color_arg_yes) + return 1; + if (logfile) + return 0; + return isatty(STDERR_FILENO); +} + +static int get_loglevel_by_name(const char *txt, size_t n) +{ + if (!strncasecmp(txt, "debug", n)) + return LL_DEBUG; + if (!strncasecmp(txt, "info", n)) + return LL_INFO; + if (!strncasecmp(txt, "notice", n)) + return LL_NOTICE; + if (!strncasecmp(txt, "warning", n)) + return LL_WARNING; + if (!strncasecmp(txt, "error", n)) + return LL_ERROR; + if (!strncasecmp(txt, "crit", n)) + return LL_CRIT; + if (!strncasecmp(txt, "emerg", n)) + return LL_EMERG; + return -1; +} + +static char log_colors[NUM_LOGLEVELS][COLOR_MAXLEN]; + +static void init_colors_or_die(void) +{ + int ret, i; + static const char *default_log_colors[NUM_LOGLEVELS] = { + [LL_DEBUG] = "normal", + [LL_INFO] = "white bold", + [LL_NOTICE] = "cyan bold", + [LL_WARNING] = "green bold", + [LL_ERROR] = "yellow bold", + [LL_CRIT] = "magenta bold", + [LL_EMERG] = "red bold", + }; + + for (i = 0; i < NUM_LOGLEVELS; i++) { + ret = color_parse(default_log_colors[i], log_colors[i]); + assert(ret >= 0); + } + + for (i = 0; i < conf.log_color_given; i++) { + char *arg = conf.log_color_arg[i], *p = strchr(arg, ':'); + int ll; + if (!p) + goto err; + ret = get_loglevel_by_name(arg, p - arg); + if (ret < 0) + goto err; + ll = ret; + p++; + ret = color_parse(p, log_colors[ll]); + if (ret < 0) + goto err; + } + return; +err: + PARA_EMERG_LOG("color syntax error, arg %d (%s)\n", i, + conf.log_color_arg[i]); + exit(EXIT_FAILURE); +} + /** * Para_server's log function. * @@ -140,27 +212,38 @@ struct server_command_task { __printf_2_3 void para_log(int ll, const char* fmt,...) { va_list argp; - FILE *outfd; + FILE *fp; struct tm *tm; time_t t1; - char str[MAXLINE] = ""; - pid_t mypid; + char *color, str[MAXLINE] = ""; + ll = PARA_MIN(ll, NUM_LOGLEVELS - 1); + ll = PARA_MAX(ll, LL_DEBUG); if (ll < conf.loglevel_arg) return; - outfd = logfile? logfile : stderr; + + fp = logfile? logfile : stderr; + color = want_colors()? log_colors[ll] : NULL; + + if (color) + fprintf(fp, "%s", color); + /* date and time */ time(&t1); tm = localtime(&t1); strftime(str, MAXLINE, "%b %d %H:%M:%S", tm); - fprintf(outfd, "%s ", str); + fprintf(fp, "%s ", str); + /* loglevel */ if (conf.loglevel_arg <= LL_INFO) - fprintf(outfd, "%i: ", ll); - mypid = getpid(); - if (conf.loglevel_arg <= LL_INFO) - fprintf(outfd, "(%d) ", (int)mypid); + fprintf(fp, "%i: ", ll); + if (conf.loglevel_arg <= LL_INFO) { /* log pid */ + pid_t mypid = getpid(); + fprintf(fp, "(%d) ", (int)mypid); + } va_start(argp, fmt); - vfprintf(outfd, fmt, argp); + vfprintf(fp, fmt, argp); va_end(argp); + if (color) + fprintf(fp, "%s", COLOR_RESET); } /* @@ -244,6 +327,8 @@ void parse_config_or_die(int override) } if (conf.logfile_given) logfile = open_log(conf.logfile_arg); + if (want_colors()) + init_colors_or_die(); ret = 1; out: free(cf); diff --git a/server.ggo b/server.ggo index 2a5fabc3..fc606372 100644 --- a/server.ggo +++ b/server.ggo @@ -20,6 +20,24 @@ details=" that cause para_server to terminate immediately. " +option "color" C +#~~~~~~~~~~~~~~~ +"activate color output" +enum typestr="when" +values = "yes","no","auto" +default = "auto" +optional + +option "log_color" - +#~~~~~~~~~~~~~~~~~~~ +"select a color for one type of log message" +string typestr="color_spec" +multiple +optional +details=" + Example: --log_color \"INFO:yellow black bold\" +" + option "port" p #~~~~~~~~~~~~~~ "listening port" -- 2.39.2