From b17b537a7de15ec66a0dd2b1c708409f415426a2 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 23 Jul 2020 17:57:44 +0100 Subject: [PATCH] gmessages: Expose our default filtering as API This allows programs that want to change how log messages are printed, such as gnome-terminal (gnome-terminal#42) and Flatpak, to override the log-writer or the legacy log-handler without having to reimplement the G_MESSAGES_DEBUG filtering logic. Signed-off-by: Simon McVittie --- docs/reference/glib/glib-sections.txt | 1 + glib/gmessages.c | 119 ++++++++++++++++++++------ glib/gmessages.h | 3 + glib/tests/logging.c | 51 +++++++++++ 4 files changed, 148 insertions(+), 26 deletions(-) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 30e58de0c..6c1c86198 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -1485,6 +1485,7 @@ g_log_writer_journald g_log_writer_standard_streams g_log_writer_default g_log_writer_default_set_use_stderr +g_log_writer_default_would_drop g_log_structured_standard diff --git a/glib/gmessages.c b/glib/gmessages.c index 440abd507..ec21d5d1d 100644 --- a/glib/gmessages.c +++ b/glib/gmessages.c @@ -127,7 +127,8 @@ * It is recommended that custom log writer functions re-use the * `G_MESSAGES_DEBUG` environment variable, rather than inventing a custom one, * so that developers can re-use the same debugging techniques and tools across - * projects. + * projects. Since GLib 2.68, this can be implemented by dropping messages + * for which g_log_writer_default_would_drop() returns %TRUE. * * ## Testing for Messages ## {#testing-for-messages} * @@ -2617,6 +2618,95 @@ log_is_old_api (const GLogField *fields, g_strcmp0 (fields[0].value, "1") == 0); } +/* + * Internal version of g_log_writer_default_would_drop(), which can + * read from either a log_domain or an array of fields. This avoids + * having to iterate through the fields if the @log_level is sufficient + * to make the decision. + */ +static gboolean +should_drop_message (GLogLevelFlags log_level, + const char *log_domain, + const GLogField *fields, + gsize n_fields) +{ + /* Disable debug message output unless specified in G_MESSAGES_DEBUG. */ + if (!(log_level & DEFAULT_LEVELS) && !(log_level >> G_LOG_LEVEL_USER_SHIFT)) + { + const gchar *domains; + gsize i; + + domains = g_getenv ("G_MESSAGES_DEBUG"); + + if ((log_level & INFO_LEVELS) == 0 || + domains == NULL) + return TRUE; + + if (log_domain == NULL) + { + for (i = 0; i < n_fields; i++) + { + if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0) + { + log_domain = fields[i].value; + break; + } + } + } + + if (strcmp (domains, "all") != 0 && + (log_domain == NULL || !strstr (domains, log_domain))) + return TRUE; + } + + return FALSE; +} + +/** + * g_log_writer_default_would_drop: + * @log_domain: (nullable): log domain + * @log_level: log level, either from #GLogLevelFlags, or a user-defined + * level + * + * Check whether g_log_writer_default() and g_log_default_handler() would + * ignore a message with the given domain and level. + * + * As with g_log_default_handler(), this function drops debug and informational + * messages unless their log domain (or `all`) is listed in the space-separated + * `G_MESSAGES_DEBUG` environment variable. + * + * This can be used when implementing log writers with the same filtering + * behaviour as the default, but a different destination or output format: + * + * |[ + * if (g_log_writer_default_would_drop (log_level, log_domain)) + * return G_LOG_WRITER_HANDLED; + * ]| + * + * or to skip an expensive computation if it is only needed for a debugging + * message, and `G_MESSAGES_DEBUG` is not set: + * + * |[ + * if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN)) + * { + * gchar *result = expensive_computation (my_object); + * + * g_debug ("my_object result: %s", result); + * g_free (result); + * } + * ]| + * + * Returns: %TRUE if the log message would be dropped by GLib's + * default log handlers + * Since: 2.68 + */ +gboolean +g_log_writer_default_would_drop (GLogLevelFlags log_level, + const char *log_domain) +{ + return should_drop_message (log_level, log_domain, NULL, 0); +} + /** * g_log_writer_default: * @log_level: log level, either from #GLogLevelFlags, or a user-defined @@ -2661,31 +2751,8 @@ g_log_writer_default (GLogLevelFlags log_level, g_return_val_if_fail (fields != NULL, G_LOG_WRITER_UNHANDLED); g_return_val_if_fail (n_fields > 0, G_LOG_WRITER_UNHANDLED); - /* Disable debug message output unless specified in G_MESSAGES_DEBUG. */ - if (!(log_level & DEFAULT_LEVELS) && !(log_level >> G_LOG_LEVEL_USER_SHIFT)) - { - const gchar *domains, *log_domain = NULL; - gsize i; - - domains = g_getenv ("G_MESSAGES_DEBUG"); - - if ((log_level & INFO_LEVELS) == 0 || - domains == NULL) - return G_LOG_WRITER_HANDLED; - - for (i = 0; i < n_fields; i++) - { - if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0) - { - log_domain = fields[i].value; - break; - } - } - - if (strcmp (domains, "all") != 0 && - (log_domain == NULL || !strstr (domains, log_domain))) - return G_LOG_WRITER_HANDLED; - } + if (should_drop_message (log_level, NULL, fields, n_fields)) + return G_LOG_WRITER_HANDLED; /* Mark messages as fatal if they have a level set in * g_log_set_always_fatal(). diff --git a/glib/gmessages.h b/glib/gmessages.h index 29bc1736e..6a28443b4 100644 --- a/glib/gmessages.h +++ b/glib/gmessages.h @@ -244,6 +244,9 @@ GLogWriterOutput g_log_writer_default (GLogLevelFlags log_level, GLIB_AVAILABLE_IN_2_68 void g_log_writer_default_set_use_stderr (gboolean use_stderr); +GLIB_AVAILABLE_IN_2_68 +gboolean g_log_writer_default_would_drop (GLogLevelFlags log_level, + const char *log_domain); /** * G_DEBUG_HERE: diff --git a/glib/tests/logging.c b/glib/tests/logging.c index 1e9192bef..f16503ad1 100644 --- a/glib/tests/logging.c +++ b/glib/tests/logging.c @@ -139,6 +139,53 @@ test_default_handler_debug_stderr (void) g_log ("foo", G_LOG_LEVEL_DEBUG, "6"); g_log ("bar", G_LOG_LEVEL_DEBUG, "6"); g_log ("baz", G_LOG_LEVEL_DEBUG, "6"); + + exit (0); +} + +static void +test_default_handler_would_drop (void) +{ + g_unsetenv ("G_MESSAGES_DEBUG"); + + g_assert_false (g_log_writer_default_would_drop (G_LOG_LEVEL_ERROR, "foo")); + g_assert_false (g_log_writer_default_would_drop (G_LOG_LEVEL_CRITICAL, "foo")); + g_assert_false (g_log_writer_default_would_drop (G_LOG_LEVEL_WARNING, "foo")); + g_assert_false (g_log_writer_default_would_drop (G_LOG_LEVEL_MESSAGE, "foo")); + g_assert_true (g_log_writer_default_would_drop (G_LOG_LEVEL_INFO, "foo")); + g_assert_true (g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, "foo")); + g_assert_false (g_log_writer_default_would_drop (1<