gstinfo: Add new API for getting debug log lines

If you're using a custom log handler, you had to reverse-engineer the
debug log format and create your own format function. Now, you can
call `gst_debug_log_get_line()` and it will return a string (without
ANSI escape color codes) representation instead.

This is useful in situations when you need to log the ordinary
gst_debug log to a resource that can't be opened as a `FILE` handle.

Also includes a test.
This commit is contained in:
Nirbheek Chauhan 2019-08-30 00:23:09 +05:30
parent 8140da74ba
commit 72daeee2c4
3 changed files with 126 additions and 30 deletions

View file

@ -1079,6 +1079,7 @@ gst_debug_construct_win_color (guint colorinfo)
#endif
#define PID_FMT "%5d"
#define CAT_FMT "%20s %s:%d:%s:%s"
#define NOCOLOR_PRINT_FMT " "PID_FMT" "PTR_FMT" %s "CAT_FMT" %s\n"
#ifdef G_OS_WIN32
static const guchar levelcolormap_w32[GST_LEVEL_COUNT] = {
@ -1123,6 +1124,81 @@ static const gchar *levelcolormap[GST_LEVEL_COUNT] = {
"\033[37m" /* GST_LEVEL_MEMDUMP */
};
static void
_gst_debug_log_preamble (GstDebugMessage * message, GObject * object,
const gchar ** file, const gchar ** message_str, gchar ** obj_str,
GstClockTime * elapsed)
{
gchar c;
/* Get message string first because printing it might call into our custom
* printf format extension mechanism which in turn might log something, e.g.
* from inside gst_structure_to_string() when something can't be serialised.
* This means we either need to do this outside of any critical section or
* use a recursive lock instead. As we always need the message string in all
* code paths, we might just as well get it here first thing and outside of
* the win_print_mutex critical section. */
*message_str = gst_debug_message_get (message);
/* __FILE__ might be a file name or an absolute path or a
* relative path, irrespective of the exact compiler used,
* in which case we want to shorten it to the filename for
* readability. */
c = (*file)[0];
if (c == '.' || c == '/' || c == '\\' || (c != '\0' && (*file)[1] == ':')) {
*file = gst_path_basename (*file);
}
if (object) {
*obj_str = gst_debug_print_object (object);
} else {
*obj_str = (gchar *) "";
}
*elapsed = GST_CLOCK_DIFF (_priv_gst_start_time, gst_util_get_timestamp ());
}
/**
* gst_debug_log_get_line:
* @category: category to log
* @level: level of the message
* @file: the file that emitted the message, usually the __FILE__ identifier
* @function: the function that emitted the message
* @line: the line from that the message was emitted, usually __LINE__
* @object: (transfer none) (allow-none): the object this message relates to,
* or %NULL if none
* @message: the actual message
*
* Returns the string representation for the specified debug log message
* formatted in the same way as gst_debug_log_default() (the default handler),
* without color. The purpose is to make it easy for custom log output
* handlers to get a log output that is identical to what the default handler
* would write out.
*
* Since: 1.18
*/
gchar *
gst_debug_log_get_line (GstDebugCategory * category, GstDebugLevel level,
const gchar * file, const gchar * function, gint line,
GObject * object, GstDebugMessage * message)
{
GstClockTime elapsed;
gchar *ret, *obj_str = NULL;
const gchar *message_str;
_gst_debug_log_preamble (message, object, &file, &message_str, &obj_str,
&elapsed);
ret = g_strdup_printf ("%" GST_TIME_FORMAT NOCOLOR_PRINT_FMT,
GST_TIME_ARGS (elapsed), getpid (), g_thread_self (),
gst_debug_level_get_name (level), gst_debug_category_get_name
(category), file, line, function, obj_str, message_str);
if (object != NULL)
g_free (obj_str);
return ret;
}
/**
* gst_debug_log_default:
* @category: category to log
@ -1156,37 +1232,13 @@ gst_debug_log_default (GstDebugCategory * category, GstDebugLevel level,
GstDebugColorMode color_mode;
const gchar *message_str;
FILE *log_file = user_data ? user_data : stderr;
gchar c;
/* Get message string first because printing it might call into our custom
* printf format extension mechanism which in turn might log something, e.g.
* from inside gst_structure_to_string() when something can't be serialised.
* This means we either need to do this outside of any critical section or
* use a recursive lock instead. As we always need the message string in all
* code paths, we might just as well get it here first thing and outside of
* the win_print_mutex critical section. */
message_str = gst_debug_message_get (message);
/* __FILE__ might be a file name or an absolute path or a
* relative path, irrespective of the exact compiler used,
* in which case we want to shorten it to the filename for
* readability. */
c = file[0];
if (c == '.' || c == '/' || c == '\\' || (c != '\0' && file[1] == ':')) {
file = gst_path_basename (file);
}
_gst_debug_log_preamble (message, object, &file, &message_str, &obj,
&elapsed);
pid = getpid ();
color_mode = gst_debug_get_color_mode ();
if (object) {
obj = gst_debug_print_object (object);
} else {
obj = (gchar *) "";
}
elapsed = GST_CLOCK_DIFF (_priv_gst_start_time, gst_util_get_timestamp ());
if (color_mode != GST_DEBUG_COLOR_MODE_OFF) {
#ifdef G_OS_WIN32
G_LOCK (win_print_mutex);
@ -1251,13 +1303,11 @@ gst_debug_log_default (GstDebugCategory * category, GstDebugLevel level,
#endif
} else {
/* no color, all platforms */
#define PRINT_FMT " "PID_FMT" "PTR_FMT" %s "CAT_FMT" %s\n"
fprintf (log_file, "%" GST_TIME_FORMAT PRINT_FMT, GST_TIME_ARGS (elapsed),
pid, g_thread_self (), gst_debug_level_get_name (level),
fprintf (log_file, "%" GST_TIME_FORMAT NOCOLOR_PRINT_FMT, GST_TIME_ARGS
(elapsed), pid, g_thread_self (), gst_debug_level_get_name (level),
gst_debug_category_get_name (category), file, line, function, obj,
message_str);
fflush (log_file);
#undef PRINT_FMT
}
if (object != NULL)

View file

@ -388,6 +388,15 @@ const gchar *
GST_API
const gchar * gst_debug_message_get (GstDebugMessage * message);
GST_API
gchar * gst_debug_log_get_line (GstDebugCategory * category,
GstDebugLevel level,
const gchar * file,
const gchar * function,
gint line,
GObject * object,
GstDebugMessage * message) G_GNUC_NO_INSTRUMENT;
GST_API
void gst_debug_log_default (GstDebugCategory * category,
GstDebugLevel level,
@ -397,6 +406,7 @@ void gst_debug_log_default (GstDebugCategory * category,
GObject * object,
GstDebugMessage * message,
gpointer user_data) G_GNUC_NO_INSTRUMENT;
GST_API
const gchar * gst_debug_level_get_name (GstDebugLevel level);

View file

@ -228,6 +228,41 @@ GST_START_TEST (info_log_handler)
GST_END_TEST;
static void
compare_gst_log_func (GstDebugCategory * category, GstDebugLevel level,
const gchar * file, const gchar * function, gint line, GObject * object,
GstDebugMessage * message, gpointer user_data)
{
gboolean match;
gchar *log_line;
fail_unless_equals_pointer (user_data, NULL);
log_line = gst_debug_log_get_line (category, level, file, function, line,
object, message);
match = g_pattern_match_simple ("*:*:*.*0x*DEBUG*check*gstinfo.c:*"
":info_log_handler_get_line: test message\n", log_line);
fail_unless_equals_int (match, TRUE);
g_free (log_line);
}
GST_START_TEST (info_log_handler_get_line)
{
gst_debug_remove_log_function (gst_debug_log_default);
gst_debug_add_log_function (compare_gst_log_func, NULL, NULL);
gst_debug_set_default_threshold (GST_LEVEL_LOG);
GST_DEBUG ("test message");
/* clean up */
gst_debug_set_default_threshold (GST_LEVEL_NONE);
gst_debug_add_log_function (gst_debug_log_default, NULL, NULL);
gst_debug_remove_log_function (compare_gst_log_func);
}
GST_END_TEST;
GST_START_TEST (info_dump_mem)
{
GstDebugCategory *cat = NULL;
@ -498,6 +533,7 @@ gst_info_suite (void)
tcase_add_test (tc_chain, info_segment_format_printf_extension);
tcase_add_test (tc_chain, info_ptr_format_printf_extension);
tcase_add_test (tc_chain, info_log_handler);
tcase_add_test (tc_chain, info_log_handler_get_line);
tcase_add_test (tc_chain, info_dump_mem);
tcase_add_test (tc_chain, info_fixme);
tcase_add_test (tc_chain, info_old_printf_extensions);