[folks] Add debug printing and formatting methods to Debug



commit 26ea12bdcfbca8ba00b859a59db357a9608e5b39
Author: Philip Withnall <philip tecnocode co uk>
Date:   Sun Apr 24 00:22:19 2011 +0100

    Add debug printing and formatting methods to Debug
    
    This includes indentation and output colouring support.
    
    Helps: bgo#648533

 folks/backend-store.vala |    4 +-
 folks/debug.vala         |  234 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 233 insertions(+), 5 deletions(-)
---
diff --git a/folks/backend-store.vala b/folks/backend-store.vala
index 1580006..18ee698 100644
--- a/folks/backend-store.vala
+++ b/folks/backend-store.vala
@@ -131,8 +131,10 @@ public class Folks.BackendStore : Object {
   private BackendStore ()
     {
       /* Treat this as a library init function */
+      var debug_no_colour = Environment.get_variable ("FOLKS_DEBUG_NO_COLOUR");
       this._debug =
-          Debug.dup_with_flags (Environment.get_variable ("FOLKS_DEBUG"));
+          Debug.dup_with_flags (Environment.get_variable ("FOLKS_DEBUG"),
+              (debug_no_colour == null || debug_no_colour == "0"));
 
       /* register the core debug messages */
       this._debug._register_domain (G_LOG_DOMAIN);
diff --git a/folks/debug.vala b/folks/debug.vala
index cb7245b..5bade93 100644
--- a/folks/debug.vala
+++ b/folks/debug.vala
@@ -21,6 +21,16 @@
 using GLib;
 using Gee;
 
+/* We have to declare our own wrapping of g_log so that it doesn't have the
+ * [Diagnostics] attribute, which would cause valac to add the Vala source file
+ * name to the message format, which we don't want. This is used in
+ * Debug.print_line(). */
+[PrintfFormat]
+private extern void g_log (string? log_domain,
+    LogLevelFlags log_level,
+    string format,
+    ...);
+
 /**
  * Manage debug output and status reporting for all folks objects. All GLib
  * debug logging calls are passed through a log handler in this class, which
@@ -42,6 +52,40 @@ public class Folks.Debug : Object
   private HashSet<string> _domains; /* needs to be locked when accessed */
   private bool _all = false; /* needs _domains to be locked when accessed */
 
+  /* The current indentation level, in spaces */
+  private uint _indentation = 0;
+  private string _indentation_string = "";
+
+  private bool _colour_enabled = true;
+
+  /*
+   * Whether colour output is enabled. If true, debug output may include
+   * terminal colour escape codes. Disabled by the environment variable
+   * FOLKS_DEBUG_NO_COLOUR being set to anything except â??0â??.
+   *
+   * This property is thread-safe.
+   *
+   * @since UNRELEASED
+   */
+  public bool colour_enabled
+    {
+      get
+        {
+          lock (this._colour_enabled)
+            {
+              return this._colour_enabled;
+            }
+        }
+
+      set
+        {
+          lock (this._colour_enabled)
+            {
+              this._colour_enabled = value;
+            }
+        }
+    }
+
   private bool _debug_output_enabled = true;
 
   /**
@@ -138,12 +182,15 @@ public class Folks.Debug : Object
    * set of debug domains enabled. Otherwise, the existing instance will have
    * its set of enabled domains changed to the provided set.
    *
-   * @param debug_flags	A comma-separated list of debug domains to enable,
-   *			or null to disable debug output
-   * @return		Singleton { link Debug} instance
+   * @param debug_flags		A comma-separated list of debug domains to
+   *				enable, or null to disable debug output
+   * @param colour_enabled	Whether debug output should be coloured using
+   *				terminal escape sequences
+   * @return			Singleton { link Debug} instance
    * @since UNRELEASED
    */
-  public static Debug dup_with_flags (string? debug_flags)
+  public static Debug dup_with_flags (string? debug_flags,
+      bool colour_enabled)
     {
       var retval = Debug.dup ();
 
@@ -167,6 +214,8 @@ public class Folks.Debug : Object
             }
         }
 
+      retval.colour_enabled = colour_enabled;
+
       return retval;
     }
 
@@ -183,4 +232,181 @@ public class Folks.Debug : Object
           Debug._instance = null;
         }
     }
