json_wrapper.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "json_wrapper.h"
  3. struct value_output {
  4. int c;
  5. BUFFER *wb;
  6. };
  7. static int value_list_output(const char *name, void *entry, void *data) {
  8. (void)name;
  9. struct value_output *ap = (struct value_output *)data;
  10. BUFFER *wb = ap->wb;
  11. char *output = (char *) entry;
  12. if(ap->c) buffer_strcat(wb, ",");
  13. buffer_strcat(wb, output);
  14. (ap->c)++;
  15. return 0;
  16. }
  17. void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value,
  18. QUERY_PARAMS *rrdset_query_data)
  19. {
  20. struct context_param *context_param_list = rrdset_query_data->context_param_list;
  21. char *chart_label_key = rrdset_query_data->chart_label_key;
  22. RRDDIM *temp_rd = context_param_list ? context_param_list->rd : NULL;
  23. int should_lock = (!context_param_list || !(context_param_list->flags & CONTEXT_FLAGS_ARCHIVE));
  24. uint8_t context_mode = (!context_param_list || (context_param_list->flags & CONTEXT_FLAGS_CONTEXT));
  25. if (should_lock)
  26. rrdset_check_rdlock(r->st);
  27. long rows = rrdr_rows(r);
  28. long c, i;
  29. RRDDIM *rd;
  30. //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
  31. char kq[2] = "", // key quote
  32. sq[2] = ""; // string quote
  33. if( options & RRDR_OPTION_GOOGLE_JSON ) {
  34. kq[0] = '\0';
  35. sq[0] = '\'';
  36. }
  37. else {
  38. kq[0] = '"';
  39. sq[0] = '"';
  40. }
  41. if (should_lock)
  42. rrdset_rdlock(r->st);
  43. buffer_sprintf(wb, "{\n"
  44. " %sapi%s: 1,\n"
  45. " %sid%s: %s%s%s,\n"
  46. " %sname%s: %s%s%s,\n"
  47. " %sview_update_every%s: %d,\n"
  48. " %supdate_every%s: %d,\n"
  49. " %sfirst_entry%s: %u,\n"
  50. " %slast_entry%s: %u,\n"
  51. " %sbefore%s: %u,\n"
  52. " %safter%s: %u,\n"
  53. " %sdimension_names%s: ["
  54. , kq, kq
  55. , kq, kq, sq, context_mode && temp_rd?r->st->context:r->st->id, sq
  56. , kq, kq, sq, context_mode && temp_rd?r->st->context:r->st->name, sq
  57. , kq, kq, r->update_every
  58. , kq, kq, r->st->update_every
  59. , kq, kq, (uint32_t) (context_param_list ? context_param_list->first_entry_t : rrdset_first_entry_t_nolock(r->st))
  60. , kq, kq, (uint32_t) (context_param_list ? context_param_list->last_entry_t : rrdset_last_entry_t_nolock(r->st))
  61. , kq, kq, (uint32_t)r->before
  62. , kq, kq, (uint32_t)r->after
  63. , kq, kq);
  64. if (should_lock)
  65. rrdset_unlock(r->st);
  66. for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
  67. if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
  68. if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
  69. if(i) buffer_strcat(wb, ", ");
  70. buffer_strcat(wb, sq);
  71. buffer_strcat(wb, rd->name);
  72. buffer_strcat(wb, sq);
  73. i++;
  74. }
  75. if(!i) {
  76. #ifdef NETDATA_INTERNAL_CHECKS
  77. error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
  78. #endif
  79. rows = 0;
  80. buffer_strcat(wb, sq);
  81. buffer_strcat(wb, "no data");
  82. buffer_strcat(wb, sq);
  83. }
  84. buffer_sprintf(wb, "],\n"
  85. " %sdimension_ids%s: ["
  86. , kq, kq);
  87. for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
  88. if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
  89. if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
  90. if(i) buffer_strcat(wb, ", ");
  91. buffer_strcat(wb, sq);
  92. buffer_strcat(wb, rd->id);
  93. buffer_strcat(wb, sq);
  94. i++;
  95. }
  96. if(!i) {
  97. rows = 0;
  98. buffer_strcat(wb, sq);
  99. buffer_strcat(wb, "no data");
  100. buffer_strcat(wb, sq);
  101. }
  102. buffer_strcat(wb, "],\n");
  103. if (rrdset_query_data->show_dimensions) {
  104. buffer_sprintf(wb, " %sfull_dimension_list%s: [", kq, kq);
  105. char name[RRD_ID_LENGTH_MAX * 2 + 2];
  106. char output[RRD_ID_LENGTH_MAX * 2 + 8];
  107. char value[RRD_ID_LENGTH_MAX * 2 + 1];
  108. struct value_output co = {.c = 0, .wb = wb};
  109. DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
  110. for (i = 0, rd = temp_rd ? temp_rd : r->st->dimensions; rd; rd = rd->next) {
  111. snprintfz(name, RRD_ID_LENGTH_MAX * 2, "%s:%s", rd->id, rd->name);
  112. int len = snprintfz(output, RRD_ID_LENGTH_MAX * 2 + 7, "[\"%s\",\"%s\"]", rd->id, rd->name);
  113. dictionary_set(dict, name, output, len+1);
  114. }
  115. dictionary_walkthrough_read(dict, value_list_output, &co);
  116. dictionary_destroy(dict);
  117. co.c = 0;
  118. buffer_sprintf(wb, "],\n %sfull_chart_list%s: [", kq, kq);
  119. dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
  120. for (i = 0, rd = temp_rd ? temp_rd : r->st->dimensions; rd; rd = rd->next) {
  121. int len = snprintfz(output, RRD_ID_LENGTH_MAX * 2 + 7, "[\"%s\",\"%s\"]", rd->rrdset->id, rd->rrdset->name);
  122. snprintfz(name, RRD_ID_LENGTH_MAX * 2, "%s:%s", rd->rrdset->id, rd->rrdset->name);
  123. dictionary_set(dict, name, output, len + 1);
  124. }
  125. dictionary_walkthrough_read(dict, value_list_output, &co);
  126. dictionary_destroy(dict);
  127. RRDSET *st;
  128. co.c = 0;
  129. buffer_sprintf(wb, "],\n %sfull_chart_labels%s: [", kq, kq);
  130. dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
  131. for (i = 0, rd = temp_rd ? temp_rd : r->st->dimensions; rd; rd = rd->next) {
  132. st = rd->rrdset;
  133. if (likely(st->state)) {
  134. struct label_index *labels = &st->state->labels;
  135. if (labels->head) {
  136. netdata_rwlock_rdlock(&labels->labels_rwlock);
  137. for (struct label *label = labels->head; label; label = label->next) {
  138. sanitize_json_string(value, label->value, RRD_ID_LENGTH_MAX * 2);
  139. int len = snprintfz(output, RRD_ID_LENGTH_MAX * 2 + 7, "[\"%s\", \"%s\"]", label->key, value);
  140. snprintfz(name, RRD_ID_LENGTH_MAX * 2, "%s:%s", label->key, value);
  141. dictionary_set(dict, name, output, len + 1);
  142. }
  143. netdata_rwlock_unlock(&labels->labels_rwlock);
  144. }
  145. }
  146. }
  147. dictionary_walkthrough_read(dict, value_list_output, &co);
  148. dictionary_destroy(dict);
  149. buffer_strcat(wb, "],\n");
  150. }
  151. // Composite charts
  152. if (context_mode && temp_rd) {
  153. buffer_sprintf(
  154. wb,
  155. " %schart_ids%s: [",
  156. kq, kq);
  157. for (c = 0, i = 0, rd = temp_rd ; rd && c < r->d; c++, rd = rd->next) {
  158. if (unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN))
  159. continue;
  160. if (unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO)))
  161. continue;
  162. if (i)
  163. buffer_strcat(wb, ", ");
  164. buffer_strcat(wb, sq);
  165. buffer_strcat(wb, rd->rrdset->id);
  166. buffer_strcat(wb, sq);
  167. i++;
  168. }
  169. if (!i) {
  170. rows = 0;
  171. buffer_strcat(wb, sq);
  172. buffer_strcat(wb, "no data");
  173. buffer_strcat(wb, sq);
  174. }
  175. buffer_strcat(wb, "],\n");
  176. if (chart_label_key) {
  177. buffer_sprintf(wb, " %schart_labels%s: { ", kq, kq);
  178. SIMPLE_PATTERN *pattern = simple_pattern_create(chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT);
  179. SIMPLE_PATTERN *original_pattern = pattern;
  180. char *label_key = NULL;
  181. int keys = 0;
  182. while (pattern && (label_key = simple_pattern_iterate(&pattern))) {
  183. uint32_t key_hash = simple_hash(label_key);
  184. struct label *current_label;
  185. if (keys)
  186. buffer_strcat(wb, ", ");
  187. buffer_sprintf(wb, "%s%s%s : [", kq, label_key, kq);
  188. keys++;
  189. for (c = 0, i = 0, rd = temp_rd; rd && c < r->d; c++, rd = rd->next) {
  190. if (unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN))
  191. continue;
  192. if (unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO)))
  193. continue;
  194. if (i)
  195. buffer_strcat(wb, ", ");
  196. current_label = rrdset_lookup_label_key(rd->rrdset, label_key, key_hash);
  197. if (current_label) {
  198. buffer_strcat(wb, sq);
  199. buffer_strcat(wb, current_label->value);
  200. buffer_strcat(wb, sq);
  201. } else
  202. buffer_strcat(wb, "null");
  203. i++;
  204. }
  205. if (!i) {
  206. rows = 0;
  207. buffer_strcat(wb, sq);
  208. buffer_strcat(wb, "no data");
  209. buffer_strcat(wb, sq);
  210. }
  211. buffer_strcat(wb, "]");
  212. }
  213. buffer_strcat(wb, "},\n");
  214. simple_pattern_free(original_pattern);
  215. }
  216. }
  217. buffer_sprintf(wb, " %slatest_values%s: ["
  218. , kq, kq);
  219. for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
  220. if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
  221. if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
  222. if(i) buffer_strcat(wb, ", ");
  223. i++;
  224. calculated_number value = rd->last_stored_value;
  225. if (NAN == value)
  226. buffer_strcat(wb, "null");
  227. else
  228. buffer_rrd_value(wb, value);
  229. /*
  230. storage_number n = rd->values[rrdset_last_slot(r->st)];
  231. if(!does_storage_number_exist(n))
  232. buffer_strcat(wb, "null");
  233. else
  234. buffer_rrd_value(wb, unpack_storage_number(n));
  235. */
  236. }
  237. if(!i) {
  238. rows = 0;
  239. buffer_strcat(wb, "null");
  240. }
  241. buffer_sprintf(wb, "],\n"
  242. " %sview_latest_values%s: ["
  243. , kq, kq);
  244. i = 0;
  245. if(rows) {
  246. calculated_number total = 1;
  247. if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
  248. total = 0;
  249. for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
  250. calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
  251. calculated_number n = cn[c];
  252. if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
  253. n = -n;
  254. total += n;
  255. }
  256. // prevent a division by zero
  257. if(total == 0) total = 1;
  258. }
  259. for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
  260. if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
  261. if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
  262. if(i) buffer_strcat(wb, ", ");
  263. i++;
  264. calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
  265. RRDR_VALUE_FLAGS *co = &r->o[ (rrdr_rows(r) - 1) * r->d ];
  266. calculated_number n = cn[c];
  267. if(co[c] & RRDR_VALUE_EMPTY) {
  268. if(options & RRDR_OPTION_NULL2ZERO)
  269. buffer_strcat(wb, "0");
  270. else
  271. buffer_strcat(wb, "null");
  272. }
  273. else {
  274. if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
  275. n = -n;
  276. if(unlikely(options & RRDR_OPTION_PERCENTAGE))
  277. n = n * 100 / total;
  278. buffer_rrd_value(wb, n);
  279. }
  280. }
  281. }
  282. if(!i) {
  283. rows = 0;
  284. buffer_strcat(wb, "null");
  285. }
  286. buffer_sprintf(wb, "],\n"
  287. " %sdimensions%s: %ld,\n"
  288. " %spoints%s: %ld,\n"
  289. " %sformat%s: %s"
  290. , kq, kq, i
  291. , kq, kq, rows
  292. , kq, kq, sq
  293. );
  294. rrdr_buffer_print_format(wb, format);
  295. if((options & RRDR_OPTION_CUSTOM_VARS) && (options & RRDR_OPTION_JSON_WRAP)) {
  296. buffer_sprintf(wb, "%s,\n %schart_variables%s: ", sq, kq, kq);
  297. health_api_v1_chart_custom_variables2json(r->st, wb);
  298. }
  299. else
  300. buffer_sprintf(wb, "%s", sq);
  301. buffer_sprintf(wb, ",\n %sresult%s: ", kq, kq);
  302. if(string_value) buffer_strcat(wb, sq);
  303. //info("JSONWRAPPER(): %s: END", r->st->id);
  304. }
  305. void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value) {
  306. (void)format;
  307. char kq[2] = "", // key quote
  308. sq[2] = ""; // string quote
  309. if( options & RRDR_OPTION_GOOGLE_JSON ) {
  310. kq[0] = '\0';
  311. sq[0] = '\'';
  312. }
  313. else {
  314. kq[0] = '"';
  315. sq[0] = '"';
  316. }
  317. if(string_value) buffer_strcat(wb, sq);
  318. buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
  319. buffer_rrd_value(wb, r->min);
  320. buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
  321. buffer_rrd_value(wb, r->max);
  322. buffer_strcat(wb, "\n}\n");
  323. }