[glib] Redo structured logging API
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] Redo structured logging API
- Date: Thu, 21 Jul 2016 00:42:40 +0000 (UTC)
commit bdcf9e8b4efb16ee6d00dc0ce04349cd01a5f0e4
Author: Matthias Clasen <mclasen redhat com>
Date: Wed Jul 20 18:36:52 2016 -0400
Redo structured logging API
It turns out that the current approach of parsing g_log_structured
varargs is unworkable, because vprintf is not guaranteed to advance
the passed-in va_list. So, we have to reshuffle the argument list
a bit; I've come up with this approach:
g_log_structured (domain, level,
key-value pairs...
"MESSAGE", format,
printf arguments);
This requires a "MESSAGE" key to always be present, and it requires
the "MESSAGE"-format pair to be last, but it avoids an extra NULL
as marker after the key-value pairs. And it can be parsed with a
single pass over the va_list, without any va_copy.
Since we have G_LOG_USE_STRUCTURED, the separate ...structured()
convenience macros are pretty pointless, and I have dropped them
for now.
glib/gmessages.c | 111 +++++++++++++++++---------------
glib/gmessages.h | 186 ++++++-----------------------------------------------
2 files changed, 80 insertions(+), 217 deletions(-)
---
diff --git a/glib/gmessages.c b/glib/gmessages.c
index 55a25a4..f05d74b 100644
--- a/glib/gmessages.c
+++ b/glib/gmessages.c
@@ -1295,10 +1295,9 @@ color_reset (gboolean use_color)
* @log_domain: log domain, usually %G_LOG_DOMAIN
* @log_level: log level, either from #GLogLevelFlags, or a user-defined
* level
- * @format: message format, in printf() style
- * @...: parameters to insert into the format string, followed by key–value
- * pairs of structured data to add to the log message, terminated with a
- * %NULL
+ * @...: key-value pairs of structured data to add to the log entry, followed
+ * by the key "MESSAGE", followed by a printf()-style message format,
+ * followed by parameters to insert in the format string
*
* Log a message with structured data. The message will be passed through to the
* log writer set by the application using g_log_set_writer_func(). If the
@@ -1335,11 +1334,10 @@ color_reset (gboolean use_color)
* For example:
* ```
* g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
- * "This is a debug message about pointer %p and integer %u.",
- * some_pointer, some_integer,
* "MESSAGE_ID", "06d4df59e6c24647bfe69d2c27ef0b4e",
* "MY_APPLICATION_CUSTOM_FIELD", "some debug string",
- * NULL);
+ * "MESSAGE", "This is a debug message about pointer %p and integer %u.",
+ * some_pointer, some_integer);
* ```
*
* Note that each `MESSAGE_ID` **must** be [uniquely and randomly
@@ -1364,8 +1362,8 @@ color_reset (gboolean use_color)
* g_log_structured_array (G_LOG_LEVEL_DEBUG, fields, G_N_ELEMENTS (fields));
* ```
*
- * Note also that, even if no structured fields are specified, the argument list
- * **must** be %NULL-terminated.
+ * Note also that, even if no structured fields are specified, the key-value part
+ * of the argument list **must** be %NULL-terminated.
*
* The default writer function for `stdout` and `stderr` will automatically
* append a new-line character to the @format, so you should not add one
@@ -1376,18 +1374,60 @@ color_reset (gboolean use_color)
void
g_log_structured (const gchar *log_domain,
GLogLevelFlags log_level,
- const gchar *format,
...)
{
- va_list args, field_args;
+ va_list args;
gchar buffer[1025], *message_allocated = NULL;
- const gchar *message, *priority;
+ const char *format;
+ const gchar *message;
gpointer p;
gsize n_fields, i;
- GLogField *fields = NULL;
+ GLogField stack_fields[16];
+ GLogField *fields = stack_fields;
+ GLogField *fields_allocated = NULL;
+ GArray *array = NULL;
- /* Format the message. */
- va_start (args, format);
+ va_start (args, log_level);
+
+ for (p = va_arg (args, gchar *), i = 3;
+ strcmp (p, "MESSAGE") != 0;
+ p = va_arg (args, gchar *), i++)
+ {
+ GLogField field;
+ const gchar *key = p;
+ gconstpointer value = va_arg (args, gpointer);
+
+ field.key = key;
+ field.value = value;
+ field.length = -1;
+
+ if (i < 16)
+ stack_fields[i] = field;
+ else
+ {
+ /* Don't allow dynamic allocation, since we're likely
+ * in an out-of-memory situation. For lack of a better solution,
+ * just ignore further key-value pairs.
+ */
+ if (log_level & G_LOG_FLAG_RECURSION)
+ continue;
+
+ if (i == 16)
+ {
+ array = g_array_sized_new (FALSE, FALSE, sizeof (GLogField), 32);
+ g_array_append_vals (array, stack_fields, 16);
+ }
+
+ g_array_append_val (array, field);
+ }
+ }
+
+ n_fields = i;
+
+ if (array)
+ fields = fields_allocated = (GLogField *) g_array_free (array, FALSE);
+
+ format = va_arg (args, gchar *);
if (log_level & G_LOG_FLAG_RECURSION)
{
@@ -1404,58 +1444,25 @@ g_log_structured (const gchar *log_domain,
message = message_allocated = g_strdup_vprintf (format, args);
}
- /* Format the priority. */
- priority = log_level_to_priority (log_level);
-
- /* Work out how many fields we have. */
- G_VA_COPY (field_args, args);
-
- for (p = va_arg (args, gchar *), n_fields = 0;
- p != NULL;
- p = va_arg (args, gchar *), n_fields++)
- va_arg (args, gpointer);
-
/* Add MESSAGE, PRIORITY and GLIB_DOMAIN. */
- n_fields += 3;
-
- /* Build the fields array. */
- fields = g_alloca (sizeof (GLogField) * n_fields);
-
fields[0].key = "MESSAGE";
fields[0].value = message;
fields[0].length = -1;
fields[1].key = "PRIORITY";
- fields[1].value = priority;
- fields[1].length = 1; /* byte */
+ fields[1].value = log_level_to_priority (log_level);
+ fields[1].length = 1;
fields[2].key = "GLIB_DOMAIN";
fields[2].value = log_domain;
fields[2].length = -1;
- for (p = va_arg (field_args, gchar *), i = 3;
- p != NULL;
- p = va_arg (field_args, gchar *), i++)
- {
- GLogField *field = &fields[i];
- const gchar *key = p;
- gconstpointer value = va_arg (field_args, gpointer);
-
- /* These are already provided as @format, @log_level and @log_domain. */
- g_warn_if_fail (g_strcmp0 (key, "MESSAGE") != 0);
- g_warn_if_fail (g_strcmp0 (key, "PRIORITY") != 0);
- g_warn_if_fail (g_strcmp0 (key, "GLIB_DOMAIN") != 0);
-
- field->key = key;
- field->value = value;
- field->length = -1;
- }
-
/* Log it. */
g_log_structured_array (log_level, fields, n_fields);
+ g_free (fields_allocated);
g_free (message_allocated);
- va_end (field_args);
+
va_end (args);
}
diff --git a/glib/gmessages.h b/glib/gmessages.h
index ce28c8f..7471e4e 100644
--- a/glib/gmessages.h
+++ b/glib/gmessages.h
@@ -190,10 +190,7 @@ typedef GLogWriterOutput (*GLogWriterFunc) (GLogLevelFlags log_level,
GLIB_AVAILABLE_IN_2_50
void g_log_structured (const gchar *log_domain,
GLogLevelFlags log_level,
- const gchar *format,
- ...) G_GNUC_PRINTF (3, 0)
- G_GNUC_NULL_TERMINATED;
-
+ ...);
GLIB_AVAILABLE_IN_2_50
void g_log_structured_array (GLogLevelFlags log_level,
const GLogField *fields,
@@ -234,148 +231,19 @@ GLogWriterOutput g_log_writer_default (GLogLevelFlags log_level,
/**
* G_DEBUG_HERE:
*
- * A convenience form of g_debug_structured(), recommended to be added to
+ * A convenience form of g_log_structured(), recommended to be added to
* functions when debugging. It prints the current monotonic time and the code
* location using %G_STRLOC.
*
* Since: 2.50
*/
-#define G_DEBUG_HERE() \
- g_debug_structured ("%" G_GINT64_FORMAT ": %s", g_get_monotonic_time (), \
- G_STRLOC)
-
-/**
- * g_debug_structured:
- * @format: message format, in printf() style
- * @...: parameters to insert into the format string
- *
- * Convenience wrapper around g_log_structured() to output a log message at
- * %G_LOG_LEVEL_DEBUG in %G_LOG_DOMAIN with the given message and the
- * `CODE_FILE`, `CODE_LINE` and `CODE_FUNC` fields added automatically. As this
- * macro uses `__LINE__`, it cannot be wrapped in a helper function.
- *
- * The provided structured fields may change in future.
- *
- * Since: 2.50
- */
-#define g_debug_structured(...) \
- g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
- __VA_ARGS__, \
- "CODE_FILE", __FILE__, \
- "CODE_LINE", G_STRINGIFY (__LINE__), \
- "CODE_FUNC", __func__, \
- NULL)
-
-/**
- * g_info_structured:
- * @format: message format, in printf() style
- * @...: parameters to insert into the format string
- *
- * Convenience wrapper around g_log_structured() to output a log message at
- * %G_LOG_LEVEL_INFO in %G_LOG_DOMAIN with the given message and the
- * `CODE_FILE`, `CODE_LINE` and `CODE_FUNC` fields added automatically. As this
- * macro uses `__LINE__`, it cannot be wrapped in a helper function.
- *
- * The provided structured fields may change in future.
- *
- * Since: 2.50
- */
-#define g_info_structured(...) \
- g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
- __VA_ARGS__, \
- "CODE_FILE", __FILE__, \
- "CODE_LINE", G_STRINGIFY (__LINE__), \
- "CODE_FUNC", __func__, \
- NULL)
-
-/**
- * g_message_structured:
- * @format: message format, in printf() style
- * @...: parameters to insert into the format string
- *
- * Convenience wrapper around g_log_structured() to output a log message at
- * %G_LOG_LEVEL_MESSAGE in %G_LOG_DOMAIN with the given message and the
- * `CODE_FILE`, `CODE_LINE` and `CODE_FUNC` fields added automatically. As this
- * macro uses `__LINE__`, it cannot be wrapped in a helper function.
- *
- * The provided structured fields may change in future.
- *
- * Since: 2.50
- */
-#define g_message_structured(...) \
- g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \
- __VA_ARGS__, \
- "CODE_FILE", __FILE__, \
- "CODE_LINE", G_STRINGIFY (__LINE__), \
- "CODE_FUNC", __func__, \
- NULL)
-
-
-/**
- * g_warning_structured:
- * @format: message format, in printf() style
- * @...: parameters to insert into the format string
- *
- * Convenience wrapper around g_log_structured() to output a log message at
- * %G_LOG_LEVEL_WARNING in %G_LOG_DOMAIN with the given message and the
- * `CODE_FILE`, `CODE_LINE` and `CODE_FUNC` fields added automatically. As this
- * macro uses `__LINE__`, it cannot be wrapped in a helper function.
- *
- * The provided structured fields may change in future.
- *
- * Since: 2.50
- */
-#define g_warning_structured(...) \
- g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
- __VA_ARGS__, \
- "CODE_FILE", __FILE__, \
- "CODE_LINE", G_STRINGIFY (__LINE__), \
- "CODE_FUNC", __func__, \
- NULL)
-
-/**
- * g_critical_structured:
- * @format: message format, in printf() style
- * @...: parameters to insert into the format string
- *
- * Convenience wrapper around g_log_structured() to output a log message at
- * %G_LOG_LEVEL_CRITICAL in %G_LOG_DOMAIN with the given message and the
- * `CODE_FILE`, `CODE_LINE` and `CODE_FUNC` fields added automatically. As this
- * macro uses `__LINE__`, it cannot be wrapped in a helper function.
- *
- * The provided structured fields may change in future.
- *
- * Since: 2.50
- */
-#define g_critical_structured(...) \
- g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
- __VA_ARGS__, \
- "CODE_FILE", __FILE__, \
- "CODE_LINE", G_STRINGIFY (__LINE__), \
- "CODE_FUNC", __func__, \
- NULL)
-
-/**
- * g_error_structured:
- * @format: message format, in printf() style
- * @...: parameters to insert into the format string
- *
- * Convenience wrapper around g_log_structured() to output a log message at
- * %G_LOG_LEVEL_ERROR in %G_LOG_DOMAIN with the given message and the
- * `CODE_FILE`, `CODE_LINE` and `CODE_FUNC` fields added automatically. As this
- * macro uses `__LINE__`, it cannot be wrapped in a helper function.
- *
- * The provided structured fields may change in future.
- *
- * Since: 2.50
- */
-#define g_error_structured(...) \
- g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
- __VA_ARGS__, \
- "CODE_FILE", __FILE__, \
- "CODE_LINE", G_STRINGIFY (__LINE__), \
- "CODE_FUNC", __func__, \
- NULL)
+#define G_DEBUG_HERE() \
+ g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
+ "CODE_FILE", __FILE__, \
+ "CODE_LINE", G_STRINGIFY (__LINE__), \
+ "CODE_FUNC", __func__, \
+ "MESSAGE", "%" G_GINT64_FORMAT ": %s", \
+ g_get_monotonic_time (), G_STRLOC)
/* internal */
void _g_log_fallback_handler (const gchar *log_domain,
@@ -410,43 +278,37 @@ void g_assert_warning (const char *log_domain,
#ifdef G_LOG_USE_STRUCTURED
#define g_error(...) G_STMT_START { \
g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
- __VA_ARGS__, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL); \
+ "MESSAGE", __VA_ARGS__); \
for (;;) ; \
} G_STMT_END
#define g_message(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \
- __VA_ARGS__, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", __VA_ARGS__)
#define g_critical(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
- __VA_ARGS__, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", __VA_ARGS__)
#define g_warning(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
- __VA_ARGS__, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", __VA_ARGS__)
#define g_info(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
- __VA_ARGS__, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", __VA_ARGS__)
#define g_debug(...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
- __VA_ARGS__, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", __VA_ARGS__)
#else
/* for(;;) ; so that GCC knows that control doesn't go past g_error().
* Put space before ending semicolon to avoid C++ build warnings.
@@ -477,43 +339,37 @@ void g_assert_warning (const char *log_domain,
#ifdef G_LOG_USE_STRUCTURED
#define g_error(format...) G_STMT_START { \
g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
- format, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL); \
+ "MESSAGE", format); \
for (;;) ; \
} G_STMT_END
#define g_message(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \
- format, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", format)
#define g_critical(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
- format, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", format)
#define g_warning(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
- format, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", format)
#define g_info(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
- format, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", format)
#define g_debug(format...) g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
- format, \
"CODE_FILE", __FILE__, \
"CODE_LINE", G_STRINGIFY (__LINE__), \
"CODE_FUNC", __func__, \
- NULL)
+ "MESSAGE", format)
#else
#define g_error(format...) G_STMT_START { \
g_log (G_LOG_DOMAIN, \
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]