X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Futils_format_json.c;h=5b5fcdf0536170b5cae56c3230f90e7e95995dc9;hb=c76419c0cf983f1ecd3d36aa236cc4e3f9cff733;hp=8d46edd03418832d20277a78d82a07bac81bd4df;hpb=c1219a1c9db2e8400e2ee94b87f86ccd441485d5;p=collectd.git diff --git a/src/utils_format_json.c b/src/utils_format_json.c index 8d46edd0..5b5fcdf0 100644 --- a/src/utils_format_json.c +++ b/src/utils_format_json.c @@ -1,35 +1,51 @@ /** * collectd - src/utils_format_json.c - * Copyright (C) 2009 Florian octo Forster + * Copyright (C) 2009-2015 Florian octo Forster * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; only version 2 of the License is applicable. + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. * * Authors: - * Florian octo Forster + * Florian octo Forster **/ #include "collectd.h" + +#include "utils_format_json.h" + #include "plugin.h" #include "common.h" - #include "utils_cache.h" -#include "utils_format_json.h" -static int escape_string (char *buffer, size_t buffer_size, /* {{{ */ +#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)) @@ -51,7 +67,7 @@ static int 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] == '\\')) @@ -70,13 +86,12 @@ static int escape_string (char *buffer, size_t buffer_size, /* {{{ */ #undef BUFFER_ADD return (0); -} /* }}} int escape_string */ +} /* }}} int json_escape_string */ 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); @@ -100,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 (","); @@ -155,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); @@ -172,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 (","); @@ -192,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); @@ -209,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 (","); @@ -230,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; @@ -245,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]; @@ -257,8 +269,12 @@ static int meta_data_keys_to_json (char *buffer, size_t buffer_size, /* {{{ */ if (meta_data_get_string (meta, key, &value) == 0) { char temp[512] = ""; - escape_string (temp, sizeof (temp), value); + + status = json_escape_string (temp, sizeof (temp), value); sfree (value); + if (status != 0) + return status; + BUFFER_ADD (",\"%s\":%s", key, temp); } } @@ -288,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 ',' */ @@ -305,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); @@ -317,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); @@ -367,7 +382,7 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */ BUFFER_ADD (",\"interval\":%.3f", CDTIME_T_TO_DOUBLE (vl->interval)); #define BUFFER_ADD_KEYVAL(key, value) do { \ - status = escape_string (temp, sizeof (temp), (value)); \ + status = json_escape_string (temp, sizeof (temp), (value)); \ if (status != 0) \ return (status); \ BUFFER_ADD (",\"%s\":%s", (key), temp); \ @@ -489,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 : */