+#if HAVE_LIBYAJL
+static int json_add_string(yajl_gen g, char const *str) /* {{{ */
+{
+ if (str == NULL)
+ return (int)yajl_gen_null(g);
+
+ return (int)yajl_gen_string(g, (unsigned char const *)str,
+ (unsigned int)strlen(str));
+} /* }}} int json_add_string */
+
+#define JSON_ADD(g, str) \
+ do { \
+ yajl_gen_status status = json_add_string(g, str); \
+ if (status != yajl_gen_status_ok) { \
+ return -1; \
+ } \
+ } while (0)
+
+#define JSON_ADDF(g, format, ...) \
+ do { \
+ char *str = ssnprintf_alloc(format, __VA_ARGS__); \
+ yajl_gen_status status = json_add_string(g, str); \
+ free(str); \
+ if (status != yajl_gen_status_ok) { \
+ return -1; \
+ } \
+ } while (0)
+
+static int format_json_meta(yajl_gen g, notification_meta_t *meta) /* {{{ */
+{
+ if (meta == NULL)
+ return 0;
+
+ JSON_ADD(g, meta->name);
+ switch (meta->type) {
+ case NM_TYPE_STRING:
+ JSON_ADD(g, meta->nm_value.nm_string);
+ break;
+ case NM_TYPE_SIGNED_INT:
+ JSON_ADDF(g, "%" PRIi64, meta->nm_value.nm_signed_int);
+ break;
+ case NM_TYPE_UNSIGNED_INT:
+ JSON_ADDF(g, "%" PRIu64, meta->nm_value.nm_unsigned_int);
+ break;
+ case NM_TYPE_DOUBLE:
+ JSON_ADDF(g, JSON_GAUGE_FORMAT, meta->nm_value.nm_double);
+ break;
+ case NM_TYPE_BOOLEAN:
+ JSON_ADD(g, meta->nm_value.nm_boolean ? "true" : "false");
+ break;
+ default:
+ ERROR("format_json_meta: unknown meta data type %d (name \"%s\")",
+ meta->type, meta->name);
+ yajl_gen_null(g);
+ }
+
+ return format_json_meta(g, meta->next);
+} /* }}} int format_json_meta */
+
+static int format_time(yajl_gen g, cdtime_t t) /* {{{ */
+{
+ char buffer[RFC3339NANO_SIZE] = "";
+
+ if (rfc3339nano(buffer, sizeof(buffer), t) != 0)
+ return -1;
+
+ JSON_ADD(g, buffer);
+ return 0;
+} /* }}} int format_time */
+
+static int format_alert(yajl_gen g, notification_t const *n) /* {{{ */
+{
+ yajl_gen_array_open(g);
+ yajl_gen_map_open(g); /* BEGIN alert */
+
+ /*
+ * labels
+ */
+ JSON_ADD(g, "labels");
+ yajl_gen_map_open(g); /* BEGIN labels */
+
+ JSON_ADD(g, "alertname");
+ if (strncmp(n->plugin, n->type, strlen(n->plugin)) == 0)
+ JSON_ADDF(g, "collectd_%s", n->type);
+ else
+ JSON_ADDF(g, "collectd_%s_%s", n->plugin, n->type);
+
+ JSON_ADD(g, "instance");
+ JSON_ADD(g, n->host);
+
+ /* mangling of plugin instance and type instance into labels is copied from
+ * the Prometheus collectd exporter. */
+ if (strlen(n->plugin_instance) > 0) {
+ JSON_ADD(g, n->plugin);
+ JSON_ADD(g, n->plugin_instance);
+ }
+ if (strlen(n->type_instance) > 0) {
+ if (strlen(n->plugin_instance) > 0)
+ JSON_ADD(g, "type");
+ else
+ JSON_ADD(g, n->plugin);
+ JSON_ADD(g, n->type_instance);
+ }
+
+ JSON_ADD(g, "severity");
+ JSON_ADD(g, (n->severity == NOTIF_FAILURE)
+ ? "FAILURE"
+ : (n->severity == NOTIF_WARNING)
+ ? "WARNING"
+ : (n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN");
+
+ JSON_ADD(g, "service");
+ JSON_ADD(g, "collectd");
+
+ yajl_gen_map_close(g); /* END labels */
+
+ /*
+ * annotations
+ */
+ JSON_ADD(g, "annotations");
+ yajl_gen_map_open(g); /* BEGIN annotations */
+
+ JSON_ADD(g, "summary");
+ JSON_ADD(g, n->message);
+
+ if (format_json_meta(g, n->meta) != 0)
+ return -1;
+
+ yajl_gen_map_close(g); /* END annotations */
+
+ JSON_ADD(g, "startsAt");
+ format_time(g, n->time);
+
+ yajl_gen_map_close(g); /* END alert */
+ yajl_gen_array_close(g);
+
+ return 0;
+} /* }}} format_alert */
+
+/*
+ * Format (prometheus/alertmanager v1):
+ *
+ * [{
+ * "labels": {
+ * "alertname": "collectd_cpu",
+ * "instance": "host.example.com",
+ * "severity": "FAILURE",
+ * "service": "collectd",
+ * "cpu": "0",
+ * "type": "wait"
+ * },
+ * "annotations": {
+ * "summary": "...",
+ * // meta
+ * },
+ * "startsAt": <rfc3339 time>,
+ * "endsAt": <rfc3339 time>, // not used
+ * }]
+ */
+int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
+ notification_t const *n) {
+ yajl_gen g;
+ unsigned char const *out;
+#if HAVE_YAJL_V2
+ size_t unused_out_len;
+#else
+ unsigned int unused_out_len;
+#endif
+
+ if ((buffer == NULL) || (n == NULL))
+ return EINVAL;
+
+#if HAVE_YAJL_V2
+ g = yajl_gen_alloc(NULL);
+ if (g == NULL)
+ return -1;
+#if COLLECT_DEBUG
+ yajl_gen_config(g, yajl_gen_beautify, 1);
+ yajl_gen_config(g, yajl_gen_validate_utf8, 1);
+#endif
+
+#else /* !HAVE_YAJL_V2 */
+ yajl_gen_config conf = {0};
+#if COLLECT_DEBUG
+ conf.beautify = 1;
+ conf.indentString = " ";
+#endif
+ g = yajl_gen_alloc(&conf, NULL);
+ if (g == NULL)
+ return -1;
+#endif
+
+ if (format_alert(g, n) != 0) {
+ yajl_gen_clear(g);
+ yajl_gen_free(g);
+ return -1;
+ }
+
+ /* copy to output buffer */
+ yajl_gen_get_buf(g, &out, &unused_out_len);
+ sstrncpy(buffer, (void *)out, buffer_size);
+
+ yajl_gen_clear(g);
+ yajl_gen_free(g);
+ return 0;
+} /* }}} format_json_notification */
+#else
+int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
+ notification_t const *n) {
+ ERROR("format_json_notification: Not available (requires libyajl).");
+ return ENOTSUP;
+} /* }}} int format_json_notification */
+#endif
+