Ticket #571: Coloring for logs in Linux/UNIX, two more spaces in logs and runtime log colors configuration (thanks Ondrej.Sterbak)

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@2159 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjlib/include/pj/log.h b/pjlib/include/pj/log.h
index 76d609d..2d0961c 100644
--- a/pjlib/include/pj/log.h
+++ b/pjlib/include/pj/log.h
@@ -68,15 +68,17 @@
  */
 enum pj_log_decoration
 {
-    PJ_LOG_HAS_DAY_NAME   =   1, /**< Include day name [default: no].	     */
-    PJ_LOG_HAS_YEAR       =   2, /**< Include year digit [default: no]	     */
-    PJ_LOG_HAS_MONTH	  =   4, /**< Include month [default: no]	     */
-    PJ_LOG_HAS_DAY_OF_MON =   8, /**< Include day of month [default: no]     */
-    PJ_LOG_HAS_TIME	  =  16, /**< Include time [default: yes].	     */
-    PJ_LOG_HAS_MICRO_SEC  =  32, /**< Include microseconds [yes]             */
-    PJ_LOG_HAS_SENDER	  =  64, /**< Include sender in the log [yes].	     */
-    PJ_LOG_HAS_NEWLINE	  = 128, /**< Terminate each call with newline [yes].*/
-    PJ_LOG_HAS_CR	  = 256  /**< Include carriage return [no].	     */
+    PJ_LOG_HAS_DAY_NAME   =    1, /**< Include day name [default: no] 	      */
+    PJ_LOG_HAS_YEAR       =    2, /**< Include year digit [no]		      */
+    PJ_LOG_HAS_MONTH	  =    4, /**< Include month [no]		      */
+    PJ_LOG_HAS_DAY_OF_MON =    8, /**< Include day of month [no]	      */
+    PJ_LOG_HAS_TIME	  =   16, /**< Include time [yes]		      */
+    PJ_LOG_HAS_MICRO_SEC  =   32, /**< Include microseconds [yes]             */
+    PJ_LOG_HAS_SENDER	  =   64, /**< Include sender in the log [yes] 	      */
+    PJ_LOG_HAS_NEWLINE	  =  128, /**< Terminate each call with newline [yes] */
+    PJ_LOG_HAS_CR	  =  256, /**< Include carriage return [no] 	      */
+    PJ_LOG_HAS_SPACE	  =  512, /**< Include two spaces before log [yes]    */
+    PJ_LOG_HAS_COLOR	  = 1024  /**< Colorize logs [yes on win32]	      */
 };
 
 /**
@@ -199,6 +201,23 @@
 PJ_DECL(unsigned) pj_log_get_decor(void);
 
 
+/**
+ * Set color of log messages.
+ *
+ * @param level	    Log level which color will be changed.
+ * @param color	    Desired color.
+ */
+PJ_DECL(void) pj_log_set_color(int level, pj_color_t color);
+
+/**
+ * Get color of log messages.
+ *
+ * @param level	    Log level which color will be returned.
+ * @return	    Log color.
+ */
+PJ_DECL(pj_color_t) pj_log_get_color(int level);
+
+
 #else	/* #if PJ_LOG_MAX_LEVEL >= 1 */
 
 /**
@@ -236,6 +255,14 @@
 #  define pj_log_set_decor(decor)
 
 /**
+ * Set color of log messages.
+ *
+ * @param level	    Log level which color will be changed.
+ * @param color	    Desired color.
+ */
+#  define pj_log_set_color(level, color)
+
+/**
  * Get current maximum log verbositylevel.
  *
  * @return	    Current log maximum level.
@@ -249,6 +276,14 @@
  */
 #  define pj_log_get_decor()	0
 
+/**
+ * Get color of log messages.
+ *
+ * @param level	    Log level which color will be returned.
+ * @return	    Log color.
+ */
+#  define pj_log_get_color(level) 0
+
 
 #endif	/* #if PJ_LOG_MAX_LEVEL >= 1 */
 
diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c
index 164e25d..863d868 100644
--- a/pjlib/src/pj/log.c
+++ b/pjlib/src/pj/log.c
@@ -31,7 +31,35 @@
 #endif
 static pj_log_func *log_writer = &pj_log_write;
 static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
-			    PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE;
+			    PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE |
+			    PJ_LOG_HAS_SPACE 
+#if defined(PJ_WIN32) && PJ_WIN32!=0
+			    | PJ_LOG_HAS_COLOR
+#endif
+			    ;
+
+static pj_color_t PJ_LOG_COLOR_0 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
+static pj_color_t PJ_LOG_COLOR_1 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
+static pj_color_t PJ_LOG_COLOR_2 = PJ_TERM_COLOR_BRIGHT | 
+				   PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G;
+static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT | 
+				   PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G | 
+				   PJ_TERM_COLOR_B;
+static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G | 
+				   PJ_TERM_COLOR_B;
+static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G | 
+				   PJ_TERM_COLOR_B;
+static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G | 
+				   PJ_TERM_COLOR_B;
+/* Default terminal color */
+static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R | 
+				    PJ_TERM_COLOR_G | 
+				    PJ_TERM_COLOR_B;
 
 #if PJ_LOG_USE_STACK_BUFFER==0
 static char log_buffer[PJ_LOG_MAX_SIZE];
@@ -47,6 +75,56 @@
     return log_decor;
 }
 
