mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-11 03:46:17 +01:00
gmessages: Add colour output support to structured log messages
If outputting to a terminal which supports coloured output (rather than, for example, redirecting to a file). This is only enabled for structured log messages, where colour output support can be tested. It is not enabled for non-structured log messages. https://bugzilla.gnome.org/show_bug.cgi?id=744456
This commit is contained in:
parent
052eaf24f7
commit
37ef301684
121
glib/gmessages.c
121
glib/gmessages.c
@ -82,7 +82,7 @@
|
|||||||
* its log writer function so that, for example, it can use the correct
|
* its log writer function so that, for example, it can use the correct
|
||||||
* server connection to submit logs to, that user data can be passed as a
|
* server connection to submit logs to, that user data can be passed as a
|
||||||
* zero-length #GLogField to g_log_structured_array().
|
* zero-length #GLogField to g_log_structured_array().
|
||||||
* * Colour output needed to be supported on the terminal, to make reading
|
* * Color output needed to be supported on the terminal, to make reading
|
||||||
* through logs easier.
|
* through logs easier.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -969,48 +969,58 @@ format_unsigned (gchar *buf,
|
|||||||
/* these are filtered by G_MESSAGES_DEBUG by the default log handler */
|
/* these are filtered by G_MESSAGES_DEBUG by the default log handler */
|
||||||
#define INFO_LEVELS (G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG)
|
#define INFO_LEVELS (G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG)
|
||||||
|
|
||||||
|
static const gchar *log_level_to_color (GLogLevelFlags log_level,
|
||||||
|
gboolean use_color);
|
||||||
|
static const gchar *color_reset (gboolean use_color);
|
||||||
|
|
||||||
static FILE *
|
static FILE *
|
||||||
mklevel_prefix (gchar level_prefix[STRING_BUFFER_SIZE],
|
mklevel_prefix (gchar level_prefix[STRING_BUFFER_SIZE],
|
||||||
GLogLevelFlags log_level)
|
GLogLevelFlags log_level,
|
||||||
|
gboolean use_color)
|
||||||
{
|
{
|
||||||
gboolean to_stdout = TRUE;
|
gboolean to_stdout = TRUE;
|
||||||
|
|
||||||
/* we may not call _any_ GLib functions here */
|
/* we may not call _any_ GLib functions here */
|
||||||
|
|
||||||
|
strcpy (level_prefix, log_level_to_color (log_level, use_color));
|
||||||
|
|
||||||
switch (log_level & G_LOG_LEVEL_MASK)
|
switch (log_level & G_LOG_LEVEL_MASK)
|
||||||
{
|
{
|
||||||
case G_LOG_LEVEL_ERROR:
|
case G_LOG_LEVEL_ERROR:
|
||||||
strcpy (level_prefix, "ERROR");
|
strcat (level_prefix, "ERROR");
|
||||||
to_stdout = FALSE;
|
to_stdout = FALSE;
|
||||||
break;
|
break;
|
||||||
case G_LOG_LEVEL_CRITICAL:
|
case G_LOG_LEVEL_CRITICAL:
|
||||||
strcpy (level_prefix, "CRITICAL");
|
strcat (level_prefix, "CRITICAL");
|
||||||
to_stdout = FALSE;
|
to_stdout = FALSE;
|
||||||
break;
|
break;
|
||||||
case G_LOG_LEVEL_WARNING:
|
case G_LOG_LEVEL_WARNING:
|
||||||
strcpy (level_prefix, "WARNING");
|
strcat (level_prefix, "WARNING");
|
||||||
to_stdout = FALSE;
|
to_stdout = FALSE;
|
||||||
break;
|
break;
|
||||||
case G_LOG_LEVEL_MESSAGE:
|
case G_LOG_LEVEL_MESSAGE:
|
||||||
strcpy (level_prefix, "Message");
|
strcat (level_prefix, "Message");
|
||||||
to_stdout = FALSE;
|
to_stdout = FALSE;
|
||||||
break;
|
break;
|
||||||
case G_LOG_LEVEL_INFO:
|
case G_LOG_LEVEL_INFO:
|
||||||
strcpy (level_prefix, "INFO");
|
strcat (level_prefix, "INFO");
|
||||||
break;
|
break;
|
||||||
case G_LOG_LEVEL_DEBUG:
|
case G_LOG_LEVEL_DEBUG:
|
||||||
strcpy (level_prefix, "DEBUG");
|
strcat (level_prefix, "DEBUG");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (log_level)
|
if (log_level)
|
||||||
{
|
{
|
||||||
strcpy (level_prefix, "LOG-");
|
strcat (level_prefix, "LOG-");
|
||||||
format_unsigned (level_prefix + 4, log_level & G_LOG_LEVEL_MASK, 16);
|
format_unsigned (level_prefix + 4, log_level & G_LOG_LEVEL_MASK, 16);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
strcpy (level_prefix, "LOG");
|
strcat (level_prefix, "LOG");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strcat (level_prefix, color_reset (use_color));
|
||||||
|
|
||||||
if (log_level & G_LOG_FLAG_RECURSION)
|
if (log_level & G_LOG_FLAG_RECURSION)
|
||||||
strcat (level_prefix, " (recursed)");
|
strcat (level_prefix, " (recursed)");
|
||||||
if (log_level & ALERT_LEVELS)
|
if (log_level & ALERT_LEVELS)
|
||||||
@ -1097,7 +1107,7 @@ g_logv (const gchar *log_domain,
|
|||||||
gchar level_prefix[STRING_BUFFER_SIZE];
|
gchar level_prefix[STRING_BUFFER_SIZE];
|
||||||
gchar *expected_message;
|
gchar *expected_message;
|
||||||
|
|
||||||
mklevel_prefix (level_prefix, expected->log_level);
|
mklevel_prefix (level_prefix, expected->log_level, FALSE);
|
||||||
expected_message = g_strdup_printf ("Did not see expected message %s-%s: %s",
|
expected_message = g_strdup_printf ("Did not see expected message %s-%s: %s",
|
||||||
expected->log_domain ? expected->log_domain : "**",
|
expected->log_domain ? expected->log_domain : "**",
|
||||||
level_prefix, expected->pattern);
|
level_prefix, expected->pattern);
|
||||||
@ -1242,6 +1252,43 @@ log_level_to_file (GLogLevelFlags log_level)
|
|||||||
return stdout;
|
return stdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const gchar *
|
||||||
|
log_level_to_color (GLogLevelFlags log_level,
|
||||||
|
gboolean use_color)
|
||||||
|
{
|
||||||
|
/* we may not call _any_ GLib functions here */
|
||||||
|
|
||||||
|
if (!use_color)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (log_level & G_LOG_LEVEL_ERROR)
|
||||||
|
return "\033[1;31m";
|
||||||
|
else if (log_level & G_LOG_LEVEL_CRITICAL)
|
||||||
|
return "\033[1;35m";
|
||||||
|
else if (log_level & G_LOG_LEVEL_WARNING)
|
||||||
|
return "\033[1;33m";
|
||||||
|
else if (log_level & G_LOG_LEVEL_MESSAGE)
|
||||||
|
return "\033[1;32m";
|
||||||
|
else if (log_level & G_LOG_LEVEL_INFO)
|
||||||
|
return "\033[1;32m";
|
||||||
|
else if (log_level & G_LOG_LEVEL_DEBUG)
|
||||||
|
return "\033[1;32m";
|
||||||
|
|
||||||
|
/* No color for custom log levels. */
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const gchar *
|
||||||
|
color_reset (gboolean use_color)
|
||||||
|
{
|
||||||
|
/* we may not call _any_ GLib functions here */
|
||||||
|
|
||||||
|
if (!use_color)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return "\033[0m";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_log_structured:
|
* g_log_structured:
|
||||||
* @log_domain: log domain, usually %G_LOG_DOMAIN
|
* @log_domain: log domain, usually %G_LOG_DOMAIN
|
||||||
@ -1505,6 +1552,39 @@ g_log_set_writer_func (GLogWriterFunc func,
|
|||||||
g_mutex_unlock (&g_messages_lock);
|
g_mutex_unlock (&g_messages_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_log_writer_supports_color:
|
||||||
|
* @output_fd: output file descriptor to check
|
||||||
|
*
|
||||||
|
* Check whether the given @output_fd file descriptor supports ANSI color
|
||||||
|
* escape sequences. If so, they can safely be used when formatting log
|
||||||
|
* messages.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if ANSI color escapes are supported, %FALSE otherwise
|
||||||
|
* Since: 2.50
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
g_log_writer_supports_color (gint output_fd)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (output_fd >= 0, FALSE);
|
||||||
|
|
||||||
|
/* FIXME: This check could easily be expanded in future to be more robust
|
||||||
|
* against different types of terminal, which still vary in their color
|
||||||
|
* support. cmd.exe on Windows, for example, does not support ANSI colors;
|
||||||
|
* but bash on Windows does.
|
||||||
|
*
|
||||||
|
* On UNIX systems, we probably want to use the functions from terminfo to
|
||||||
|
* work out whether colors are supported.
|
||||||
|
*
|
||||||
|
* Some examples:
|
||||||
|
* - https://github.com/chalk/supports-color/blob/9434c93918301a6b47faa01999482adfbf1b715c/index.js#L61
|
||||||
|
* - http://stackoverflow.com/questions/16755142/how-to-make-win32-console-recognize-ansi-vt100-escape-sequences
|
||||||
|
* - http://blog.mmediasys.com/2010/11/24/we-all-love-colors/
|
||||||
|
* - http://unix.stackexchange.com/questions/198794/where-does-the-term-environment-variable-default-get-set
|
||||||
|
*/
|
||||||
|
return isatty (output_fd);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_log_writer_is_journald:
|
* g_log_writer_is_journald:
|
||||||
* @output_fd: output file descriptor to check
|
* @output_fd: output file descriptor to check
|
||||||
@ -1563,6 +1643,8 @@ static void escape_string (GString *string);
|
|||||||
* @fields: (array length=n_fields): key–value pairs of structured data forming
|
* @fields: (array length=n_fields): key–value pairs of structured data forming
|
||||||
* the log message
|
* the log message
|
||||||
* @n_fields: number of elements in the @fields array
|
* @n_fields: number of elements in the @fields array
|
||||||
|
* @use_color: %TRUE to use ANSI color escape sequences when formatting the
|
||||||
|
* message, %FALSE to not
|
||||||
*
|
*
|
||||||
* Format a structured log message as a string suitable for outputting to the
|
* Format a structured log message as a string suitable for outputting to the
|
||||||
* terminal (or elsewhere). This will include the values of all fields it knows
|
* terminal (or elsewhere). This will include the values of all fields it knows
|
||||||
@ -1581,7 +1663,8 @@ static void escape_string (GString *string);
|
|||||||
gchar *
|
gchar *
|
||||||
g_log_writer_format_fields (GLogLevelFlags log_level,
|
g_log_writer_format_fields (GLogLevelFlags log_level,
|
||||||
const GLogField *fields,
|
const GLogField *fields,
|
||||||
gsize n_fields)
|
gsize n_fields,
|
||||||
|
gboolean use_color)
|
||||||
{
|
{
|
||||||
gsize i;
|
gsize i;
|
||||||
const gchar *message = NULL;
|
const gchar *message = NULL;
|
||||||
@ -1601,7 +1684,7 @@ g_log_writer_format_fields (GLogLevelFlags log_level,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Format things. */
|
/* Format things. */
|
||||||
mklevel_prefix (level_prefix, log_level);
|
mklevel_prefix (level_prefix, log_level, use_color);
|
||||||
|
|
||||||
gstring = g_string_new (NULL);
|
gstring = g_string_new (NULL);
|
||||||
if (log_level & ALERT_LEVELS)
|
if (log_level & ALERT_LEVELS)
|
||||||
@ -1751,6 +1834,9 @@ g_log_writer_journald (GLogLevelFlags log_level,
|
|||||||
* which are understood by this function are included in the formatted string
|
* which are understood by this function are included in the formatted string
|
||||||
* which is printed.
|
* which is printed.
|
||||||
*
|
*
|
||||||
|
* If the output stream supports ANSI color escape sequences, they will be used
|
||||||
|
* in the output.
|
||||||
|
*
|
||||||
* A trailing new-line character is added to the log message when it is printed.
|
* A trailing new-line character is added to the log message when it is printed.
|
||||||
*
|
*
|
||||||
* This is suitable for use as a #GLogWriterFunc.
|
* This is suitable for use as a #GLogWriterFunc.
|
||||||
@ -1771,7 +1857,8 @@ g_log_writer_standard_streams (GLogLevelFlags log_level,
|
|||||||
g_return_val_if_fail (n_fields > 0, G_LOG_WRITER_UNHANDLED);
|
g_return_val_if_fail (n_fields > 0, G_LOG_WRITER_UNHANDLED);
|
||||||
|
|
||||||
stream = log_level_to_file (log_level);
|
stream = log_level_to_file (log_level);
|
||||||
out = g_log_writer_format_fields (log_level, fields, n_fields);
|
out = g_log_writer_format_fields (log_level, fields, n_fields,
|
||||||
|
g_log_writer_supports_color (fileno (stream)));
|
||||||
_g_fprintf (stream, "%s\n", out);
|
_g_fprintf (stream, "%s\n", out);
|
||||||
g_free (out);
|
g_free (out);
|
||||||
|
|
||||||
@ -2081,7 +2168,7 @@ g_test_assert_expected_messages_internal (const char *domain,
|
|||||||
|
|
||||||
expected = expected_messages->data;
|
expected = expected_messages->data;
|
||||||
|
|
||||||
mklevel_prefix (level_prefix, expected->log_level);
|
mklevel_prefix (level_prefix, expected->log_level, FALSE);
|
||||||
message = g_strdup_printf ("Did not see expected message %s-%s: %s",
|
message = g_strdup_printf ("Did not see expected message %s-%s: %s",
|
||||||
expected->log_domain ? expected->log_domain : "**",
|
expected->log_domain ? expected->log_domain : "**",
|
||||||
level_prefix, expected->pattern);
|
level_prefix, expected->pattern);
|
||||||
@ -2121,7 +2208,7 @@ _g_log_fallback_handler (const gchar *log_domain,
|
|||||||
* the process ID unconditionally however.
|
* the process ID unconditionally however.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
stream = mklevel_prefix (level_prefix, log_level);
|
stream = mklevel_prefix (level_prefix, log_level, FALSE);
|
||||||
if (!message)
|
if (!message)
|
||||||
message = "(NULL) message";
|
message = "(NULL) message";
|
||||||
|
|
||||||
@ -2269,7 +2356,7 @@ g_log_default_handler (const gchar *log_domain,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream = mklevel_prefix (level_prefix, log_level);
|
stream = mklevel_prefix (level_prefix, log_level, FALSE);
|
||||||
|
|
||||||
gstring = g_string_new (NULL);
|
gstring = g_string_new (NULL);
|
||||||
if (log_level & ALERT_LEVELS)
|
if (log_level & ALERT_LEVELS)
|
||||||
|
@ -204,13 +204,16 @@ void g_log_set_writer_func (GLogWriterFunc func,
|
|||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GDestroyNotify user_data_free);
|
GDestroyNotify user_data_free);
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_50
|
||||||
|
gboolean g_log_writer_supports_color (gint output_fd);
|
||||||
GLIB_AVAILABLE_IN_2_50
|
GLIB_AVAILABLE_IN_2_50
|
||||||
gboolean g_log_writer_is_journald (gint output_fd);
|
gboolean g_log_writer_is_journald (gint output_fd);
|
||||||
|
|
||||||
GLIB_AVAILABLE_IN_2_50
|
GLIB_AVAILABLE_IN_2_50
|
||||||
gchar *g_log_writer_format_fields (GLogLevelFlags log_level,
|
gchar *g_log_writer_format_fields (GLogLevelFlags log_level,
|
||||||
const GLogField *fields,
|
const GLogField *fields,
|
||||||
gsize n_fields);
|
gsize n_fields,
|
||||||
|
gboolean use_color);
|
||||||
|
|
||||||
GLIB_AVAILABLE_IN_2_50
|
GLIB_AVAILABLE_IN_2_50
|
||||||
GLogWriterOutput g_log_writer_journald (GLogLevelFlags log_level,
|
GLogWriterOutput g_log_writer_journald (GLogLevelFlags log_level,
|
||||||
|
Loading…
Reference in New Issue
Block a user