2 * Copyright (C) 2010 Collabora Ltd.
4 * This library is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 2.1 of the License, or
7 * (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
18 * Philip Withnall <philip.withnall@collabora.co.uk>
24 /* We have to declare our own wrapping of g_log so that it doesn't have the
25 * [Diagnostics] attribute, which would cause valac to add the Vala source file
26 * name to the message format, which we don't want. This is used in
27 * Debug.print_line(). */
29 private extern void g_log (string? log_domain,
30 LogLevelFlags log_level,
35 * Manage debug output and status reporting for all folks objects. All GLib
36 * debug logging calls are passed through a log handler in this class, which
37 * allows debug domains to be outputted according to whether they've been
38 * enabled by being passed to {@link Debug.dup}.
42 public class Folks.Debug : Object
44 private enum Domains {
45 /* Zero is used for "no debug spew" */
47 TELEPATHY_BACKEND = 1 << 1,
48 KEY_FILE_BACKEND = 1 << 2
51 private static weak Debug _instance; /* needs to be locked when accessed */
52 private HashSet<string> _domains; /* needs to be locked when accessed */
53 private bool _all = false; /* needs _domains to be locked when accessed */
55 /* The current indentation level, in spaces */
56 private uint _indentation = 0;
57 private string _indentation_string = "";
59 private bool _colour_enabled = true;
62 * Whether colour output is enabled. If true, debug output may include
63 * terminal colour escape codes. Disabled by the environment variable
64 * FOLKS_DEBUG_NO_COLOUR being set to anything except “0”.
66 * This property is thread-safe.
70 public bool colour_enabled
74 lock (this._colour_enabled)
76 return this._colour_enabled;
82 lock (this._colour_enabled)
84 this._colour_enabled = value;
89 private bool _debug_output_enabled = true;
92 * Whether debug output is enabled. This is orthogonal to the set of enabled
93 * debug domains; filtering of debug output as a whole is done after filtering
98 public bool debug_output_enabled
102 lock (this._debug_output_enabled)
104 return this._debug_output_enabled;
110 lock (this._debug_output_enabled)
112 this._debug_output_enabled = value;
118 * Signal emitted in the main thread whenever objects should print their
119 * current status. All significant objects in the library should connect
120 * to this and print their current status in some suitable format when it's
123 * Client processes should emit this signal by calling
124 * {@link Debug.emit_print_status}.
128 public signal void print_status ();
131 * Log domain for the status messages logged as a result of calling
132 * {@link Debug.emit_print_status}.
134 * This could be used in conjunction with a log handler to redirect the
135 * status information to a debug window or log file, for example.
139 public const string STATUS_LOG_DOMAIN = "folks-status";
141 private void _print_status_log_handler_cb (string? log_domain,
142 LogLevelFlags log_levels,
145 /* Print directly to stdout without any adornments */
146 GLib.stdout.printf ("%s\n", message);
149 private void _log_handler_cb (string? log_domain,
150 LogLevelFlags log_levels,
153 if (this.debug_output_enabled == false)
155 /* Don't output anything if debug output is disabled, even for
156 * enabled debug domains. */
160 /* Otherwise, pass through to the default log handler */
161 Log.default_handler (log_domain, log_levels, message);
164 /* turn off debug output for the given domain unless it was in the FOLKS_DEBUG
165 * environment variable (or 'all' was set) */
166 internal void _register_domain (string domain)
170 if (this._all || this._domains.contains (domain.down ()))
172 Log.set_handler (domain, LogLevelFlags.LEVEL_MASK,
173 this._log_handler_cb);
178 /* Install a log handler which will blackhole the log message.
179 * Other log messages will be printed out by the default log handler. */
180 Log.set_handler (domain, LogLevelFlags.LEVEL_DEBUG,
181 (domain_arg, flags, message) => {});
185 * Create or return the singleton {@link Debug} class instance.
186 * If the instance doesn't exist already, it will be created with no debug
189 * This function is thread-safe.
191 * @return Singleton {@link Debug} instance
194 public static Debug dup ()
196 lock (Debug._instance)
198 var retval = Debug._instance;
202 /* use an intermediate variable to force a strong reference */
203 retval = new Debug ();
204 Debug._instance = retval;
212 * Create or return the singleton {@link Debug} class instance.
213 * If the instance doesn't exist already, it will be created with the given
214 * set of debug domains enabled. Otherwise, the existing instance will have
215 * its set of enabled domains changed to the provided set.
217 * @param debug_flags A comma-separated list of debug domains to enable, or
218 * null to disable debug output
219 * @param colour_enabled Whether debug output should be coloured using
220 * terminal escape sequences
221 * @return Singleton {@link Debug} instance
224 public static Debug dup_with_flags (string? debug_flags,
227 var retval = Debug.dup ();
229 lock (retval._domains)
232 retval._domains = new HashSet<string> (str_hash, str_equal);
234 if (debug_flags != null && debug_flags != "")
236 var domains_split = debug_flags.split (",");
237 foreach (var domain in domains_split)
239 var domain_lower = domain.down ();
241 if (GLib.strcmp (domain_lower, "all") == 0)
244 retval._domains.add (domain_lower);
249 retval.colour_enabled = colour_enabled;
256 /* Private constructor for singleton */
258 /* Install a log handler for log messages emitted as a result of
259 * Debug.print-status being emitted. */
260 Log.set_handler (Debug.STATUS_LOG_DOMAIN, LogLevelFlags.LEVEL_MASK,
261 this._print_status_log_handler_cb);
266 /* Manually clear the singleton _instance */
267 lock (Debug._instance)
269 Debug._instance = null;
274 * Causes all significant objects in the library to print their current
275 * status to standard output, obeying the options set on this
276 * {@link Debug} instance for colouring and other formatting.
280 public void emit_print_status ()
282 print ("Dumping status information…\n");
283 this.print_status ();
287 * Increment the indentation level used when printing output through the
290 * This is intended to be used by backend libraries only.
294 public void indent ()
296 /* We indent in increments of two spaces */
298 this._indentation_string = string.nfill (this._indentation * 2, ' ');
302 * Decrement the indentation level used when printing output through the
305 * This is intended to be used by backend libraries only.
309 public void unindent ()
312 this._indentation_string = string.nfill (this._indentation * 2, ' ');
316 * Print a debug line with the current indentation level for the specified
319 * This is intended to be used by backend libraries only.
321 * @param domain The debug domain name
322 * @param level A set of log level flags for the message
323 * @param format A printf-style format string for the heading
324 * @param ... Arguments for the format string
328 public void print_line (string domain,
333 /* FIXME: store the va_list temporarily to work around bgo#638308 */
334 var valist = va_list ();
335 string output = format.vprintf (valist);
336 g_log (domain, level, "%s%s", this._indentation_string, output);
340 * Print a debug line as a heading. It will be coloured according to the
341 * current indentation level so that different levels of headings stand out.
343 * This is intended to be used by backend libraries only.
345 * @param domain The debug domain name
346 * @param level A set of log level flags for the message
347 * @param format A printf-style format string for the heading
348 * @param ... Arguments for the format string
352 public void print_heading (string domain,
357 /* Colour the heading according to the current indentation level.
358 * ANSI terminal colour codes. */
359 const int[] heading_colours =
366 var wrapper_format = "%s";
367 if (this.colour_enabled == true)
370 this._indentation.clamp (0, heading_colours.length - 1);
372 "\033[1;%im%%s\033[0m".printf (heading_colours[indentation]);
375 /* FIXME: store the va_list temporarily to work around bgo#638308 */
376 var valist = va_list ();
377 string output = format.vprintf (valist);
378 this.print_line (domain, level, wrapper_format, output);
382 * Format a potentially null string for printing; if the string is null,
383 * “(null)” will be outputted. If coloured output is enabled, this output
384 * will be coloured brown. */
385 private string _format_nullable_string (string? input)
387 if (this.colour_enabled == true && input == null)
389 return "\033[1;36m(null)\033[0m"; /* cyan */
391 else if (input == null)
406 * Print a set of key–value pairs in a table. The width of the key column is
407 * automatically set to the width of the longest key. The keys and values
408 * must be provided as a null-delimited list of alternating key–value varargs.
409 * Values may be null but keys may not.
411 * This is intended to be used by backend libraries only.
413 * The table will be printed at the current indentation level plus one.
415 * @param domain The debug domain name
416 * @param level A set of log level flags for the message
417 * @param ... Alternating keys and values, terminated with null
420 public void print_key_value_pairs (string domain,
424 var valist = va_list ();
425 KeyValuePair[] lines = {};
426 uint max_key_length = 0;
428 /* Read in the arguments and calculate the longest key for alignment
432 string? key = valist.arg ();
438 string? val = valist.arg ();
440 /* Keep track of the longest key we've seen */
441 max_key_length = uint.max (key.length, max_key_length);
443 lines += KeyValuePair ()
452 /* Print out the lines */
453 foreach (var line in lines)
455 var padding = string.nfill (max_key_length - line.key.length, ' ');
456 this.print_line (domain, level, "%s: %s%s", line.key, padding,
457 this._format_nullable_string (line.val));