+PJ_DEF(void) pj_log_set_color(int level, pj_color_t color)
+{
+    switch (level) 
+    {
+	case 0: PJ_LOG_COLOR_0 = color; 
+	    break;
+	case 1: PJ_LOG_COLOR_1 = color; 
+	    break;
+	case 2: PJ_LOG_COLOR_2 = color; 
+	    break;
+	case 3: PJ_LOG_COLOR_3 = color; 
+	    break;
+	case 4: PJ_LOG_COLOR_4 = color; 
+	    break;
+	case 5: PJ_LOG_COLOR_5 = color; 
+	    break;
+	case 6: PJ_LOG_COLOR_6 = color; 
+	    break;
+	/* Default terminal color */
+	case 77: PJ_LOG_COLOR_77 = color; 
+	    break;
+	default:
+	    /* Do nothing */
+	    break;
+    }
+}
+
+PJ_DEF(pj_color_t) pj_log_get_color(int level)
+{
+    switch (level) {
+	case 0:
+	    return PJ_LOG_COLOR_0;
+	case 1:
+	    return PJ_LOG_COLOR_1;
+	case 2:
+	    return PJ_LOG_COLOR_2;
+	case 3:
+	    return PJ_LOG_COLOR_3;
+	case 4:
+	    return PJ_LOG_COLOR_4;
+	case 5:
+	    return PJ_LOG_COLOR_5;
+	case 6:
+	    return PJ_LOG_COLOR_6;
+	default:
+	    /* Return default terminal color */
+	    return PJ_LOG_COLOR_77;
+    }
+}
+
 PJ_DEF(void) pj_log_set_level(int level)
 {
     pj_log_max_level = level;
@@ -105,7 +183,7 @@
 	pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
     }
     if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
-	*pre++ = ' ';
+	*pre++ = '-';
 	pre += pj_utoa_pad(ptime.day, pre, 2, '0');
     }
     if (log_decor & PJ_LOG_HAS_TIME) {
@@ -139,6 +217,10 @@
     if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
 	*pre++ = ' ';
 
+    if (log_decor & PJ_LOG_HAS_SPACE) {
+	*pre++ = ' ';
+    }
+
     len = pre - log_buffer;
 
     /* Print the whole message to the string log_buffer. */
diff --git a/pjlib/src/pj/log_writer_stdout.c b/pjlib/src/pj/log_writer_stdout.c
index 9bffe46..e14b2df 100644
--- a/pjlib/src/pj/log_writer_stdout.c
+++ b/pjlib/src/pj/log_writer_stdout.c
@@ -20,37 +20,19 @@
 #include <pj/os.h>
 #include <pj/compat/stdfileio.h>
 
-#define CLR_FATAL    (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R)
-#define CLR_WARNING  (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G)
-#define CLR_INFO     (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | \
-		      PJ_TERM_COLOR_B)
-#define CLR_DEFAULT  (PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B)
 
 static void term_set_color(int level)
 {
 #if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
-    unsigned attr = 0;
-    switch (level) {
-    case 0:
-    case 1: attr = CLR_FATAL; 
-	break;
-    case 2: attr = CLR_WARNING; 
-	break;
-    case 3: attr = CLR_INFO; 
-	break;
-    default:
-	attr = CLR_DEFAULT;
-	break;
-    }
-
-    pj_term_set_color(attr);
+    pj_term_set_color(pj_log_get_color(level));
 #endif
 }
 
 static void term_restore_color(void)
 {
 #if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
-    pj_term_set_color(CLR_DEFAULT);
+    /* Set terminal to its default color */
+    pj_term_set_color(pj_log_get_color(77));
 #endif
 }
 
@@ -61,8 +43,12 @@
     PJ_UNUSED_ARG(len);
 
     /* Copy to terminal/file. */
-    term_set_color(level);
-    printf("%s", buffer);
-    term_restore_color();
+    if (pj_log_get_decor() & PJ_LOG_HAS_COLOR) {
+	term_set_color(level);
+	printf("%s", buffer);
+	term_restore_color();
+    } else {
+	printf("%s", buffer);
+    }
 }
 
diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c
index d6ce78e..006f2a0 100644
--- a/pjlib/src/pj/os_core_unix.c
+++ b/pjlib/src/pj/os_core_unix.c
@@ -1689,8 +1689,57 @@
  */
 PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
 {
-    PJ_UNUSED_ARG(color);
-    return PJ_EINVALIDOP;
+    /* put bright prefix to ansi_color */
+    char ansi_color[12] = "\033[01;3";
+
+    if (color & PJ_TERM_COLOR_BRIGHT) {
+	color ^= PJ_TERM_COLOR_BRIGHT;
+    } else {
+	strcpy(ansi_color, "\033[00;3");
+    }
+
+    switch (color) {
+    case 0:
+	/* black color */
+	strcat(ansi_color, "0m");
+	break;
+    case PJ_TERM_COLOR_R:
+	/* red color */
+	strcat(ansi_color, "1m");
+	break;
+    case PJ_TERM_COLOR_G:
+	/* green color */
+	strcat(ansi_color, "2m");
+	break;
+    case PJ_TERM_COLOR_B:
+	/* blue color */
+	strcat(ansi_color, "4m");
+	break;
+    case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G:
+	/* yellow color */
+	strcat(ansi_color, "3m");
+	break;
+    case PJ_TERM_COLOR_R | PJ_TERM_COLOR_B:
+	/* magenta color */
+	strcat(ansi_color, "5m");
+	break;
+    case PJ_TERM_COLOR_G | PJ_TERM_COLOR_B:
+	/* cyan color */
+	strcat(ansi_color, "6m");
+	break;
+    case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B:
+	/* white color */
+	strcat(ansi_color, "7m");
+	break;
+    default:
+	/* default console color */
+	strcpy(ansi_color, "\033[00m");
+	break;
+    }
+
+    fputs(ansi_color, stdout);
+
+    return PJ_SUCCESS;
 }
 
 /**