Add g_log_variant(): structured log that accepts a GVariant

This makes the structured logging available to other
languages via introspection.

https://bugzilla.gnome.org/show_bug.cgi?id=770971
This commit is contained in:
Jonh Wendell 2016-09-09 09:06:05 -03:00
parent 9323378834
commit e7bdd5d189
4 changed files with 173 additions and 0 deletions

View File

@ -1177,6 +1177,7 @@ g_log_set_default_handler
<SUBSECTION>
g_log_structured
g_log_variant
GLogField
g_log_structured_array
G_DEBUG_HERE

View File

@ -1602,6 +1602,116 @@ g_log_structured (const gchar *log_domain,
va_end (args);
}
/**
* g_log_variant:
* @log_domain: log domain, usually %G_LOG_DOMAIN
* @log_level: log level, either from #GLogLevelFlags, or a user-defined
* level
* @fields: a dictionary (#GVariant of the type %G_VARIANT_TYPE_VARDICT)
* containing the key-value pairs of message data.
*
* Log a message with structured data, accepting the data within a #GVariant. This
* version is especially useful for use in other languages, via introspection.
*
* The only mandatory item in the @fields dictionary is the "MESSAGE" which must
* contain the text shown to the user.
*
* The values in the @fields dictionary are likely to be of type String
* (#G_VARIANT_TYPE_STRING). Array of bytes (#G_VARIANT_TYPE_BYTESTRING) is also
* supported. In this case the message is handled as binary and will be forwarded
* to the log writer as such. The size of the array should not be higher than
* %G_MAXSSIZE. Otherwise it will be truncated to this size. For other types
* g_variant_print() will be used to convert the value into a string.
*
* For more details on its usage and about the parameters, see g_log_structured().
*
* Since: 2.50
*/
void
g_log_variant (const gchar *log_domain,
GLogLevelFlags log_level,
GVariant *fields)
{
GVariantIter iter;
GVariant *value;
gchar *key;
GArray *fields_array;
GLogField field;
GSList *values_list, *print_list;
g_return_if_fail (g_variant_is_of_type (fields, G_VARIANT_TYPE_VARDICT));
values_list = print_list = NULL;
fields_array = g_array_new (FALSE, FALSE, sizeof (GLogField));
field.key = "PRIORITY";
field.value = log_level_to_priority (log_level);
field.length = -1;
g_array_append_val (fields_array, field);
if (log_domain)
{
field.key = "GLIB_DOMAIN";
field.value = log_domain;
field.length = -1;
g_array_append_val (fields_array, field);
}
g_variant_iter_init (&iter, fields);
while (g_variant_iter_next (&iter, "{&sv}", &key, &value))
{
gboolean defer_unref = TRUE;
field.key = key;
field.length = -1;
if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
{
field.value = g_variant_get_string (value, NULL);
}
else if (g_variant_is_of_type (value, G_VARIANT_TYPE_BYTESTRING))
{
gsize s;
field.value = g_variant_get_fixed_array (value, &s, sizeof (guchar));
if (G_LIKELY (s <= G_MAXSSIZE))
{
field.length = s;
}
else
{
_g_fprintf (stderr,
"Byte array too large (%" G_GSIZE_FORMAT " bytes)"
" passed to g_log_variant(). Truncating to " G_STRINGIFY (G_MAXSSIZE)
" bytes.", s);
field.length = G_MAXSSIZE;
}
}
else
{
char *s = g_variant_print (value, FALSE);
field.value = s;
print_list = g_slist_prepend (print_list, s);
defer_unref = FALSE;
}
g_array_append_val (fields_array, field);
if (G_LIKELY (defer_unref))
values_list = g_slist_prepend (values_list, value);
else
g_variant_unref (value);
}
/* Log it. */
g_log_structured_array (log_level, (GLogField *) fields_array->data, fields_array->len);
g_array_free (fields_array, TRUE);
g_slist_free_full (values_list, (GDestroyNotify) g_variant_unref);
g_slist_free_full (print_list, g_free);
}
#pragma GCC diagnostic pop
static GLogWriterOutput _g_log_writer_fallback (GLogLevelFlags log_level,

View File

@ -32,6 +32,7 @@
#include <stdarg.h>
#include <glib/gtypes.h>
#include <glib/gmacros.h>
#include <glib/gvariant.h>
G_BEGIN_DECLS
@ -196,6 +197,11 @@ void g_log_structured_array (GLogLevelFlags log_level,
const GLogField *fields,
gsize n_fields);
GLIB_AVAILABLE_IN_2_50
void g_log_variant (const gchar *log_domain,
GLogLevelFlags log_level,
GVariant *fields);
GLIB_AVAILABLE_IN_2_50
void g_log_set_writer_func (GLogWriterFunc func,
gpointer user_data,

View File

@ -345,6 +345,8 @@ compare_fields (const GLogField *f1, gsize n1, const GLogField *f2, gsize n2)
}
static GSList *expected_messages = NULL;
static const guchar binary_field[] = {1, 2, 3, 4, 5};
static GLogWriterOutput
expect_log_writer (GLogLevelFlags log_level,
@ -500,6 +502,58 @@ test_structured_logging_roundtrip3 (void)
g_assert (expected_messages == NULL);
}
static GVariant *
create_variant_fields (void)
{
GVariant *binary;
GVariantBuilder builder;
binary = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, binary_field, G_N_ELEMENTS (binary_field), sizeof (binary_field[0]));
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&builder, "{sv}", "MESSAGE_ID", g_variant_new_string ("06d4df59e6c24647bfe69d2c27ef0b4e"));
g_variant_builder_add (&builder, "{sv}", "MESSAGE", g_variant_new_string ("This is a debug message"));
g_variant_builder_add (&builder, "{sv}", "MY_APPLICATION_CUSTOM_FIELD", g_variant_new_string ("some debug string"));
g_variant_builder_add (&builder, "{sv}", "MY_APPLICATION_CUSTOM_FIELD_BINARY", binary);
return g_variant_builder_end (&builder);
}
static void
test_structured_logging_variant1 (void)
{
GVariant *v = create_variant_fields ();
log_count = 0;
g_log_set_writer_func (null_log_writer, NULL, NULL);
g_log_variant ("some-domain", G_LOG_LEVEL_MESSAGE, v);
g_variant_unref (v);
g_assert_cmpint (log_count, ==, 1);
}
static void
test_structured_logging_variant2 (void)
{
const GLogField fields[] = {
{ "GLIB_DOMAIN", "some-domain", -1 },
{ "PRIORITY", "5", -1 },
{ "MESSAGE", "This is a debug message", -1 },
{ "MESSAGE_ID", "06d4df59e6c24647bfe69d2c27ef0b4e", -1 },
{ "MY_APPLICATION_CUSTOM_FIELD", "some debug string", -1 },
{ "MY_APPLICATION_CUSTOM_FIELD_BINARY", binary_field, sizeof (binary_field) }
};
ExpectedMessage expected = { fields, 6 };
GVariant *v = create_variant_fields ();
expected_messages = g_slist_append (NULL, &expected);
g_log_set_writer_func (expect_log_writer, NULL, NULL);
g_log_variant ("some-domain", G_LOG_LEVEL_MESSAGE, v);
g_variant_unref (v);
g_assert (expected_messages == NULL);
}
int
main (int argc, char *argv[])
{
@ -531,6 +585,8 @@ main (int argc, char *argv[])
g_test_add_func ("/structured-logging/roundtrip1", test_structured_logging_roundtrip1);
g_test_add_func ("/structured-logging/roundtrip2", test_structured_logging_roundtrip2);
g_test_add_func ("/structured-logging/roundtrip3", test_structured_logging_roundtrip3);
g_test_add_func ("/structured-logging/variant1", test_structured_logging_variant1);
g_test_add_func ("/structured-logging/variant2", test_structured_logging_variant2);
return g_test_run ();
}