From f7c833242af5bf319caaef6644bf7e453a5b0654 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Sat, 29 Dec 2018 12:12:32 +1100 Subject: [PATCH] Add Geary.Loggable interface for objects that can log and be logged For now, just implement using existing non-structured logging calls. --- po/POTFILES.in | 1 + src/engine/api/geary-logging.vala | 24 +++++-- src/engine/meson.build | 1 + src/engine/util/util-loggable.vala | 106 +++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 src/engine/util/util-loggable.vala diff --git a/po/POTFILES.in b/po/POTFILES.in index cc42fb14..b73f99bf 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -389,6 +389,7 @@ src/engine/util/util-imap-utf7.vala src/engine/util/util-inet.vala src/engine/util/util-iterable.vala src/engine/util/util-js.vala +src/engine/util/util-loggable.vala src/engine/util/util-numeric.vala src/engine/util/util-object.vala src/engine/util/util-reference-semantics.vala diff --git a/src/engine/api/geary-logging.vala b/src/engine/api/geary-logging.vala index 652b7005..406485ca 100644 --- a/src/engine/api/geary-logging.vala +++ b/src/engine/api/geary-logging.vala @@ -14,11 +14,12 @@ namespace Geary.Logging { +/** The logging domain for the engine. */ +public const string DOMAIN = "Geary"; + /** Specifies the default number of log records retained. */ public const uint DEFAULT_MAX_LOG_BUFFER_LENGTH = 4096; -private const string DOMAIN = "Geary"; - /** * Denotes a type of log message. * @@ -172,31 +173,40 @@ public inline bool are_all_flags_set(Flag flags) { [PrintfFormat] public inline void error(Flag flags, string fmt, ...) { if (logging_flags.is_any_set(flags)) - logv(DOMAIN, LogLevelFlags.LEVEL_ERROR, fmt, va_list()); + GLib.logv(DOMAIN, LogLevelFlags.LEVEL_ERROR, fmt, va_list()); } [PrintfFormat] public inline void critical(Flag flags, string fmt, ...) { if (logging_flags.is_any_set(flags)) - logv(DOMAIN, LogLevelFlags.LEVEL_CRITICAL, fmt, va_list()); + GLib.logv(DOMAIN, LogLevelFlags.LEVEL_CRITICAL, fmt, va_list()); } [PrintfFormat] public inline void warning(Flag flags, string fmt, ...) { if (logging_flags.is_any_set(flags)) - logv(DOMAIN, LogLevelFlags.LEVEL_WARNING, fmt, va_list()); + GLib.logv(DOMAIN, LogLevelFlags.LEVEL_WARNING, fmt, va_list()); } [PrintfFormat] public inline void message(Flag flags, string fmt, ...) { if (logging_flags.is_any_set(flags)) - logv(DOMAIN, LogLevelFlags.LEVEL_MESSAGE, fmt, va_list()); + GLib.logv(DOMAIN, LogLevelFlags.LEVEL_MESSAGE, fmt, va_list()); } [PrintfFormat] public inline void debug(Flag flags, string fmt, ...) { if (logging_flags.is_any_set(flags)) { - logv(DOMAIN, LogLevelFlags.LEVEL_DEBUG, fmt, va_list()); + GLib.logv(DOMAIN, LogLevelFlags.LEVEL_DEBUG, fmt, va_list()); + } +} + +public inline void logv(Flag flags, + GLib.LogLevelFlags level, + string fmt, + va_list args) { + if (logging_flags.is_any_set(flags)) { + GLib.logv(DOMAIN, level, fmt, args); } } diff --git a/src/engine/meson.build b/src/engine/meson.build index d413e509..71815983 100644 --- a/src/engine/meson.build +++ b/src/engine/meson.build @@ -307,6 +307,7 @@ geary_engine_vala_sources = files( 'util/util-inet.vala', 'util/util-iterable.vala', 'util/util-js.vala', + 'util/util-loggable.vala', 'util/util-numeric.vala', 'util/util-object.vala', 'util/util-reference-semantics.vala', diff --git a/src/engine/util/util-loggable.vala b/src/engine/util/util-loggable.vala new file mode 100644 index 00000000..d220d397 --- /dev/null +++ b/src/engine/util/util-loggable.vala @@ -0,0 +1,106 @@ +/* + * Copyright 2018 Michael Gratton + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +/** + * Mixin interface for objects that support structured logging. + * + * Loggable objects provide both a standard means to obtain a string + * representation of the object for display to humans, and keep a weak + * reference to some parent loggable, enabling this context to be + * automatically added to logging calls. For example, if a Foo object + * is the loggable parent of a Bar object, log calls made by Bar will + * automatically be decorated with Foo. + */ +public interface Geary.Loggable : GLib.Object { + + + /** + * Default flags to use for this loggable when logging messages. + */ + public abstract Logging.Flag loggable_flags { get; protected set; } + + /** + * The parent of this loggable. + * + * If not null, the parent and its ancestors recursively will be + * added to to log message context. + */ + public abstract Loggable? loggable_parent { get; } + + /** + * Returns a string representation of the service, for debugging. + */ + public abstract string to_string(); + + + /** + * Logs a debug-level log message with this object as context. + */ + [PrintfFormat] + public inline void debug(string fmt, ...) { + log_structured( + this.loggable_flags, LogLevelFlags.LEVEL_DEBUG, fmt, va_list() + ); + } + + /** + * Logs a message-level log message with this object as context. + */ + [PrintfFormat] + public inline void message(string fmt, ...) { + log_structured( + this.loggable_flags, LogLevelFlags.LEVEL_MESSAGE, fmt, va_list() + ); + } + + /** + * Logs a warning-level log message with this object as context. + */ + [PrintfFormat] + public inline void warning(string fmt, ...) { + log_structured( + this.loggable_flags, LogLevelFlags.LEVEL_WARNING, fmt, va_list() + ); + } + + /** + * Logs a error-level log message with this object as context. + */ + [PrintfFormat] + [NoReturn] + public inline void error(string fmt, ...) { + log_structured( + this.loggable_flags, LogLevelFlags.LEVEL_ERROR, fmt, va_list() + ); + } + + /** + * Logs a critical-level log message with this object as context. + */ + [PrintfFormat] + public inline void critical(string fmt, ...) { + log_structured( + this.loggable_flags, LogLevelFlags.LEVEL_CRITICAL, fmt, va_list() + ); + } + + private inline void log_structured(Logging.Flag flags, + GLib.LogLevelFlags levels, + string fmt, + va_list args) { + GLib.StringBuilder message = new GLib.StringBuilder(fmt); + Loggable? decorator = this; + while (decorator != null) { + message.prepend_c(' '); + message.prepend(decorator.to_string()); + decorator = decorator.loggable_parent; + } + + Logging.logv(flags, levels, message.str, args); + } + +}