json.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "json.h"
  3. /**
  4. * Initialize JSON connector instance
  5. *
  6. * @param instance an instance data structure.
  7. * @return Returns 0 on success, 1 on failure.
  8. */
  9. int init_json_instance(struct instance *instance)
  10. {
  11. instance->worker = simple_connector_worker;
  12. struct simple_connector_config *connector_specific_config = callocz(1, sizeof(struct simple_connector_config));
  13. instance->config.connector_specific_config = (void *)connector_specific_config;
  14. connector_specific_config->default_port = 5448;
  15. struct simple_connector_data *connector_specific_data = callocz(1, sizeof(struct simple_connector_data));
  16. instance->connector_specific_data = connector_specific_data;
  17. instance->start_batch_formatting = NULL;
  18. instance->start_host_formatting = format_host_labels_json_plaintext;
  19. instance->start_chart_formatting = NULL;
  20. if (EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED)
  21. instance->metric_formatting = format_dimension_collected_json_plaintext;
  22. else
  23. instance->metric_formatting = format_dimension_stored_json_plaintext;
  24. instance->end_chart_formatting = NULL;
  25. instance->variables_formatting = NULL;
  26. instance->end_host_formatting = flush_host_labels;
  27. instance->end_batch_formatting = simple_connector_end_batch;
  28. instance->prepare_header = NULL;
  29. instance->check_response = exporting_discard_response;
  30. instance->buffer = (void *)buffer_create(0, &netdata_buffers_statistics.buffers_exporters);
  31. if (!instance->buffer) {
  32. netdata_log_error("EXPORTING: cannot create buffer for json exporting connector instance %s", instance->config.name);
  33. return 1;
  34. }
  35. simple_connector_init(instance);
  36. if (uv_mutex_init(&instance->mutex))
  37. return 1;
  38. if (uv_cond_init(&instance->cond_var))
  39. return 1;
  40. return 0;
  41. }
  42. /**
  43. * Initialize JSON connector instance for HTTP protocol
  44. *
  45. * @param instance an instance data structure.
  46. * @return Returns 0 on success, 1 on failure.
  47. */
  48. int init_json_http_instance(struct instance *instance)
  49. {
  50. instance->worker = simple_connector_worker;
  51. struct simple_connector_config *connector_specific_config = callocz(1, sizeof(struct simple_connector_config));
  52. instance->config.connector_specific_config = (void *)connector_specific_config;
  53. connector_specific_config->default_port = 5448;
  54. struct simple_connector_data *connector_specific_data = callocz(1, sizeof(struct simple_connector_data));
  55. instance->connector_specific_data = connector_specific_data;
  56. #ifdef ENABLE_HTTPS
  57. connector_specific_data->ssl = NETDATA_SSL_UNSET_CONNECTION;
  58. if (instance->config.options & EXPORTING_OPTION_USE_TLS) {
  59. netdata_ssl_initialize_ctx(NETDATA_SSL_EXPORTING_CTX);
  60. }
  61. #endif
  62. instance->start_batch_formatting = open_batch_json_http;
  63. instance->start_host_formatting = format_host_labels_json_plaintext;
  64. instance->start_chart_formatting = NULL;
  65. if (EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED)
  66. instance->metric_formatting = format_dimension_collected_json_plaintext;
  67. else
  68. instance->metric_formatting = format_dimension_stored_json_plaintext;
  69. instance->end_chart_formatting = NULL;
  70. instance->variables_formatting = NULL;
  71. instance->end_host_formatting = flush_host_labels;
  72. instance->end_batch_formatting = close_batch_json_http;
  73. instance->prepare_header = json_http_prepare_header;
  74. instance->check_response = exporting_discard_response;
  75. instance->buffer = (void *)buffer_create(0, &netdata_buffers_statistics.buffers_exporters);
  76. simple_connector_init(instance);
  77. if (uv_mutex_init(&instance->mutex))
  78. return 1;
  79. if (uv_cond_init(&instance->cond_var))
  80. return 1;
  81. return 0;
  82. }
  83. /**
  84. * Format host labels for JSON connector
  85. *
  86. * @param instance an instance data structure.
  87. * @param host a data collecting host.
  88. * @return Always returns 0.
  89. */
  90. int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
  91. {
  92. if (!instance->labels_buffer)
  93. instance->labels_buffer = buffer_create(1024, &netdata_buffers_statistics.buffers_exporters);
  94. if (unlikely(!sending_labels_configured(instance)))
  95. return 0;
  96. buffer_strcat(instance->labels_buffer, "\"labels\":{");
  97. rrdlabels_to_buffer(host->rrdlabels, instance->labels_buffer, "", ":", "\"", ",",
  98. exporting_labels_filter_callback, instance,
  99. NULL, sanitize_json_string);
  100. buffer_strcat(instance->labels_buffer, "},");
  101. return 0;
  102. }
  103. /**
  104. * Format dimension using collected data for JSON connector
  105. *
  106. * @param instance an instance data structure.
  107. * @param rd a dimension.
  108. * @return Always returns 0.
  109. */
  110. int format_dimension_collected_json_plaintext(struct instance *instance, RRDDIM *rd)
  111. {
  112. RRDSET *st = rd->rrdset;
  113. RRDHOST *host = st->rrdhost;
  114. const char *tags_pre = "", *tags_post = "", *tags = rrdhost_tags(host);
  115. if (!tags)
  116. tags = "";
  117. if (*tags) {
  118. if (*tags == '{' || *tags == '[' || *tags == '"') {
  119. tags_pre = "\"host_tags\":";
  120. tags_post = ",";
  121. } else {
  122. tags_pre = "\"host_tags\":\"";
  123. tags_post = "\",";
  124. }
  125. }
  126. if (instance->config.type == EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  127. if (buffer_strlen((BUFFER *)instance->buffer) > 2)
  128. buffer_strcat(instance->buffer, ",\n");
  129. }
  130. buffer_sprintf(
  131. instance->buffer,
  132. "{"
  133. "\"prefix\":\"%s\","
  134. "\"hostname\":\"%s\","
  135. "%s%s%s"
  136. "%s"
  137. "\"chart_id\":\"%s\","
  138. "\"chart_name\":\"%s\","
  139. "\"chart_family\":\"%s\","
  140. "\"chart_context\":\"%s\","
  141. "\"chart_type\":\"%s\","
  142. "\"units\":\"%s\","
  143. "\"id\":\"%s\","
  144. "\"name\":\"%s\","
  145. "\"value\":" COLLECTED_NUMBER_FORMAT ","
  146. "\"timestamp\":%llu}",
  147. instance->config.prefix,
  148. (host == localhost) ? instance->config.hostname : rrdhost_hostname(host),
  149. tags_pre,
  150. tags,
  151. tags_post,
  152. instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "",
  153. rrdset_id(st),
  154. rrdset_name(st),
  155. rrdset_family(st),
  156. rrdset_context(st),
  157. rrdset_parts_type(st),
  158. rrdset_units(st),
  159. rrddim_id(rd),
  160. rrddim_name(rd),
  161. rd->collector.last_collected_value,
  162. (unsigned long long)rd->collector.last_collected_time.tv_sec);
  163. if (instance->config.type != EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  164. buffer_strcat(instance->buffer, "\n");
  165. }
  166. return 0;
  167. }
  168. /**
  169. * Format dimension using a calculated value from stored data for JSON connector
  170. *
  171. * @param instance an instance data structure.
  172. * @param rd a dimension.
  173. * @return Always returns 0.
  174. */
  175. int format_dimension_stored_json_plaintext(struct instance *instance, RRDDIM *rd)
  176. {
  177. RRDSET *st = rd->rrdset;
  178. RRDHOST *host = st->rrdhost;
  179. time_t last_t;
  180. NETDATA_DOUBLE value = exporting_calculate_value_from_stored_data(instance, rd, &last_t);
  181. if(isnan(value))
  182. return 0;
  183. const char *tags_pre = "", *tags_post = "", *tags = rrdhost_tags(host);
  184. if (!tags)
  185. tags = "";
  186. if (*tags) {
  187. if (*tags == '{' || *tags == '[' || *tags == '"') {
  188. tags_pre = "\"host_tags\":";
  189. tags_post = ",";
  190. } else {
  191. tags_pre = "\"host_tags\":\"";
  192. tags_post = "\",";
  193. }
  194. }
  195. if (instance->config.type == EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  196. if (buffer_strlen((BUFFER *)instance->buffer) > 2)
  197. buffer_strcat(instance->buffer, ",\n");
  198. }
  199. buffer_sprintf(
  200. instance->buffer,
  201. "{"
  202. "\"prefix\":\"%s\","
  203. "\"hostname\":\"%s\","
  204. "%s%s%s"
  205. "%s"
  206. "\"chart_id\":\"%s\","
  207. "\"chart_name\":\"%s\","
  208. "\"chart_family\":\"%s\","
  209. "\"chart_context\": \"%s\","
  210. "\"chart_type\":\"%s\","
  211. "\"units\": \"%s\","
  212. "\"id\":\"%s\","
  213. "\"name\":\"%s\","
  214. "\"value\":" NETDATA_DOUBLE_FORMAT ","
  215. "\"timestamp\": %llu}",
  216. instance->config.prefix,
  217. (host == localhost) ? instance->config.hostname : rrdhost_hostname(host),
  218. tags_pre,
  219. tags,
  220. tags_post,
  221. instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "",
  222. rrdset_id(st),
  223. rrdset_name(st),
  224. rrdset_family(st),
  225. rrdset_context(st),
  226. rrdset_parts_type(st),
  227. rrdset_units(st),
  228. rrddim_id(rd),
  229. rrddim_name(rd),
  230. value,
  231. (unsigned long long)last_t);
  232. if (instance->config.type != EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  233. buffer_strcat(instance->buffer, "\n");
  234. }
  235. return 0;
  236. }
  237. /**
  238. * Open a JSON list for a bach
  239. *
  240. * @param instance an instance data structure.
  241. * @return Always returns 0.
  242. */
  243. int open_batch_json_http(struct instance *instance)
  244. {
  245. buffer_strcat(instance->buffer, "[\n");
  246. return 0;
  247. }
  248. /**
  249. * Close a JSON list for a bach and update buffered bytes counter
  250. *
  251. * @param instance an instance data structure.
  252. * @return Always returns 0.
  253. */
  254. int close_batch_json_http(struct instance *instance)
  255. {
  256. buffer_strcat(instance->buffer, "\n]\n");
  257. simple_connector_end_batch(instance);
  258. return 0;
  259. }
  260. /**
  261. * Prepare HTTP header
  262. *
  263. * @param instance an instance data structure.
  264. * @return Returns 0 on success, 1 on failure.
  265. */
  266. void json_http_prepare_header(struct instance *instance)
  267. {
  268. struct simple_connector_data *simple_connector_data = instance->connector_specific_data;
  269. buffer_sprintf(
  270. simple_connector_data->last_buffer->header,
  271. "POST /api/put HTTP/1.1\r\n"
  272. "Host: %s\r\n"
  273. "%s"
  274. "Content-Type: application/json\r\n"
  275. "Content-Length: %lu\r\n"
  276. "\r\n",
  277. instance->config.destination,
  278. simple_connector_data->auth_string ? simple_connector_data->auth_string : "",
  279. (unsigned long int) buffer_strlen(simple_connector_data->last_buffer->buffer));
  280. return;
  281. }