[geary/wip/structured-logging] WIP: Switch to using GLib structured logging
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/structured-logging] WIP: Switch to using GLib structured logging
- Date: Sat, 29 Dec 2018 02:59:13 +0000 (UTC)
commit 46a1232b8069bf3a4eff3c8a7379d785386508b9
Author: Michael Gratton <mike vee net>
Date: Sat Dec 29 12:57:01 2018 +1100
WIP: Switch to using GLib structured logging
This converts the logging infrastructure to using structured logging
calls, adds and sets a structured logging writer.
TODO:
* Fix crash in formatting message string in Context
* Switch/remove calls in Logging, use Loggables instead
* Properly GLib log level as PRIORITY in context
* Set G_STRUCTURED_LOGGING define up front in the build
src/client/application/geary-application.vala | 1 +
src/engine/api/geary-logging.vala | 127 ++++++++++++++++++++++++++
src/engine/util/util-loggable.vala | 62 +++++++++++--
3 files changed, 183 insertions(+), 7 deletions(-)
---
diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala
index e7ecc762..4cca1fd0 100644
--- a/src/client/application/geary-application.vala
+++ b/src/client/application/geary-application.vala
@@ -187,6 +187,7 @@ public class GearyApplication : Gtk.Application {
Geary.Logging.init();
Geary.Logging.log_to(stderr);
GLib.Log.set_default_handler(Geary.Logging.default_handler);
+ GLib.Log.set_writer_func(Geary.Logging.default_log_writer);
Date.init();
diff --git a/src/engine/api/geary-logging.vala b/src/engine/api/geary-logging.vala
index c6ec9a6a..1f8cc2b3 100644
--- a/src/engine/api/geary-logging.vala
+++ b/src/engine/api/geary-logging.vala
@@ -35,6 +35,51 @@ public enum Flag {
public inline bool is_any_set(Flag flags) {
return (flags & this) != 0;
}
+
+ public string to_string() {
+ GLib.StringBuilder buf = new GLib.StringBuilder();
+ if (this == ALL) {
+ buf.append("ALL");
+ } else if (this == NONE) {
+ buf.append("NONE");
+ } else {
+ if (this.is_any_set(NETWORK)) {
+ buf.append("NET");
+ }
+ if (this.is_any_set(SERIALIZER)) {
+ if (buf.len > 0) {
+ buf.append_c('|');
+ }
+ buf.append("SER");
+ }
+ if (this.is_any_set(REPLAY)) {
+ if (buf.len > 0) {
+ buf.append_c('|');
+ }
+ buf.append("REPLAY");
+ }
+ if (this.is_any_set(SQL)) {
+ if (buf.len > 0) {
+ buf.append_c('|');
+ }
+ buf.append("SQL");
+ }
+ if (this.is_any_set(FOLDER_NORMALIZATION)) {
+ if (buf.len > 0) {
+ buf.append_c('|');
+ }
+ buf.append("NORM");
+ }
+ if (this.is_any_set(DESERIALIZER)) {
+ if (buf.len > 0) {
+ buf.append_c('|');
+ }
+ buf.append("DESER");
+ }
+ }
+ return buf.str;
+ }
+
}
private int init_count = 0;
@@ -169,6 +214,88 @@ public void default_handler(string? domain,
}
}
+public GLib.LogWriterOutput default_log_writer(GLib.LogLevelFlags log_levels,
+ GLib.LogField[] fields) {
+ unowned FileStream? out = stream;
+ if (out != null ||
+ ((LogLevelFlags.LEVEL_WARNING & log_levels) > 0) ||
+ ((LogLevelFlags.LEVEL_CRITICAL & log_levels) > 0) ||
+ ((LogLevelFlags.LEVEL_ERROR & log_levels) > 0)) {
+
+ if (out == null) {
+ out = GLib.stderr;
+ }
+
+ string domain = "default";
+ Flag flag = Flag.NONE;
+ string message = "[no message]";
+
+ string[] strings = new string[fields.length];
+ uint string_count = 0;
+
+ foreach (GLib.LogField field in fields) {
+ switch (field.key.ascii_up()) {
+ case "GLIB_DOMAIN":
+ if (field.length < 0) {
+ domain = (string) field.value;
+ }
+ break;
+ case "MESSAGE":
+ if (field.length < 0) {
+ message = (string) field.value;
+ }
+ break;
+ case "PRIORITY":
+ // noop, we get it from the args
+ break;
+ case "GEARY_FLAGS":
+ flag = (Flag) field.value;
+ break;
+ default:
+ string? string_value = null;
+ if (field.length < 0) {
+ string_value = (string) field.value;
+ } else {
+ Loggable? loggable = field.value as Loggable;
+ if (loggable != null) {
+ string_value = loggable.to_string();
+ }
+ }
+ if (string_value != null) {
+ strings[string_count++] = string_value;
+ }
+ break;
+ }
+ }
+
+ GLib.Time tm = GLib.Time.local(time_t());
+ out.printf(
+ "%s %02d:%02d:%02d %lf %s",
+ to_prefix(log_levels),
+ tm.hour, tm.minute, tm.second,
+ entry_timer.elapsed(),
+ domain
+ );
+
+ if (flag != ALL && flag != NONE) {
+ out.printf("[%s]: ", flag.to_string());
+ } else {
+ out.puts(": ");
+ }
+
+ for (int i = 0; i < string_count; i++) {
+ out.puts(strings[i]);
+ out.putc(' ');
+ }
+ out.puts(message);
+ out.putc('\n');
+ entry_timer.start();
+ }
+
+ return GLib.LogWriterOutput.HANDLED;
+}
+
+
private inline string to_prefix(LogLevelFlags level) {
switch (level) {
case LogLevelFlags.LEVEL_ERROR:
diff --git a/src/engine/util/util-loggable.vala b/src/engine/util/util-loggable.vala
index d220d397..0e39bd64 100644
--- a/src/engine/util/util-loggable.vala
+++ b/src/engine/util/util-loggable.vala
@@ -17,6 +17,54 @@
*/
public interface Geary.Loggable : GLib.Object {
+ protected struct Context {
+
+ // 8 fields ought to be enough for anybody...
+ private const uint8 FIELD_COUNT = 8;
+
+ public GLib.LogField[] fields;
+ public uint8 len;
+ public uint8 count;
+
+ Context(string domain,
+ Logging.Flag flags,
+ GLib.LogLevelFlags levels,
+ string message,
+ va_list args) {
+ this.fields = new GLib.LogField[FIELD_COUNT];
+ this.len = FIELD_COUNT;
+ this.count = 0;
+ append("PRIORITY", levels);
+ append("GLIB_DOMAIN", domain);
+ append("GEARY_FLAGS", flags);
+ append("MESSAGE", message.vprintf(args));
+
+ GLib.debug("XXX context: %s", message.vprintf(args));
+ }
+
+ public void append<T>(string key, T value) {
+ uint8 count = this.count;
+ if (count + 1 >= this.len) {
+ this.fields.resize(this.len + FIELD_COUNT);
+ }
+
+ this.fields[count].key = key;
+ this.fields[count].value = value;
+ this.fields[count].length = (typeof(T) == typeof(string)) ? -1 : 0;
+
+ this.count++;
+ }
+
+ public inline void append_instance<T>(T value) {
+ this.append(typeof(T).name(), value);
+ }
+
+ public GLib.LogField[] to_array() {
+ return this.fields[0:this.count];
+ }
+
+ }
+
/**
* Default flags to use for this loggable when logging messages.
@@ -92,15 +140,15 @@ public interface Geary.Loggable : GLib.Object {
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;
+ GLib.debug("XXX log call: %s", fmt.vprintf(args));
+ Context context = Context(Logging.DOMAIN, flags, levels, fmt, va_list.copy(args));
+ Loggable? decorated = this;
+ while (decorated != null) {
+ context.append_instance(decorated);
+ decorated = decorated.loggable_parent;
}
- Logging.logv(flags, levels, message.str, args);
+ GLib.log_structured_array(levels, context.to_array());
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]