summaryrefslogtreecommitdiffstats
path: root/src/stats/event-exporter-fmt-json.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stats/event-exporter-fmt-json.c')
-rw-r--r--src/stats/event-exporter-fmt-json.c249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/stats/event-exporter-fmt-json.c b/src/stats/event-exporter-fmt-json.c
new file mode 100644
index 0000000..25c6116
--- /dev/null
+++ b/src/stats/event-exporter-fmt-json.c
@@ -0,0 +1,249 @@
+/* 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 "json-parser.h"
+#include "hostpid.h"
+
+static void append_str(string_t *dest, const char *str)
+{
+ str_append_c(dest, '"');
+ json_append_escaped(dest, str);
+ str_append_c(dest, '"');
+}
+
+static void append_str_max_len(string_t *dest, const char *str,
+ const struct metric_export_info *info)
+{
+ str_append_c(dest, '"');
+ if (info->exporter->format_max_field_len == 0)
+ json_append_escaped(dest, str);
+ else {
+ size_t len = strlen(str);
+ json_append_escaped_data(dest, (const unsigned char *)str,
+ I_MIN(len, info->exporter->format_max_field_len));
+ if (len > info->exporter->format_max_field_len)
+ str_append(dest, "...");
+ }
+ str_append_c(dest, '"');
+}
+
+static void
+append_strlist(string_t *dest, const ARRAY_TYPE(const_string) *strlist,
+ const struct metric_export_info *info)
+{
+ const char *value;
+ bool first = TRUE;
+
+ str_append_c(dest, '[');
+ array_foreach_elem(strlist, value) {
+ if (first)
+ first = FALSE;
+ else
+ str_append_c(dest, ',');
+ append_str_max_len(dest, value, info);
+ }
+ str_append_c(dest, ']');
+}
+
+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("JSON 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:
+ str_append_c(dest, '"');
+ event_export_helper_fmt_rfc3339_time(dest, time);
+ str_append_c(dest, '"');
+ 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:
+ append_str_max_len(dest, field->value.str, info);
+ 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;
+ case EVENT_FIELD_VALUE_TYPE_STRLIST:
+ append_strlist(dest, &field->value.strlist, info);
+ break;
+ }
+}
+
+static void json_export_name(string_t *dest, struct event *event,
+ const struct metric_export_info *info)
+{
+ if ((info->include & EVENT_EXPORTER_INCL_NAME) == 0)
+ return;
+
+ append_str(dest, "event");
+ str_append_c(dest, ':');
+ append_str(dest, event->sending_name);
+ str_append_c(dest, ',');
+}
+
+static void json_export_hostname(string_t *dest,
+ const struct metric_export_info *info)
+{
+ if ((info->include & EVENT_EXPORTER_INCL_HOSTNAME) == 0)
+ return;
+
+ append_str(dest, "hostname");
+ str_append_c(dest, ':');
+ append_str(dest, my_hostname);
+ str_append_c(dest, ',');
+}
+
+static void json_export_timestamps(string_t *dest, struct event *event,
+ const struct metric_export_info *info)
+{
+ if ((info->include & EVENT_EXPORTER_INCL_TIMESTAMPS) == 0)
+ return;
+
+ append_str(dest, "start_time");
+ str_append_c(dest, ':');
+ append_time(dest, &event->tv_created, info->exporter->time_format);
+ str_append_c(dest, ',');
+
+ append_str(dest, "end_time");
+ str_append_c(dest, ':');
+ append_time(dest, &ioloop_timeval, info->exporter->time_format);
+ str_append_c(dest, ',');
+}
+
+static void json_export_categories(string_t *dest, struct event *event,
+ const struct metric_export_info *info)
+{
+ struct event_category *const *cats;
+ unsigned int count;
+
+ if ((info->include & EVENT_EXPORTER_INCL_CATEGORIES) == 0)
+ return;
+
+ append_str(dest, "categories");
+ str_append(dest, ":[");
+
+ cats = event_get_categories(event, &count);
+ event_export_helper_fmt_categories(dest, cats, count,
+ append_str, ",");
+
+ str_append(dest, "],");
+}
+
+static void json_export_fields(string_t *dest, struct event *event,
+ const struct metric_export_info *info,
+ const unsigned int fields_count,
+ const struct metric_field *fields)
+{
+ bool appended = FALSE;
+
+ if ((info->include & EVENT_EXPORTER_INCL_FIELDS) == 0)
+ return;
+
+ append_str(dest, "fields");
+ str_append(dest, ":{");
+
+ 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];
+
+ append_str(dest, field->key);
+ str_append_c(dest, ':');
+ append_field_value(dest, field, info);
+ str_append_c(dest, ',');
+
+ appended = TRUE;
+ }
+ } 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_recursive(event, name);
+ if (field == NULL)
+ continue; /* doesn't exist, skip it */
+
+ append_str(dest, name);
+ str_append_c(dest, ':');
+ append_field_value(dest, field, info);
+ str_append_c(dest, ',');
+
+ appended = TRUE;
+ }
+ }
+
+ /* remove trailing comma */
+ if (appended)
+ str_truncate(dest, str_len(dest) - 1);
+
+ str_append(dest, "},");
+}
+
+/*
+ * Serialize the event as:
+ *
+ * {
+ * "name": <event name>,
+ * "hostname": <hostname>,
+ * "start_time": <event creation timestamp>,
+ * "end_time": <event export timestamp>,
+ * "categories": [ <categories>, ... ],
+ * "fields": {
+ * <name>: <value>,
+ * ...
+ * }
+ * }
+ *
+ */
+void event_export_fmt_json(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) {
+ str_append(dest, "{}");
+ return;
+ }
+
+ str_append_c(dest, '{');
+
+ json_export_name(dest, event, info);
+ json_export_hostname(dest, info);
+ json_export_timestamps(dest, event, info);
+ json_export_categories(dest, event, info);
+ json_export_fields(dest, event, info, metric->fields_count,
+ metric->fields);
+
+ /* remove trailing comma */
+ str_truncate(dest, str_len(dest) - 1);
+
+ str_append_c(dest, '}');
+}