json.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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->end_host_formatting = flush_host_labels;
  26. instance->end_batch_formatting = simple_connector_end_batch;
  27. instance->prepare_header = NULL;
  28. instance->check_response = exporting_discard_response;
  29. instance->buffer = (void *)buffer_create(0);
  30. if (!instance->buffer) {
  31. error("EXPORTING: cannot create buffer for json exporting connector instance %s", instance->config.name);
  32. return 1;
  33. }
  34. simple_connector_init(instance);
  35. if (uv_mutex_init(&instance->mutex))
  36. return 1;
  37. if (uv_cond_init(&instance->cond_var))
  38. return 1;
  39. return 0;
  40. }
  41. /**
  42. * Initialize JSON connector instance for HTTP protocol
  43. *
  44. * @param instance an instance data structure.
  45. * @return Returns 0 on success, 1 on failure.
  46. */
  47. int init_json_http_instance(struct instance *instance)
  48. {
  49. instance->worker = simple_connector_worker;
  50. struct simple_connector_config *connector_specific_config = callocz(1, sizeof(struct simple_connector_config));
  51. instance->config.connector_specific_config = (void *)connector_specific_config;
  52. connector_specific_config->default_port = 5448;
  53. struct simple_connector_data *connector_specific_data = callocz(1, sizeof(struct simple_connector_data));
  54. instance->connector_specific_data = connector_specific_data;
  55. #ifdef ENABLE_HTTPS
  56. connector_specific_data->flags = NETDATA_SSL_START;
  57. connector_specific_data->conn = NULL;
  58. if (instance->config.options & EXPORTING_OPTION_USE_TLS) {
  59. security_start_ssl(NETDATA_SSL_CONTEXT_EXPORTING);
  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->end_host_formatting = flush_host_labels;
  71. instance->end_batch_formatting = close_batch_json_http;
  72. instance->prepare_header = json_http_prepare_header;
  73. instance->check_response = exporting_discard_response;
  74. instance->buffer = (void *)buffer_create(0);
  75. simple_connector_init(instance);
  76. if (uv_mutex_init(&instance->mutex))
  77. return 1;
  78. if (uv_cond_init(&instance->cond_var))
  79. return 1;
  80. return 0;
  81. }
  82. /**
  83. * Format host labels for JSON connector
  84. *
  85. * @param instance an instance data structure.
  86. * @param host a data collecting host.
  87. * @return Always returns 0.
  88. */
  89. int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
  90. {
  91. if (!instance->labels)
  92. instance->labels = buffer_create(1024);
  93. if (unlikely(!sending_labels_configured(instance)))
  94. return 0;
  95. buffer_strcat(instance->labels, "\"labels\":{");
  96. int count = 0;
  97. rrdhost_check_rdlock(host);
  98. netdata_rwlock_rdlock(&host->labels.labels_rwlock);
  99. for (struct label *label = host->labels.head; label; label = label->next) {
  100. if (!should_send_label(instance, label))
  101. continue;
  102. char value[CONFIG_MAX_VALUE * 2 + 1];
  103. sanitize_json_string(value, label->value, CONFIG_MAX_VALUE);
  104. if (count > 0)
  105. buffer_strcat(instance->labels, ",");
  106. buffer_sprintf(instance->labels, "\"%s\":\"%s\"", label->key, value);
  107. count++;
  108. }
  109. netdata_rwlock_unlock(&host->labels.labels_rwlock);
  110. buffer_strcat(instance->labels, "},");
  111. return 0;
  112. }
  113. /**
  114. * Format dimension using collected data for JSON connector
  115. *
  116. * @param instance an instance data structure.
  117. * @param rd a dimension.
  118. * @return Always returns 0.
  119. */
  120. int format_dimension_collected_json_plaintext(struct instance *instance, RRDDIM *rd)
  121. {
  122. RRDSET *st = rd->rrdset;
  123. RRDHOST *host = st->rrdhost;
  124. const char *tags_pre = "", *tags_post = "", *tags = host->tags;
  125. if (!tags)
  126. tags = "";
  127. if (*tags) {
  128. if (*tags == '{' || *tags == '[' || *tags == '"') {
  129. tags_pre = "\"host_tags\":";
  130. tags_post = ",";
  131. } else {
  132. tags_pre = "\"host_tags\":\"";
  133. tags_post = "\",";
  134. }
  135. }
  136. if (instance->config.type == EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  137. if (buffer_strlen((BUFFER *)instance->buffer) > 2)
  138. buffer_strcat(instance->buffer, ",\n");
  139. }
  140. buffer_sprintf(
  141. instance->buffer,
  142. "{"
  143. "\"prefix\":\"%s\","
  144. "\"hostname\":\"%s\","
  145. "%s%s%s"
  146. "%s"
  147. "\"chart_id\":\"%s\","
  148. "\"chart_name\":\"%s\","
  149. "\"chart_family\":\"%s\","
  150. "\"chart_context\":\"%s\","
  151. "\"chart_type\":\"%s\","
  152. "\"units\":\"%s\","
  153. "\"id\":\"%s\","
  154. "\"name\":\"%s\","
  155. "\"value\":" COLLECTED_NUMBER_FORMAT ","
  156. "\"timestamp\":%llu}",
  157. instance->config.prefix,
  158. (host == localhost) ? instance->config.hostname : host->hostname,
  159. tags_pre,
  160. tags,
  161. tags_post,
  162. instance->labels ? buffer_tostring(instance->labels) : "",
  163. st->id,
  164. st->name,
  165. st->family,
  166. st->context,
  167. st->type,
  168. st->units,
  169. rd->id,
  170. rd->name,
  171. rd->last_collected_value,
  172. (unsigned long long)rd->last_collected_time.tv_sec);
  173. if (instance->config.type != EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  174. buffer_strcat(instance->buffer, "\n");
  175. }
  176. return 0;
  177. }
  178. /**
  179. * Format dimension using a calculated value from stored data for JSON connector
  180. *
  181. * @param instance an instance data structure.
  182. * @param rd a dimension.
  183. * @return Always returns 0.
  184. */
  185. int format_dimension_stored_json_plaintext(struct instance *instance, RRDDIM *rd)
  186. {
  187. RRDSET *st = rd->rrdset;
  188. RRDHOST *host = st->rrdhost;
  189. time_t last_t;
  190. calculated_number value = exporting_calculate_value_from_stored_data(instance, rd, &last_t);
  191. if(isnan(value))
  192. return 0;
  193. const char *tags_pre = "", *tags_post = "", *tags = host->tags;
  194. if (!tags)
  195. tags = "";
  196. if (*tags) {
  197. if (*tags == '{' || *tags == '[' || *tags == '"') {
  198. tags_pre = "\"host_tags\":";
  199. tags_post = ",";
  200. } else {
  201. tags_pre = "\"host_tags\":\"";
  202. tags_post = "\",";
  203. }
  204. }
  205. if (instance->config.type == EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  206. if (buffer_strlen((BUFFER *)instance->buffer) > 2)
  207. buffer_strcat(instance->buffer, ",\n");
  208. }
  209. buffer_sprintf(
  210. instance->buffer,
  211. "{"
  212. "\"prefix\":\"%s\","
  213. "\"hostname\":\"%s\","
  214. "%s%s%s"
  215. "%s"
  216. "\"chart_id\":\"%s\","
  217. "\"chart_name\":\"%s\","
  218. "\"chart_family\":\"%s\","
  219. "\"chart_context\": \"%s\","
  220. "\"chart_type\":\"%s\","
  221. "\"units\": \"%s\","
  222. "\"id\":\"%s\","
  223. "\"name\":\"%s\","
  224. "\"value\":" CALCULATED_NUMBER_FORMAT ","
  225. "\"timestamp\": %llu}",
  226. instance->config.prefix,
  227. (host == localhost) ? instance->config.hostname : host->hostname,
  228. tags_pre,
  229. tags,
  230. tags_post,
  231. instance->labels ? buffer_tostring(instance->labels) : "",
  232. st->id,
  233. st->name,
  234. st->family,
  235. st->context,
  236. st->type,
  237. st->units,
  238. rd->id,
  239. rd->name,
  240. value,
  241. (unsigned long long)last_t);
  242. if (instance->config.type != EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
  243. buffer_strcat(instance->buffer, "\n");
  244. }
  245. return 0;
  246. }
  247. /**
  248. * Open a JSON list for a bach
  249. *
  250. * @param instance an instance data structure.
  251. * @return Always returns 0.
  252. */
  253. int open_batch_json_http(struct instance *instance)
  254. {
  255. buffer_strcat(instance->buffer, "[\n");
  256. return 0;
  257. }
  258. /**
  259. * Close a JSON list for a bach and update buffered bytes counter
  260. *
  261. * @param instance an instance data structure.
  262. * @return Always returns 0.
  263. */
  264. int close_batch_json_http(struct instance *instance)
  265. {
  266. buffer_strcat(instance->buffer, "\n]\n");
  267. simple_connector_end_batch(instance);
  268. return 0;
  269. }
  270. /**
  271. * Prepare HTTP header
  272. *
  273. * @param instance an instance data structure.
  274. * @return Returns 0 on success, 1 on failure.
  275. */
  276. void json_http_prepare_header(struct instance *instance)
  277. {
  278. struct simple_connector_data *simple_connector_data = instance->connector_specific_data;
  279. buffer_sprintf(
  280. simple_connector_data->last_buffer->header,
  281. "POST /api/put HTTP/1.1\r\n"
  282. "Host: %s\r\n"
  283. "%s"
  284. "Content-Type: application/json\r\n"
  285. "Content-Length: %lu\r\n"
  286. "\r\n",
  287. instance->config.destination,
  288. simple_connector_data->auth_string ? simple_connector_data->auth_string : "",
  289. buffer_strlen(simple_connector_data->last_buffer->buffer));
  290. return;
  291. }