json.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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);
  31. if (!instance->buffer) {
  32. 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->flags = NETDATA_SSL_START;
  58. connector_specific_data->conn = NULL;
  59. if (instance->config.options & EXPORTING_OPTION_USE_TLS) {
  60. security_start_ssl(NETDATA_SSL_CONTEXT_EXPORTING);
  61. }
  62. #endif
  63. instance->start_batch_formatting = open_batch_json_http;
  64. instance->start_host_formatting = format_host_labels_json_plaintext;
  65. instance->start_chart_formatting = NULL;
  66. if (EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED)
  67. instance->metric_formatting = format_dimension_collected_json_plaintext;
  68. else
  69. instance->metric_formatting = format_dimension_stored_json_plaintext;
  70. instance->end_chart_formatting = NULL;
  71. instance->variables_formatting = NULL;
  72. instance->end_host_formatting = flush_host_labels;
  73. instance->end_batch_formatting = close_batch_json_http;
  74. instance->prepare_header = json_http_prepare_header;
  75. instance->check_response = exporting_discard_response;
  76. instance->buffer = (void *)buffer_create(0);
  77. simple_connector_init(instance);
  78. if (uv_mutex_init(&instance->mutex))
  79. return 1;
  80. if (uv_cond_init(&instance->cond_var))
  81. return 1;
  82. return 0;
  83. }
  84. /**
  85. * Format host labels for JSON connector
  86. *
  87. * @param instance an instance data structure.
  88. * @param host a data collecting host.
  89. * @return Always returns 0.
  90. */
  91. int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
  92. {
  93. if (!instance->labels_buffer)
  94. instance->labels_buffer = buffer_create(1024);
  95. if (unlikely(!sending_labels_configured(instance)))
  96. return 0;
  97. buffer_strcat(instance->labels_buffer, "\"labels\":{");
  98. rrdlabels_to_buffer(host->host_labels, instance->labels_buffer, "", ":", "\"", ",",
  99. exporting_labels_filter_callback, instance,
  100. NULL, sanitize_json_string);
  101. buffer_strcat(instance->labels_buffer, "},");
  102. return 0;
  103. }
  104. /**
  105. * Format dimension using collected data for JSON connector
  106. *
  107. * @param instance an instance data structure.
  108. * @param rd a dimension.
  109. * @return Always returns 0.
  110. */
  111. int format_dimension_collected_json_plaintext(struct instance *instance, RRDDIM *rd)
  112. {
  113. RRDSET *st = rd->rrdset;
  114. RRDHOST *host = st->rrdhost;
  115. const char *tags_pre = "", *tags_post = "", *tags = host->tags;
  116. if (!tags)
  117. tags = "";
  118. if (*tags) {
  119. if (*tags == '{' || *tags == '[' || *tags == '"') {
  120. tags_pre = "\"host_tags\":";
  121. tags_post = ",";
  122. } else {
  123. tags_pre = "\"host_tags\":\"";
  124. tags_post = "\",";
  125. }
  126. }
  127. if (instance->config.type == EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  128. if (buffer_strlen((BUFFER *)instance->buffer) > 2)
  129. buffer_strcat(instance->buffer, ",\n");
  130. }
  131. buffer_sprintf(
  132. instance->buffer,
  133. "{"
  134. "\"prefix\":\"%s\","
  135. "\"hostname\":\"%s\","
  136. "%s%s%s"
  137. "%s"
  138. "\"chart_id\":\"%s\","
  139. "\"chart_name\":\"%s\","
  140. "\"chart_family\":\"%s\","
  141. "\"chart_context\":\"%s\","
  142. "\"chart_type\":\"%s\","
  143. "\"units\":\"%s\","
  144. "\"id\":\"%s\","
  145. "\"name\":\"%s\","
  146. "\"value\":" COLLECTED_NUMBER_FORMAT ","
  147. "\"timestamp\":%llu}",
  148. instance->config.prefix,
  149. (host == localhost) ? instance->config.hostname : host->hostname,
  150. tags_pre,
  151. tags,
  152. tags_post,
  153. instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "",
  154. st->id,
  155. st->name,
  156. st->family,
  157. st->context,
  158. st->type,
  159. st->units,
  160. rd->id,
  161. rd->name,
  162. rd->last_collected_value,
  163. (unsigned long long)rd->last_collected_time.tv_sec);
  164. if (instance->config.type != EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  165. buffer_strcat(instance->buffer, "\n");
  166. }
  167. return 0;
  168. }
  169. /**
  170. * Format dimension using a calculated value from stored data for JSON connector
  171. *
  172. * @param instance an instance data structure.
  173. * @param rd a dimension.
  174. * @return Always returns 0.
  175. */
  176. int format_dimension_stored_json_plaintext(struct instance *instance, RRDDIM *rd)
  177. {
  178. RRDSET *st = rd->rrdset;
  179. RRDHOST *host = st->rrdhost;
  180. time_t last_t;
  181. NETDATA_DOUBLE value = exporting_calculate_value_from_stored_data(instance, rd, &last_t);
  182. if(isnan(value))
  183. return 0;
  184. const char *tags_pre = "", *tags_post = "", *tags = host->tags;
  185. if (!tags)
  186. tags = "";
  187. if (*tags) {
  188. if (*tags == '{' || *tags == '[' || *tags == '"') {
  189. tags_pre = "\"host_tags\":";
  190. tags_post = ",";
  191. } else {
  192. tags_pre = "\"host_tags\":\"";
  193. tags_post = "\",";
  194. }
  195. }
  196. if (instance->config.type == EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  197. if (buffer_strlen((BUFFER *)instance->buffer) > 2)
  198. buffer_strcat(instance->buffer, ",\n");
  199. }
  200. buffer_sprintf(
  201. instance->buffer,
  202. "{"
  203. "\"prefix\":\"%s\","
  204. "\"hostname\":\"%s\","
  205. "%s%s%s"
  206. "%s"
  207. "\"chart_id\":\"%s\","
  208. "\"chart_name\":\"%s\","
  209. "\"chart_family\":\"%s\","
  210. "\"chart_context\": \"%s\","
  211. "\"chart_type\":\"%s\","
  212. "\"units\": \"%s\","
  213. "\"id\":\"%s\","
  214. "\"name\":\"%s\","
  215. "\"value\":" NETDATA_DOUBLE_FORMAT ","
  216. "\"timestamp\": %llu}",
  217. instance->config.prefix,
  218. (host == localhost) ? instance->config.hostname : host->hostname,
  219. tags_pre,
  220. tags,
  221. tags_post,
  222. instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "",
  223. st->id,
  224. st->name,
  225. st->family,
  226. st->context,
  227. st->type,
  228. st->units,
  229. rd->id,
  230. rd->name,
  231. value,
  232. (unsigned long long)last_t);
  233. if (instance->config.type != EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  234. buffer_strcat(instance->buffer, "\n");
  235. }
  236. return 0;
  237. }
  238. /**
  239. * Open a JSON list for a bach
  240. *
  241. * @param instance an instance data structure.
  242. * @return Always returns 0.
  243. */
  244. int open_batch_json_http(struct instance *instance)
  245. {
  246. buffer_strcat(instance->buffer, "[\n");
  247. return 0;
  248. }
  249. /**
  250. * Close a JSON list for a bach and update buffered bytes counter
  251. *
  252. * @param instance an instance data structure.
  253. * @return Always returns 0.
  254. */
  255. int close_batch_json_http(struct instance *instance)
  256. {
  257. buffer_strcat(instance->buffer, "\n]\n");
  258. simple_connector_end_batch(instance);
  259. return 0;
  260. }
  261. /**
  262. * Prepare HTTP header
  263. *
  264. * @param instance an instance data structure.
  265. * @return Returns 0 on success, 1 on failure.
  266. */
  267. void json_http_prepare_header(struct instance *instance)
  268. {
  269. struct simple_connector_data *simple_connector_data = instance->connector_specific_data;
  270. buffer_sprintf(
  271. simple_connector_data->last_buffer->header,
  272. "POST /api/put HTTP/1.1\r\n"
  273. "Host: %s\r\n"
  274. "%s"
  275. "Content-Type: application/json\r\n"
  276. "Content-Length: %lu\r\n"
  277. "\r\n",
  278. instance->config.destination,
  279. simple_connector_data->auth_string ? simple_connector_data->auth_string : "",
  280. buffer_strlen(simple_connector_data->last_buffer->buffer));
  281. return;
  282. }