[glib/wip/smcv/issue2087: 2/2] gmessages: Expose our default filtering as API




commit b17b537a7de15ec66a0dd2b1c708409f415426a2
Author: Simon McVittie <smcv collabora com>
Date:   Thu Jul 23 17:57:44 2020 +0100

    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 <smcv collabora com>

 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
 
 <SUBSECTION Private>
 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:
+ *
+ * |[<!-- language="C" -->
+ *   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:
+ *
+ * |[<!-- language="C" -->
+ *   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<<G_LOG_LEVEL_USER_SHIFT, "foo"));
+
+  g_setenv ("G_MESSAGES_DEBUG", "bar baz", TRUE);
+
+  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<<G_LOG_LEVEL_USER_SHIFT, "foo"));
+
+  g_setenv ("G_MESSAGES_DEBUG", "foo bar", TRUE);
+
+  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_false (g_log_writer_default_would_drop (G_LOG_LEVEL_INFO, "foo"));
+  g_assert_false (g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, "foo"));
+  g_assert_false (g_log_writer_default_would_drop (1<<G_LOG_LEVEL_USER_SHIFT, "foo"));
+
+  g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+
+  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_false (g_log_writer_default_would_drop (G_LOG_LEVEL_INFO, "foo"));
+  g_assert_false (g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, "foo"));
+  g_assert_false (g_log_writer_default_would_drop (1<<G_LOG_LEVEL_USER_SHIFT, "foo"));
+
   exit (0);
 }
 
@@ -193,6 +240,9 @@ test_default_handler (void)
   g_test_trap_subprocess ("/logging/default-handler/subprocess/0x400", 0, 0);
   g_test_trap_assert_passed ();
   g_test_trap_assert_stdout ("*LOG-0x400*message7*");
+
+  g_test_trap_subprocess ("/logging/default-handler/subprocess/would-drop", 0, 0);
+  g_test_trap_assert_passed ();
 }
 
 static void
@@ -600,6 +650,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/logging/default-handler/subprocess/debug", test_default_handler_debug);
   g_test_add_func ("/logging/default-handler/subprocess/debug-stderr", test_default_handler_debug_stderr);
   g_test_add_func ("/logging/default-handler/subprocess/0x400", test_default_handler_0x400);
+  g_test_add_func ("/logging/default-handler/subprocess/would-drop", test_default_handler_would_drop);
   g_test_add_func ("/logging/warnings", test_warnings);
   g_test_add_func ("/logging/fatal-log-mask", test_fatal_log_mask);
   g_test_add_func ("/logging/set-handler", test_set_handler);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]