]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
stats: event export - Add tabescaped plain text format support
authorJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Thu, 4 Apr 2019 11:25:45 +0000 (14:25 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Tue, 25 Jun 2019 12:17:39 +0000 (12:17 +0000)
This commit adds support for format=tab-text.

Since we don't care to select a native timestamp data type, one must specify
exactly one time-* format_args token.

src/stats/Makefile.am
src/stats/event-exporter-fmt-tab-text.c [new file with mode: 0644]
src/stats/event-exporter.h
src/stats/stats-metrics.c
src/stats/stats-settings.c

index 0a5106b1ab88dafc5658fbbb1853495a0966a483..e376879b1388db6379954fa1b0c07e690e511218 100644 (file)
@@ -20,6 +20,7 @@ stats_SOURCES = \
        event-exporter-fmt.c \
        event-exporter-fmt-json.c \
        event-exporter-fmt-none.c \
+       event-exporter-fmt-tab-text.c \
        event-exporter-transport-drop.c \
        event-exporter-transport-http-post.c \
        event-exporter-transport-log.c \
diff --git a/src/stats/event-exporter-fmt-tab-text.c b/src/stats/event-exporter-fmt-tab-text.c
new file mode 100644 (file)
index 0000000..e4875e9
--- /dev/null
@@ -0,0 +1,182 @@
+/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "array.h"
+#include "lib-event-private.h"
+#include "event-exporter.h"
+#include "str.h"
+#include "strescape.h"
+#include "hostpid.h"
+
+static void append_int(string_t *dest, intmax_t val)
+{
+       str_printfa(dest, "%jd", val);
+}
+
+static void append_time(string_t *dest, const struct timeval *time,
+                       enum event_exporter_time_fmt fmt)
+{
+       switch (fmt) {
+       case EVENT_EXPORTER_TIME_FMT_NATIVE:
+               i_panic("tab-text format does not have a native date/time type");
+       case EVENT_EXPORTER_TIME_FMT_UNIX:
+               event_export_helper_fmt_unix_time(dest, time);
+               break;
+       case EVENT_EXPORTER_TIME_FMT_RFC3339:
+               event_export_helper_fmt_rfc3339_time(dest, time);
+               break;
+       }
+}
+
+static void append_field_value(string_t *dest, const struct event_field *field,
+                              const struct metric_export_info *info)
+{
+       switch (field->value_type) {
+       case EVENT_FIELD_VALUE_TYPE_STR:
+               str_append_tabescaped(dest, field->value.str);
+               break;
+       case EVENT_FIELD_VALUE_TYPE_INTMAX:
+               append_int(dest, field->value.intmax);
+               break;
+       case EVENT_FIELD_VALUE_TYPE_TIMEVAL:
+               append_time(dest, &field->value.timeval,
+                           info->exporter->time_format);
+               break;
+       }
+}
+
+static void tabtext_export_name(string_t *dest, struct event *event,
+                               const struct metric_export_info *info)
+{
+       if ((info->include & EVENT_EXPORTER_INCL_NAME) == 0)
+               return;
+
+       str_append(dest, "event:");
+       str_append_tabescaped(dest, event->sending_name);
+       str_append_c(dest, '\t');
+}
+
+static void tabtext_export_hostname(string_t *dest,
+                                   const struct metric_export_info *info)
+{
+       if ((info->include & EVENT_EXPORTER_INCL_HOSTNAME) == 0)
+               return;
+
+       str_append(dest, "hostname:");
+       str_append_tabescaped(dest, my_hostname);
+       str_append_c(dest, '\t');
+}
+
+static void tabtext_export_timestamps(string_t *dest, struct event *event,
+                                     const struct metric_export_info *info)
+{
+       if ((info->include & EVENT_EXPORTER_INCL_TIMESTAMPS) == 0)
+               return;
+
+       str_append(dest, "start_time:");
+       append_time(dest, &event->tv_created, info->exporter->time_format);
+       str_append(dest, "\tend_time:");
+       append_time(dest, &ioloop_timeval, info->exporter->time_format);
+       str_append_c(dest, '\t');
+}
+
+static void append_category(string_t *dest, struct event_category *cat)
+{
+       /* append parent's categories */
+       if (cat->parent != NULL)
+               append_category(dest, cat->parent);
+
+       /* append this */
+       str_append(dest, "category:");
+       str_append_tabescaped(dest, cat->name);
+       str_append_c(dest, '\t');
+}
+
+static void tabtext_export_categories(string_t *dest, struct event *event,
+                                     const struct metric_export_info *info)
+{
+       struct event_category *const *cats;
+       unsigned int count;
+       unsigned int i;
+
+       if ((info->include & EVENT_EXPORTER_INCL_CATEGORIES) == 0)
+               return;
+
+       cats = event_get_categories(event, &count);
+       for (i = 0; i < count; i++)
+               append_category(dest, cats[i]);
+}
+
+static void tabtext_export_fields(string_t *dest, struct event *event,
+                                 const struct metric_export_info *info,
+                                 const unsigned int fields_count,
+                                 const struct metric_field *fields)
+{
+       if ((info->include & EVENT_EXPORTER_INCL_FIELDS) == 0)
+               return;
+
+       if (fields_count == 0) {
+               /* include all fields */
+               const struct event_field *fields;
+               unsigned int count;
+
+               fields = event_get_fields(event, &count);
+
+               for (unsigned int i = 0; i < count; i++) {
+                       const struct event_field *field = &fields[i];
+
+                       str_append(dest, "field:");
+                       str_append_tabescaped(dest, field->key);
+                       str_append_c(dest, '=');
+                       append_field_value(dest, field, info);
+                       str_append_c(dest, '\t');
+               }
+       } else {
+               for (unsigned int i = 0; i < fields_count; i++) {
+                       const char *name = fields[i].field_key;
+                       const struct event_field *field;
+
+                       field = event_find_field(event, name);
+                       if (field == NULL)
+                               continue; /* doesn't exist, skip it */
+
+                       str_append(dest, "field:");
+                       str_append_tabescaped(dest, name);
+                       str_append_c(dest, '=');
+                       append_field_value(dest, field, info);
+                       str_append_c(dest, '\t');
+               }
+       }
+}
+
+/*
+ * Serialize the event as tab delimited collection of the following:
+ *
+ *    event:<event name>
+ *    hostname:<tab escaped hostname>
+ *    start_time:<event creation timestamp>
+ *    end_time:<event export timestamp>
+ *    category:<category>
+ *    field:<name>=<tab escaped value>
+ *
+ * Note: cat and field can occur multiple times.
+ */
+void event_export_fmt_tabescaped_text(const struct metric *metric,
+                                     struct event *event, buffer_t *dest)
+{
+       const struct metric_export_info *info = &metric->export_info;
+
+       if (info->include == EVENT_EXPORTER_INCL_NONE)
+               return;
+
+       tabtext_export_name(dest, event, info);
+       tabtext_export_hostname(dest, info);
+       tabtext_export_timestamps(dest, event, info);
+       tabtext_export_categories(dest, event, info);
+       tabtext_export_fields(dest, event, info, metric->fields_count,
+                             metric->fields);
+
+       /* remove trailing tab */
+       str_truncate(dest, str_len(dest) - 1);
+}
index c006bbe4105b38e04341aec965d2dec0c5f51462..7ec0590656c503ec65030f5fcf9a7639b1489a95 100644 (file)
@@ -6,6 +6,7 @@
 /* fmt functions */
 void event_export_fmt_json(const struct metric *metric, struct event *event, buffer_t *dest);
 void event_export_fmt_none(const struct metric *metric, struct event *event, buffer_t *dest);
+void event_export_fmt_tabescaped_text(const struct metric *metric, struct event *event, buffer_t *dest);
 
 /* transport functions */
 void event_export_transport_drop(const struct exporter *exporter, const buffer_t *buf);
index 1981375773423db12ad359dfe4b5a6df7f774d38..9da52a7da9f7567fa664a0f973d0c8465a08c238 100644 (file)
@@ -70,6 +70,9 @@ static void stats_exporters_add_set(struct stats_metrics *metrics,
        } else if (strcmp(set->format, "json") == 0) {
                exporter->format = event_export_fmt_json;
                exporter->format_mime_type = "application/json";
+       } else if (strcmp(set->format, "tab-text") == 0) {
+               exporter->format = event_export_fmt_tabescaped_text;
+               exporter->format_mime_type = "text/plain";
        } else {
                i_unreached();
        }
index 14059e8b1b4fdee449d881bcc53c35d08e9bf0f3..f72527dda046a3ae5fdc1384b7dc00101eb042f1 100644 (file)
@@ -236,6 +236,8 @@ static bool stats_exporter_settings_check(void *_set, pool_t pool ATTR_UNUSED,
                time_fmt_required = FALSE;
        } else if (strcmp(set->format, "json") == 0) {
                time_fmt_required = TRUE;
+       } else if (strcmp(set->format, "tab-text") == 0) {
+               time_fmt_required = TRUE;
        } else {
                *error_r = t_strdup_printf("Unknown exporter format '%s'",
                                           set->format);