master
c 539 lines 22.5 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "jsonwrap.h"
4 #include "jsonwrap-internal.h"
5 #include "web/mcp/mcp.h"
6 #include "libnetdata/json/json-keys.h"
7
8 void buffer_json_agent_status_id(BUFFER *wb, size_t ai, usec_t duration_ut) {
9 buffer_json_member_add_object(wb, JSKEY(status));
10 {
11 buffer_json_member_add_uint64(wb, JSKEY(agent_index), ai);
12 buffer_json_member_add_uint64(wb, "code", 200);
13 buffer_json_member_add_string(wb, "msg", "");
14 if (duration_ut)
15 buffer_json_member_add_double(wb, "ms", (NETDATA_DOUBLE) duration_ut / 1000.0);
16 }
17 buffer_json_object_close(wb);
18 }
19
20 void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut, bool status) {
21 buffer_json_member_add_string(wb, JSKEY(machine_guid), host->machine_guid);
22
23 if(!UUIDiszero(host->node_id))
24 buffer_json_member_add_uuid(wb, JSKEY(node_id), host->node_id.uuid);
25 buffer_json_member_add_string(wb, JSKEY(hostname), rrdhost_hostname(host));
26 buffer_json_member_add_uint64(wb, JSKEY(node_index), ni);
27
28 if(status)
29 buffer_json_agent_status_id(wb, 0, duration_ut);
30 }
31
32 static void query_target_combined_chart_type(BUFFER *wb, QUERY_TARGET *qt, size_t contexts) {
33 if(contexts >= 1)
34 buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(rrdcontext_acquired_chart_type(qt->contexts.array[0].rca)));
35 }
36
37 static void query_target_title(BUFFER *wb, QUERY_TARGET *qt, size_t contexts) {
38 if(contexts == 1) {
39 buffer_json_member_add_string(wb, "title", rrdcontext_acquired_title(qt->contexts.array[0].rca));
40 }
41 else if(contexts > 1) {
42 BUFFER *t = buffer_create(0, NULL);
43 DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE);
44
45 buffer_strcat(t, "Chart for contexts: ");
46
47 size_t added = 0;
48 for(size_t c = 0; c < qt->contexts.used ;c++) {
49 bool *set = dictionary_set(dict, rrdcontext_acquired_id(qt->contexts.array[c].rca), NULL, sizeof(*set));
50 if(!*set) {
51 *set = true;
52 if(added)
53 buffer_fast_strcat(t, ", ", 2);
54
55 buffer_strcat(t, rrdcontext_acquired_id(qt->contexts.array[c].rca));
56 added++;
57 }
58 }
59 buffer_json_member_add_string(wb, "title", buffer_tostring(t));
60 dictionary_destroy(dict);
61 buffer_free(t);
62 }
63 }
64
65 void version_hashes_api_v2(BUFFER *wb, struct query_versions *versions) {
66 buffer_json_member_add_object(wb, "versions");
67 buffer_json_member_add_uint64(wb, "routing_hard_hash", 1);
68 buffer_json_member_add_uint64(wb, "nodes_hard_hash", dictionary_version(rrdhost_root_index));
69 buffer_json_member_add_uint64(wb, "contexts_hard_hash", versions->contexts_hard_hash);
70 buffer_json_member_add_uint64(wb, "contexts_soft_hash", versions->contexts_soft_hash);
71 buffer_json_member_add_uint64(wb, "alerts_hard_hash", versions->alerts_hard_hash);
72 buffer_json_member_add_uint64(wb, "alerts_soft_hash", versions->alerts_soft_hash);
73 buffer_json_object_close(wb);
74 }
75
76 static void query_target_combined_units_v2(BUFFER *wb, QUERY_TARGET *qt, size_t contexts, bool ignore_percentage) {
77 if(!ignore_percentage && query_target_has_percentage_units(qt)) {
78 buffer_json_member_add_string(wb, "units", "%");
79 }
80 else if(contexts == 1) {
81 buffer_json_member_add_string(wb, "units", rrdcontext_acquired_units(qt->contexts.array[0].rca));
82 }
83 else if(contexts > 1) {
84 DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE);
85 for(size_t c = 0; c < qt->contexts.used ;c++)
86 dictionary_set(dict, rrdcontext_acquired_units(qt->contexts.array[c].rca), NULL, 0);
87
88 if(dictionary_entries(dict) == 1)
89 buffer_json_member_add_string(wb, "units", rrdcontext_acquired_units(qt->contexts.array[0].rca));
90 else {
91 buffer_json_member_add_array(wb, "units");
92 const char *s;
93 dfe_start_read(dict, s)
94 buffer_json_add_array_item_string(wb, s_dfe.name);
95 dfe_done(s);
96 buffer_json_array_close(wb);
97 }
98 dictionary_destroy(dict);
99 }
100 }
101
102 static inline void rrdr_dimension_query_points_statistics(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options, bool dview) {
103 STORAGE_POINT *sp = (dview) ? r->dview : r->dqp;
104 NETDATA_DOUBLE anomaly_rate_multiplier = (dview) ? RRDR_DVIEW_ANOMALY_COUNT_MULTIPLIER : 1.0;
105
106 if(unlikely(!sp))
107 return;
108
109 if(key)
110 buffer_json_member_add_object(wb, key);
111
112 buffer_json_member_add_array(wb, "min");
113 for(size_t c = 0; c < r->d ; c++) {
114 if (!rrdr_dimension_should_be_exposed(r->od[c], options))
115 continue;
116
117 buffer_json_add_array_item_double(wb, sp[c].min);
118 }
119 buffer_json_array_close(wb);
120
121 buffer_json_member_add_array(wb, "max");
122 for(size_t c = 0; c < r->d ; c++) {
123 if (!rrdr_dimension_should_be_exposed(r->od[c], options))
124 continue;
125
126 buffer_json_add_array_item_double(wb, sp[c].max);
127 }
128 buffer_json_array_close(wb);
129
130 if(options & RRDR_OPTION_RETURN_RAW) {
131 buffer_json_member_add_array(wb, "sum");
132 for(size_t c = 0; c < r->d ; c++) {
133 if (!rrdr_dimension_should_be_exposed(r->od[c], options))
134 continue;
135
136 buffer_json_add_array_item_double(wb, sp[c].sum);
137 }
138 buffer_json_array_close(wb);
139
140 buffer_json_member_add_array(wb, JSKEY(count));
141 for(size_t c = 0; c < r->d ; c++) {
142 if (!rrdr_dimension_should_be_exposed(r->od[c], options))
143 continue;
144
145 buffer_json_add_array_item_uint64(wb, sp[c].count);
146 }
147 buffer_json_array_close(wb);
148
149 buffer_json_member_add_array(wb, JSKEY(anomaly_count));
150 for(size_t c = 0; c < r->d ; c++) {
151 if (!rrdr_dimension_should_be_exposed(r->od[c], options))
152 continue;
153
154 buffer_json_add_array_item_uint64(wb, storage_point_anomaly_rate(sp[c]) / anomaly_rate_multiplier / 100.0 * sp[c].count);
155 }
156 buffer_json_array_close(wb);
157 }
158 else {
159 NETDATA_DOUBLE sum = 0.0;
160 for(size_t c = 0; c < r->d ; c++) {
161 if(!rrdr_dimension_should_be_exposed(r->od[c], options))
162 continue;
163
164 sum += ABS(sp[c].sum);
165 }
166
167 buffer_json_member_add_array(wb, "avg");
168 for(size_t c = 0; c < r->d ; c++) {
169 if (!rrdr_dimension_should_be_exposed(r->od[c], options))
170 continue;
171
172 buffer_json_add_array_item_double(wb, storage_point_average_value(sp[c]));
173 }
174 buffer_json_array_close(wb);
175
176 buffer_json_member_add_array(wb, JSKEY(anomaly_rate));
177 for(size_t c = 0; c < r->d ; c++) {
178 if (!rrdr_dimension_should_be_exposed(r->od[c], options))
179 continue;
180
181 buffer_json_add_array_item_double(wb, storage_point_anomaly_rate(sp[c]) / anomaly_rate_multiplier);
182 }
183 buffer_json_array_close(wb);
184
185 buffer_json_member_add_array(wb, JSKEY(contribution));
186 for(size_t c = 0; c < r->d ; c++) {
187 if (!rrdr_dimension_should_be_exposed(r->od[c], options))
188 continue;
189
190 NETDATA_DOUBLE con = (sum > 0.0) ? ABS(sp[c].sum) * 100.0 / sum : 0.0;
191 buffer_json_add_array_item_double(wb, con);
192 }
193 buffer_json_array_close(wb);
194 }
195
196 if(key)
197 buffer_json_object_close(wb);
198 }
199
200 static void rrdr_grouped_by_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options __maybe_unused) {
201 QUERY_TARGET *qt = r->internal.qt;
202
203 buffer_json_member_add_array(wb, key);
204
205 // find the deeper group-by
206 ssize_t g = 0;
207 for(g = 0; g < MAX_QUERY_GROUP_BY_PASSES ;g++) {
208 if(qt->request.group_by[g].group_by == RRDR_GROUP_BY_NONE)
209 break;
210 }
211
212 if(g > 0)
213 g--;
214
215 RRDR_GROUP_BY group_by = qt->request.group_by[g].group_by;
216
217 if(group_by & RRDR_GROUP_BY_SELECTED)
218 buffer_json_add_array_item_string(wb, "selected");
219
220 else if(group_by & RRDR_GROUP_BY_PERCENTAGE_OF_INSTANCE)
221 buffer_json_add_array_item_string(wb, "percentage-of-instance");
222
223 else {
224
225 if(group_by & RRDR_GROUP_BY_DIMENSION)
226 buffer_json_add_array_item_string(wb, "dimension");
227
228 if(group_by & RRDR_GROUP_BY_INSTANCE)
229 buffer_json_add_array_item_string(wb, "instance");
230
231 if(group_by & RRDR_GROUP_BY_LABEL) {
232 BUFFER *b = buffer_create(0, NULL);
233 for (size_t l = 0; l < qt->group_by[g].used; l++) {
234 buffer_flush(b);
235 buffer_fast_strcat(b, "label:", 6);
236 buffer_strcat(b, qt->group_by[g].label_keys[l]);
237 buffer_json_add_array_item_string(wb, buffer_tostring(b));
238 }
239 buffer_free(b);
240 }
241
242 if(group_by & RRDR_GROUP_BY_NODE)
243 buffer_json_add_array_item_string(wb, "node");
244
245 if(group_by & RRDR_GROUP_BY_CONTEXT)
246 buffer_json_add_array_item_string(wb, "context");
247
248 if(group_by & RRDR_GROUP_BY_UNITS)
249 buffer_json_add_array_item_string(wb, "units");
250 }
251
252 buffer_json_array_close(wb); // group_by_order
253 }
254
255 static void rrdr_dimension_units_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options, bool ignore_percentage) {
256 if(!r->du)
257 return;
258
259 bool percentage = !ignore_percentage && query_target_has_percentage_units(r->internal.qt);
260
261 buffer_json_member_add_array(wb, key);
262 for(size_t c = 0; c < r->d ; c++) {
263 if(!rrdr_dimension_should_be_exposed(r->od[c], options))
264 continue;
265
266 if(percentage)
267 buffer_json_add_array_item_string(wb, "%");
268 else
269 buffer_json_add_array_item_string(wb, string2str(r->du[c]));
270 }
271 buffer_json_array_close(wb);
272 }
273
274 static void rrdr_dimension_priority_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
275 if(!r->dp)
276 return;
277
278 buffer_json_member_add_array(wb, key);
279 for(size_t c = 0; c < r->d ; c++) {
280 if(!rrdr_dimension_should_be_exposed(r->od[c], options))
281 continue;
282
283 buffer_json_add_array_item_uint64(wb, r->dp[c]);
284 }
285 buffer_json_array_close(wb);
286 }
287
288 static void rrdr_dimension_aggregated_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
289 if(!r->dgbc)
290 return;
291
292 buffer_json_member_add_array(wb, key);
293 for(size_t c = 0; c < r->d ;c++) {
294 if(!rrdr_dimension_should_be_exposed(r->od[c], options))
295 continue;
296
297 buffer_json_add_array_item_uint64(wb, r->dgbc[c]);
298 }
299 buffer_json_array_close(wb);
300 }
301
302 void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb) {
303 QUERY_TARGET *qt = r->internal.qt;
304 RRDR_OPTIONS options = qt->window.options;
305
306 char kq[2] = "\"", // key quote
307 sq[2] = "\""; // string quote
308
309 if(unlikely(options & RRDR_OPTION_GOOGLE_JSON)) {
310 kq[0] = '\0';
311 sq[0] = '\'';
312 }
313
314 buffer_json_initialize(
315 wb, kq, sq, 0, true, (options & RRDR_OPTION_MINIFY) ? BUFFER_JSON_OPTIONS_MINIFY : BUFFER_JSON_OPTIONS_DEFAULT);
316
317 json_keys_init((options & RRDR_OPTION_LONG_JSON_KEYS) ? JSON_KEYS_OPTION_LONG_KEYS : 0);
318 buffer_json_member_add_uint64(wb, "api", qt->request.version);
319
320 if(options & RRDR_OPTION_DEBUG) {
321 buffer_json_member_add_string(wb, "id", qt->id);
322 buffer_json_member_add_object(wb, "request");
323 {
324 buffer_json_member_add_string(wb, "format", rrdr_format_to_string(qt->request.format));
325 rrdr_options_to_buffer_json_array(wb, "options", qt->request.options);
326
327 buffer_json_member_add_object(wb, "scope");
328 buffer_json_member_add_string(wb, "scope_nodes", qt->request.scope_nodes);
329 buffer_json_member_add_string(wb, "scope_contexts", qt->request.scope_contexts);
330 buffer_json_object_close(wb); // scope
331
332 buffer_json_member_add_object(wb, "selectors");
333 if (qt->request.host)
334 buffer_json_member_add_string(wb, "nodes", rrdhost_hostname(qt->request.host));
335 else
336 buffer_json_member_add_string(wb, "nodes", qt->request.nodes);
337 buffer_json_member_add_string(wb, "contexts", qt->request.contexts);
338 buffer_json_member_add_string(wb, "instances", qt->request.instances);
339 buffer_json_member_add_string(wb, "dimensions", qt->request.dimensions);
340 buffer_json_member_add_string(wb, "labels", qt->request.labels);
341 buffer_json_member_add_string(wb, "alerts", qt->request.alerts);
342 buffer_json_object_close(wb); // selectors
343
344 buffer_json_member_add_object(wb, "window");
345 buffer_json_member_add_time_t_formatted(wb, "after", qt->request.after, qt->request.options & RRDR_OPTION_RFC3339);
346 buffer_json_member_add_time_t_formatted(wb, "before", qt->request.before, qt->request.options & RRDR_OPTION_RFC3339);
347 buffer_json_member_add_uint64(wb, "points", qt->request.points);
348 if (qt->request.options & RRDR_OPTION_SELECTED_TIER)
349 buffer_json_member_add_uint64(wb, "tier", qt->request.tier);
350 else
351 buffer_json_member_add_string(wb, "tier", NULL);
352 buffer_json_object_close(wb); // window
353
354 buffer_json_member_add_object(wb, "aggregations");
355 {
356 buffer_json_member_add_object(wb, "time");
357 buffer_json_member_add_string(wb, "time_group", time_grouping_tostring(qt->request.time_group_method));
358 buffer_json_member_add_string(wb, "time_group_options", qt->request.time_group_options);
359 if (qt->request.resampling_time > 0)
360 buffer_json_member_add_time_t(wb, "time_resampling", qt->request.resampling_time);
361 else
362 buffer_json_member_add_string(wb, "time_resampling", NULL);
363 buffer_json_object_close(wb); // time
364
365 buffer_json_member_add_array(wb, "metrics");
366 for(size_t g = 0; g < MAX_QUERY_GROUP_BY_PASSES ;g++) {
367 if(qt->request.group_by[g].group_by == RRDR_GROUP_BY_NONE)
368 break;
369
370 buffer_json_add_array_item_object(wb);
371 {
372 buffer_json_member_add_array(wb, "group_by");
373 buffer_json_group_by_to_array(wb, qt->request.group_by[g].group_by);
374 buffer_json_array_close(wb);
375
376 buffer_json_member_add_array(wb, "group_by_label");
377 for (size_t l = 0; l < qt->group_by[g].used; l++)
378 buffer_json_add_array_item_string(wb, qt->group_by[g].label_keys[l]);
379 buffer_json_array_close(wb);
380
381 buffer_json_member_add_string(
382 wb, "aggregation",group_by_aggregate_function_to_string(qt->request.group_by[g].aggregation));
383 }
384 buffer_json_object_close(wb);
385 }
386 buffer_json_array_close(wb); // group_by
387 }
388 buffer_json_object_close(wb); // aggregations
389
390 buffer_json_member_add_uint64(wb, "timeout", qt->request.timeout_ms);
391 }
392 buffer_json_object_close(wb); // request
393 }
394
395 if(!(options & RRDR_OPTION_MINIMAL_STATS))
396 version_hashes_api_v2(wb, &qt->versions);
397
398 struct summary_total_counts
399 nodes_totals = { 0 },
400 contexts_totals = { 0 },
401 instances_totals = { 0 },
402 metrics_totals = { 0 },
403 label_key_totals = { 0 },
404 label_key_value_totals = { 0 };
405
406 buffer_json_member_add_object(wb, "summary");
407 {
408 if(options & RRDR_OPTION_MCP_INFO)
409 buffer_json_member_add_string(wb, "info", MCP_QUERY_INFO_SUMMARY_SECTION);
410
411 query_target_summary_nodes_v2(wb, qt, "nodes", &nodes_totals);
412 r->internal.contexts = query_target_summary_contexts_v2(wb, qt, "contexts", &contexts_totals);
413 query_target_summary_instances_v2(wb, qt, "instances", &instances_totals);
414 query_target_summary_dimensions_v12(wb, qt, "dimensions", true, &metrics_totals);
415 query_target_summary_labels_v12(wb, qt, "labels", true, &label_key_totals, &label_key_value_totals);
416 if(!(options & RRDR_OPTION_MINIMAL_STATS))
417 query_target_summary_alerts_v2(wb, qt, "alerts");
418 }
419 if(query_target_aggregatable(qt)) {
420 buffer_json_member_add_object(wb, "globals");
421 query_target_points_statistics(wb, qt, &qt->query_points);
422 buffer_json_object_close(wb); // globals
423 }
424 buffer_json_object_close(wb); // summary
425
426 // Only include the totals section if MINIMAL_STATS option is not set
427 if (!(options & RRDR_OPTION_MINIMAL_STATS)) {
428 buffer_json_member_add_object(wb, "totals");
429 query_target_total_counts(wb, "nodes", &nodes_totals);
430 query_target_total_counts(wb, "contexts", &contexts_totals);
431 query_target_total_counts(wb, "instances", &instances_totals);
432 query_target_total_counts(wb, "dimensions", &metrics_totals);
433 query_target_total_counts(wb, "label_keys", &label_key_totals);
434 query_target_total_counts(wb, "label_key_values", &label_key_value_totals);
435 buffer_json_object_close(wb); // totals
436 }
437
438 if(options & RRDR_OPTION_SHOW_DETAILS) {
439 buffer_json_member_add_object(wb, "detailed");
440 query_target_detailed_objects_tree(wb, r, options);
441 buffer_json_object_close(wb); // detailed
442 }
443
444 if(!(options & RRDR_OPTION_MINIMAL_STATS))
445 query_target_functions(wb, "functions", r);
446 }
447
448 void rrdr_json_wrapper_end2(RRDR *r, BUFFER *wb) {
449 QUERY_TARGET *qt = r->internal.qt;
450 DATASOURCE_FORMAT format = qt->request.format;
451 RRDR_OPTIONS options = qt->window.options;
452
453 buffer_json_member_add_object(wb, "db");
454 {
455 if(options & RRDR_OPTION_MCP_INFO)
456 buffer_json_member_add_string(wb, "info", MCP_QUERY_INFO_DATABASE_SECTION);
457
458 buffer_json_member_add_uint64(wb, "tiers", nd_profile.storage_tiers);
459 buffer_json_member_add_time_t(wb, "update_every", qt->db.minimum_latest_update_every_s);
460 buffer_json_member_add_time_t_formatted(wb, "first_entry", qt->db.first_time_s, options & RRDR_OPTION_RFC3339);
461 buffer_json_member_add_time_t_formatted(wb, "last_entry", qt->db.last_time_s, options & RRDR_OPTION_RFC3339);
462
463 query_target_combined_units_v2(wb, qt, r->internal.contexts, true);
464 buffer_json_member_add_object(wb, "dimensions");
465 {
466 rrdr_dimension_ids(wb, "ids", r, options);
467 rrdr_dimension_units_array_v2(wb, "units", r, options, true);
468 rrdr_dimension_query_points_statistics(wb, JSKEY(statistics), r, options, false);
469 }
470 buffer_json_object_close(wb); // dimensions
471
472 buffer_json_member_add_array(wb, "per_tier");
473 for(size_t tier = 0; tier < nd_profile.storage_tiers; tier++) {
474 buffer_json_add_array_item_object(wb);
475 buffer_json_member_add_uint64(wb, "tier", tier);
476 buffer_json_member_add_uint64(wb, "queries", qt->db.tiers[tier].queries);
477 buffer_json_member_add_uint64(wb, "points", qt->db.tiers[tier].points);
478 buffer_json_member_add_time_t(wb, "update_every", qt->db.tiers[tier].update_every);
479 buffer_json_member_add_time_t_formatted(wb, "first_entry", qt->db.tiers[tier].retention.first_time_s, options & RRDR_OPTION_RFC3339);
480 buffer_json_member_add_time_t_formatted(wb, "last_entry", qt->db.tiers[tier].retention.last_time_s, options & RRDR_OPTION_RFC3339);
481 buffer_json_object_close(wb);
482 }
483 buffer_json_array_close(wb);
484 }
485 buffer_json_object_close(wb);
486
487 buffer_json_member_add_object(wb, "view");
488 {
489 if(options & RRDR_OPTION_MCP_INFO)
490 buffer_json_member_add_string(wb, "info", MCP_QUERY_INFO_VIEW_SECTION);
491
492 query_target_title(wb, qt, r->internal.contexts);
493 buffer_json_member_add_time_t(wb, "update_every", r->view.update_every);
494 buffer_json_member_add_time_t_formatted(wb, "after", r->view.after, options & RRDR_OPTION_RFC3339);
495 buffer_json_member_add_time_t_formatted(wb, "before", r->view.before, options & RRDR_OPTION_RFC3339);
496
497 if(options & RRDR_OPTION_DEBUG) {
498 buffer_json_member_add_string(wb, "format", rrdr_format_to_string(format));
499 rrdr_options_to_buffer_json_array(wb, "options", options);
500 buffer_json_member_add_string(wb, "time_group", time_grouping_tostring(qt->request.time_group_method));
501 }
502
503 if(options & RRDR_OPTION_DEBUG) {
504 buffer_json_member_add_object(wb, "partial_data_trimming");
505 buffer_json_member_add_time_t(wb, "max_update_every", r->partial_data_trimming.max_update_every);
506 buffer_json_member_add_time_t_formatted(wb, "expected_after", r->partial_data_trimming.expected_after, options & RRDR_OPTION_RFC3339);
507 buffer_json_member_add_time_t_formatted(wb, "trimmed_after", r->partial_data_trimming.trimmed_after, options & RRDR_OPTION_RFC3339);
508 buffer_json_object_close(wb);
509 }
510
511 if(options & RRDR_OPTION_RETURN_RAW)
512 buffer_json_member_add_uint64(wb, "points", rrdr_rows(r));
513
514 query_target_combined_units_v2(wb, qt, r->internal.contexts, false);
515 query_target_combined_chart_type(wb, qt, r->internal.contexts);
516 buffer_json_member_add_object(wb, "dimensions");
517 {
518 rrdr_grouped_by_array_v2(wb, "grouped_by", r, options);
519 rrdr_dimension_ids(wb, "ids", r, options);
520 rrdr_dimension_names(wb, "names", r, options);
521 rrdr_dimension_units_array_v2(wb, "units", r, options, false);
522 rrdr_dimension_priority_array_v2(wb, "priorities", r, options);
523 rrdr_dimension_aggregated_array_v2(wb, "aggregated", r, options);
524 rrdr_dimension_query_points_statistics(wb, JSKEY(statistics), r, options, true);
525 rrdr_json_group_by_labels(wb, "labels", r, options);
526 }
527 buffer_json_object_close(wb); // dimensions
528 buffer_json_member_add_double(wb, "min", r->view.min);
529 buffer_json_member_add_double(wb, "max", r->view.max);
530 }
531 buffer_json_object_close(wb); // view
532
533 if(!(options & RRDR_OPTION_MINIMAL_STATS)) {
534 buffer_json_agents_v2(wb, &r->internal.qt->timings, 0, false, true, rrdr_options_to_contexts_options(options));
535 buffer_json_cloud_timings(wb, "timings", &r->internal.qt->timings);
536 }
537 buffer_json_finalize(wb);
538 json_keys_reset();
539 }