X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Futils_format_json.c;h=5b5fcdf0536170b5cae56c3230f90e7e95995dc9;hb=c76419c0cf983f1ecd3d36aa236cc4e3f9cff733;hp=020d80beacb5f8401d26ec891a202cd2da226db1;hpb=f14feb1eddfe5760a64640b98ab7bbc5c493f614;p=collectd.git diff --git a/src/utils_format_json.c b/src/utils_format_json.c index 020d80be..5b5fcdf0 100644 --- a/src/utils_format_json.c +++ b/src/utils_format_json.c @@ -1,6 +1,6 @@ /** * collectd - src/utils_format_json.c - * Copyright (C) 2009 Florian octo Forster + * Copyright (C) 2009-2015 Florian octo Forster * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -25,16 +25,27 @@ **/ #include "collectd.h" + +#include "utils_format_json.h" + #include "plugin.h" #include "common.h" - #include "utils_cache.h" -#include "utils_format_json.h" + +#if HAVE_LIBYAJL +# include +# include +# if HAVE_YAJL_YAJL_VERSION_H +# include +# endif +# if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1) +# define HAVE_YAJL_V2 1 +# endif +#endif static int json_escape_string (char *buffer, size_t buffer_size, /* {{{ */ const char *string) { - size_t src_pos; size_t dst_pos; if ((buffer == NULL) || (string == NULL)) @@ -56,7 +67,7 @@ static int json_escape_string (char *buffer, size_t buffer_size, /* {{{ */ /* Escape special characters */ BUFFER_ADD ('"'); - for (src_pos = 0; string[src_pos] != 0; src_pos++) + for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) { if ((string[src_pos] == '"') || (string[src_pos] == '\\')) @@ -81,7 +92,6 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */ const data_set_t *ds, const value_list_t *vl, int store_rates) { size_t offset = 0; - int i; gauge_t *rates = NULL; memset (buffer, 0, buffer_size); @@ -105,7 +115,7 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */ } while (0) BUFFER_ADD ("["); - for (i = 0; i < ds->ds_num; i++) + for (size_t i = 0; i < ds->ds_num; i++) { if (i > 0) BUFFER_ADD (","); @@ -160,7 +170,6 @@ static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */ const data_set_t *ds) { size_t offset = 0; - int i; memset (buffer, 0, buffer_size); @@ -177,7 +186,7 @@ static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */ } while (0) BUFFER_ADD ("["); - for (i = 0; i < ds->ds_num; i++) + for (size_t i = 0; i < ds->ds_num; i++) { if (i > 0) BUFFER_ADD (","); @@ -197,7 +206,6 @@ static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */ const data_set_t *ds) { size_t offset = 0; - int i; memset (buffer, 0, buffer_size); @@ -214,7 +222,7 @@ static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */ } while (0) BUFFER_ADD ("["); - for (i = 0; i < ds->ds_num; i++) + for (size_t i = 0; i < ds->ds_num; i++) { if (i > 0) BUFFER_ADD (","); @@ -235,7 +243,6 @@ static int meta_data_keys_to_json (char *buffer, size_t buffer_size, /* {{{ */ { size_t offset = 0; int status; - size_t i; buffer[0] = 0; @@ -250,7 +257,7 @@ static int meta_data_keys_to_json (char *buffer, size_t buffer_size, /* {{{ */ offset += ((size_t) status); \ } while (0) - for (i = 0; i < keys_num; ++i) + for (size_t i = 0; i < keys_num; ++i) { int type; char *key = keys[i]; @@ -297,7 +304,7 @@ static int meta_data_keys_to_json (char *buffer, size_t buffer_size, /* {{{ */ } } /* for (keys) */ - if (offset <= 0) + if (offset == 0) return (ENOENT); buffer[0] = '{'; /* replace leading ',' */ @@ -314,7 +321,6 @@ static int meta_data_to_json (char *buffer, size_t buffer_size, /* {{{ */ char **keys = NULL; size_t keys_num; int status; - size_t i; if ((buffer == NULL) || (buffer_size == 0) || (meta == NULL)) return (EINVAL); @@ -326,7 +332,7 @@ static int meta_data_to_json (char *buffer, size_t buffer_size, /* {{{ */ status = meta_data_keys_to_json (buffer, buffer_size, meta, keys, keys_num); - for (i = 0; i < keys_num; ++i) + for (size_t i = 0; i < keys_num; ++i) sfree (keys[i]); sfree (keys); @@ -498,4 +504,215 @@ int format_json_value_list (char *buffer, /* {{{ */ store_rates, (*ret_buffer_free) - 2)); } /* }}} int format_json_value_list */ +#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": , + * "endsAt": , // 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 + /* vim: set sw=2 sts=2 et fdm=marker : */