+
+  /**
+   * Increment the indentation level used when printing output through the
+   * object.
+   *
+   * This is intended to be used by backend libraries only.
+   *
+   * @since UNRELEASED
+   */
+  public void indent ()
+    {
+      /* We indent in increments of two spaces */
+      this._indentation++;
+      this._indentation_string = string.nfill (this._indentation * 2, ' ');
+    }
+
+  /**
+   * Decrement the indentation level used when printing output through the
+   * object.
+   *
+   * This is intended to be used by backend libraries only.
+   *
+   * @since UNRELEASED
+   */
+  public void unindent ()
+    {
+      this._indentation--;
+      this._indentation_string = string.nfill (this._indentation * 2, ' ');
+    }
+
+  /**
+   * Print a debug line with the current indentation level for the specified
+   * debug domain.
+   *
+   * This is intended to be used by backend libraries only.
+   *
+   * @param domain	The debug domain name
+   * @param level	A set of log level flags for the message
+   * @param format	A printf-style format string for the heading
+   * @param ...		Arguments for the format string
+   * @since UNRELEASED
+   */
+  [PrintfFormat ()]
+  public void print_line (string domain,
+      LogLevelFlags level,
+      string format,
+      ...)
+    {
+      /* FIXME: store the va_list temporarily to work around bgo#638308 */
+      var valist = va_list ();
+      string output = format.vprintf (valist);
+      g_log (domain, level, "%s%s", this._indentation_string, output);
+    }
+
+  /**
+   * Print a debug line as a heading. It will be coloured according to the
+   * current indentation level so that different levels of headings stand out.
+   *
+   * This is intended to be used by backend libraries only.
+   *
+   * @param domain	The debug domain name
+   * @param level	A set of log level flags for the message
+   * @param format	A printf-style format string for the heading
+   * @param ...		Arguments for the format string
+   * @since UNRELEASED
+   */
+  [PrintfFormat ()]
+  public void print_heading (string domain,
+      LogLevelFlags level,
+      string format,
+      ...)
+    {
+      /* Colour the heading according to the current indentation level.
+       * ANSI terminal colour codes. */
+      const int[] heading_colours =
+        {
+          31, /* red */
+          32, /* green */
+          34 /* blue */
+        };
+
+      var wrapper_format = "%s";
+      if (this.colour_enabled == true)
+        {
+          var indentation =
+              this._indentation.clamp (0, heading_colours.length - 1);
+          wrapper_format =
+              "\033[1;%im%%s\033[0m".printf (heading_colours[indentation]);
+        }
+
+      /* FIXME: store the va_list temporarily to work around bgo#638308 */
+      var valist = va_list ();
+      string output = format.vprintf (valist);
+      this.print_line (domain, level, wrapper_format, output);
+    }
+
+  /*
+   * Format a potentially null string for printing; if the string is null,
+   * â??(null)â?? will be outputted. If coloured output is enabled, this output
+   * will be coloured brown. */
+  private string _format_nullable_string (string? input)
+    {
+      if (this.colour_enabled == true && input == null)
+        {
+          return "\033[1;36m(null)\033[0m"; /* cyan */
+        }
+      else if (input == null)
+        {
+          return "(null)";
+        }
+
+      return input;
+    }
+
+  struct KeyValuePair
+    {
+      string key;
+      string? val;
+    }
+
+  /**
+   * Print a set of keyâ??value pairs in a table. The width of the key column is
+   * automatically set to the width of the longest key. The keys and values
+   * must be provided as a null-delimited list of alternating keyâ??value varargs.
+   * Values may be null but keys may not.
+   *
+   * This is intended to be used by backend libraries only.
+   *
+   * The table will be printed at the current indentation level plus one.
+   *
+   * @param domain	The debug domain name
+   * @param level	A set of log level flags for the message
+   * @param ...		Alternating keys and values, terminated with null
+   * @since UNRELEASED
+   */
+  public void print_key_value_pairs (string domain,
+      LogLevelFlags level,
+      ...)
+    {
+      var valist = va_list ();
+      KeyValuePair[] lines = {};
+      uint max_key_length = 0;
+
+      /* Read in the arguments and calculate the longest key for alignment
+       * purposes */
+      while (true)
+        {
+          string? key = valist.arg ();
+          if (key == null)
+            {
+              break;
+            }
+
+          string? val = valist.arg ();
+
+          /* Keep track of the longest key we've seen */
+          max_key_length = uint.max (key.length, max_key_length);
+
+          lines += KeyValuePair ()
+            {
+              key = key,
+              val = val
+            };
+        }
+
+      this.indent ();
+
+      /* Print out the lines */
+      foreach (var line in lines)
+        {
+          var padding = string.nfill (max_key_length - line.key.length, ' ');
+          this.print_line (domain, level, "%s: %s%s", line.key, padding,
+              this._format_nullable_string (line.val));
+        }
+
+      this.unindent ();
+    }
 }



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