diff options
author | Costa Tsaousis <costa@netdata.cloud> | 2023-03-13 23:39:06 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-13 23:39:06 +0200 |
commit | cd50bf42367ed49ed12e944d66a445653c6f038c (patch) | |
tree | 381768f311ad42ee9dceb5cbe63fc8969a02f515 | |
parent | 8068c952d8e1812caf348b77f38ae3df1014cc65 (diff) |
/api/v2 part 4 (#14706)
* expose the order of group by
* key renames in json wrapper v2
* added group by context and group by units
* added view_average_values
* fix for view_average_values when percentage is specified
* option group-by-labels is enabling the exposure of all the labels that are used for each of the final grouped dimensions
* when executing group by queries, allocate one dimension data at a time - not all of them
* respect hidden dimensions
* cancel running data query on socket error
* use poll to detect socket errors
* use POLLRDHUP to detect half closed connections
* make sure POLLRDHUP is available
* do not destroy aral-by-size arals
* completed documentation of /api/v2/data.
* moved min, max back to view; updated swagger yaml and json
* default format for /api/v2/data is json2
35 files changed, 2669 insertions, 958 deletions
diff --git a/database/contexts/rrdcontext.h b/database/contexts/rrdcontext.h index 0261c78227..152422aa79 100644 --- a/database/contexts/rrdcontext.h +++ b/database/contexts/rrdcontext.h @@ -258,6 +258,8 @@ typedef struct query_metric { #define MAX_QUERY_TARGET_ID_LENGTH 255 +typedef bool (*interrupt_callback_t)(void *data); + typedef struct query_target_request { size_t version; @@ -303,6 +305,9 @@ typedef struct query_target_request { RRDR_GROUP_BY_FUNCTION group_by_aggregate_function; usec_t received_ut; + + interrupt_callback_t interrupt_callback; + void *interrupt_callback_data; } QUERY_TARGET_REQUEST; #define GROUP_BY_MAX_LABEL_KEYS 10 diff --git a/database/rrdlabels.c b/database/rrdlabels.c index 3b728db95a..f6abd60232 100644 --- a/database/rrdlabels.c +++ b/database/rrdlabels.c @@ -399,7 +399,7 @@ size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_si // find how big this character is (2-4 bytes) size_t utf_character_size = 2; - while(utf_character_size <= 4 && src[utf_character_size] && IS_UTF8_BYTE(src[utf_character_size]) && !IS_UTF8_STARTBYTE(src[utf_character_size])) + while(utf_character_size < 4 && src[utf_character_size] && IS_UTF8_BYTE(src[utf_character_size]) && !IS_UTF8_STARTBYTE(src[utf_character_size])) utf_character_size++; if(utf) { diff --git a/libnetdata/aral/aral.c b/libnetdata/aral/aral.c index 2a4b0687dd..60fe5e39a7 100644 --- a/libnetdata/aral/aral.c +++ b/libnetdata/aral/aral.c @@ -885,10 +885,10 @@ void aral_by_size_release(ARAL *ar) { fatal("ARAL BY SIZE: double release detected"); aral_by_size_globals.array[size].refcount--; - if(!aral_by_size_globals.array[size].refcount) { - aral_destroy(aral_by_size_globals.array[size].ar); - aral_by_size_globals.array[size].ar = NULL; - } +// if(!aral_by_size_globals.array[size].refcount) { +// aral_destroy(aral_by_size_globals.array[size].ar); +// aral_by_size_globals.array[size].ar = NULL; +// } netdata_spinlock_unlock(&aral_by_size_globals.spinlock); } diff --git a/libnetdata/buffer/buffer.h b/libnetdata/buffer/buffer.h index 1cf1ec981d..d19aad81e8 100644 --- a/libnetdata/buffer/buffer.h +++ b/libnetdata/buffer/buffer.h @@ -3,6 +3,7 @@ #ifndef NETDATA_WEB_BUFFER_H #define NETDATA_WEB_BUFFER_H 1 +#include "../string/utf8.h" #include "../libnetdata.h" #define WEB_DATA_LENGTH_INCREASE_STEP 1024 @@ -203,18 +204,56 @@ static inline void buffer_strcat(BUFFER *wb, const char *txt) { static inline void buffer_json_strcat(BUFFER *wb, const char *txt) { if(unlikely(!txt || !*txt)) return; - const char *t = txt; + const unsigned char *t = (const unsigned char *)txt; while(*t) { - buffer_need_bytes(wb, 100); - char *s = &wb->buffer[wb->len]; - char *d = s; - const char *e = &wb->buffer[wb->size - 1]; // remove 1 to make room for the escape character + buffer_need_bytes(wb, 110); + unsigned char *s = (unsigned char *)&wb->buffer[wb->len]; + unsigned char *d = s; + const unsigned char *e = (unsigned char *)&wb->buffer[wb->size - 10]; // make room for the max escape sequence while(*t && d < e) { - if(unlikely(*t == '\\' || *t == '\"')) +#ifdef BUFFER_JSON_ESCAPE_UTF + if(unlikely(IS_UTF8_STARTBYTE(*t) && IS_UTF8_BYTE(t[1]))) { + // UTF-8 multi-byte encoded character + + // find how big this character is (2-4 bytes) + size_t utf_character_size = 2; + while(utf_character_size < 4 && t[utf_character_size] && IS_UTF8_BYTE(t[utf_character_size]) && !IS_UTF8_STARTBYTE(t[utf_character_size])) + utf_character_size++; + + uint32_t code_point = 0; + for (size_t i = 0; i < utf_character_size; i++) { + code_point <<= 6; + code_point |= (t[i] & 0x3F); + } + + t += utf_character_size; + + // encode as \u escape sequence + *d++ = '\\'; + *d++ = 'u'; + *d++ = hex_digits[(code_point >> 12) & 0xf]; + *d++ = hex_digits[(code_point >> 8) & 0xf]; + *d++ = hex_digits[(code_point >> 4) & 0xf]; + *d++ = hex_digits[code_point & 0xf]; + } + else +#endif + if(unlikely(*t < ' ')) { + uint32_t v = *t++; *d++ = '\\'; + *d++ = 'u'; + *d++ = hex_digits[(v >> 12) & 0xf]; + *d++ = hex_digits[(v >> 8) & 0xf]; + *d++ = hex_digits[(v >> 4) & 0xf]; + *d++ = hex_digits[v & 0xf]; + } + else { + if (unlikely(*t == '\\' || *t == '\"')) + *d++ = '\\'; - *d++ = *t++; + *d++ = *t++; + } } wb->len += d - s; diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c index eb8e3a93da..dd36da105e 100644 --- a/libnetdata/socket/socket.c +++ b/libnetdata/socket/socket.c @@ -1,5 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-or-later +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // for POLLRDHUP +#endif + +#ifndef __BSD_VISIBLE +#define __BSD_VISIBLE // for POLLRDHUP +#endif + #include "../libnetdata.h" // -------------------------------------------------------------------------------------------------------------------- @@ -11,6 +19,46 @@ #define LARGE_SOCK_SIZE 4096 #endif +bool fd_is_socket(int fd) { + int type; + socklen_t len = sizeof(type); + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) == -1) + return false; + + return true; +} + +bool sock_has_output_error(int fd) { + if(fd < 0) { + //internal_error(true, "invalid socket %d", fd); + return false; + } + +// if(!fd_is_socket(fd)) { +// //internal_error(true, "fd %d is not a socket", fd); +// return false; +// } + + short int errors = POLLERR | POLLHUP | POLLNVAL; + +#ifdef POLLRDHUP + errors |= POLLRDHUP; +#endif + + struct pollfd pfd = { + .fd = fd, + .events = POLLOUT | errors, + .revents = 0, + }; + + if(poll(&pfd, 1, 0) == -1) { + //internal_error(true, "poll() failed"); + return false; + } + + return ((pfd.revents & errors) || !(pfd.revents & POLLOUT)); +} + int sock_setnonblock(int fd) { int flags; diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h index 9577453d53..3a8369fba0 100644 --- a/libnetdata/socket/socket.h +++ b/libnetdata/socket/socket.h @@ -74,6 +74,9 @@ ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); #endif +bool fd_is_socket(int fd); +bool sock_has_output_error(int fd); + int sock_setnonblock(int fd); int sock_delnonblock(int fd); int sock_setreuse(int fd, int reuse); diff --git a/libnetdata/string/utf8.h b/libnetdata/string/utf8.h index 133ec710b6..3e6c8c2883 100644 --- a/libnetdata/string/utf8.h +++ b/libnetdata/string/utf8.h @@ -3,7 +3,7 @@ #ifndef NETDATA_STRING_UTF8_H #define NETDATA_STRING_UTF8_H 1 -#define IS_UTF8_BYTE(x) (x & 0x80) -#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&(x & 0x40)) +#define IS_UTF8_BYTE(x) ((x) & 0x80) +#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&((x) & 0x40)) #endif /* NETDATA_STRING_UTF8_H */ diff --git a/web/api/formatters/json/json.c b/web/api/formatters/json/json.c index 832344ec05..dae0cc99cf 100644 --- a/web/api/formatters/json/json.c +++ b/web/api/formatters/json/json.c @@ -154,7 +154,6 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) { NETDATA_DOUBLE *cn = &r->v[ i * r->d ]; RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ]; NETDATA_DOUBLE *ar = &r->ar[ i * r->d ]; - uint32_t *gbc = &r->gbc [ i * r->d ]; time_t now = r->t[i]; @@ -211,15 +210,13 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) { buffer_fast_strcat(wb, post_date, post_date_len); } - if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) { + if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_AR)))) { total = 0; for(c = 0; c < used ;c++) { if(unlikely(!(r->od[c] & RRDR_DIMENSION_QUERIED))) continue; NETDATA_DOUBLE n; - if(unlikely(options & RRDR_OPTION_INTERNAL_GBC)) - n = gbc[c]; - else if(unlikely(options & RRDR_OPTION_INTERNAL_AR)) + if(unlikely(options & RRDR_OPTION_INTERNAL_AR)) n = ar[c]; else n = cn[c]; @@ -239,9 +236,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) { continue; NETDATA_DOUBLE n; - if(unlikely(options & RRDR_OPTION_INTERNAL_GBC)) - n = gbc[c]; - else if(unlikely(options & RRDR_OPTION_INTERNAL_AR)) + if(unlikely(options & RRDR_OPTION_INTERNAL_AR)) n = ar[c]; else n = cn[c]; @@ -251,7 +246,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) { if(unlikely( options & RRDR_OPTION_OBJECTSROWS )) buffer_sprintf(wb, "%s%s%s: ", kq, string2str(r->dn[c]), kq); - if(co[c] & RRDR_VALUE_EMPTY && !(options & (RRDR_OPTION_INTERNAL_AR | RRDR_OPTION_INTERNAL_GBC))) { + if(co[c] & RRDR_VALUE_EMPTY && !(options & (RRDR_OPTION_INTERNAL_AR))) { if(unlikely(options & RRDR_OPTION_NULL2ZERO)) buffer_fast_strcat(wb, "0", 1); else @@ -261,7 +256,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) { if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0)) n = -n; - if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) { + if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_AR)))) { n = n * 100 / total; if(unlikely(i == start && c == 0)) { @@ -285,3 +280,132 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) { buffer_strcat(wb, finish); //info("RRD2JSON(): %s: END", r->st->id); } + + +void rrdr2json_v2(RRDR *r, BUFFER *wb) { + QUERY_TARGET *qt = r->internal.qt; + RRDR_OPTIONS options = qt->request.options; + + bool expose_gbc = query_target_aggregatable(qt); + + buffer_json_member_add_object(wb, "result"); + + buffer_json_member_add_array(wb, "labels"); + buffer_json_add_array_item_string(wb, "time"); + long d, i; + const long used = (long)r->d; + for(d = 0, i = 0; d < used ; d++) { + if(!rrdr_dimension_should_be_exposed(r->od[d], options)) + continue; + + buffer_json_add_array_item_string(wb, string2str(r->dn[d])); + i++; + } + buffer_json_array_close(wb); // labels + + buffer_json_member_add_object(wb, "point"); + buffer_json_member_add_uint64(wb, "value", 0); + buffer_json_member_add_uint64(wb, "ar", 1); + buffer_json_member_add_uint64(wb, "pa", 2); + if(expose_gbc) + buffer_json_member_add_uint64(wb, "count", 3); + buffer_json_object_close(wb); + + buffer_json_member_add_array(wb, "data"); + if(i) { + long start = 0, end = rrdr_rows(r), step = 1; + if (!(options & RRDR_OPTION_REVERSED)) { + start = rrdr_rows(r) - 1; + end = -1; + step = -1; + } + + // for each line in the array + for (i = start; i != end; i += step) { + NETDATA_DOUBLE *cn = &r->v[ i * r->d ]; + RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ]; + NETDATA_DOUBLE *ar = &r->ar[ i * r->d ]; + uint32_t *gbc = &r->gbc [ i * r->d ]; + time_t now = r->t[i]; + + buffer_json_add_array_item_array(wb); // row + + if (options & RRDR_OPTION_MILLISECONDS) + buffer_json_add_array_item_time_ms(wb, now); // the time + else + buffer_json_add_array_item_time_t(wb, now); // the time + + NETDATA_DOUBLE total = 1; + if(unlikely((options & RRDR_OPTION_PERCENTAGE))) { + total = 0; + for(d = 0; d < used ; d++) { + if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED))) continue; + + NETDATA_DOUBLE n = cn[d]; + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0)) + n = -n; + + total += n; + } + + // prevent a division by zero + if(total == 0) total = 1; + } + + for (d = 0; d < used; d++) { + if (!rrdr_dimension_should_be_exposed(r->od[d], options)) + continue; + + RRDR_VALUE_FLAGS o = co[d]; + + buffer_json_add_array_item_array(wb); // point + + // add the value + NETDATA_DOUBLE n = cn[d]; + + if(o & RRDR_VALUE_EMPTY) { + if (unlikely(options & RRDR_OPTION_NULL2ZERO)) + buffer_json_add_array_item_double(wb, 0); + else + buffer_json_add_array_item_double(wb, NAN); + } + else { + if (unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0)) + n = -n; + + if (unlikely((options & RRDR_OPTION_PERCENTAGE))) { + n = n * 100 / total; + } + + if(unlikely(i == start && d == 0)) { + r->view.min = r->view.max = n; + } + else { + if (n < r->view.min) r->view.min = n; + if (n > r->view.max) r->view.max = n; + } + + buffer_json_add_array_item_double(wb, n); + } + + // add the anomaly + buffer_json_add_array_item_double(wb, ar[d]); + + // add the point annotations + buffer_json_add_array_item_uint64(wb, o); + + // add the count + if(expose_gbc) + buffer_json_add_array_item_uint64(wb, gbc[d]); + + buffer_json_array_close(wb); // point + } + + buffer_json_array_close(wb); // row + } + } + + buffer_json_array_close(wb); // data + + buffer_json_object_close(wb); // annotations +} diff --git a/web/api/formatters/json/json.h b/web/api/formatters/json/json.h index fb59e5c9a6..d1ab4f9014 100644 --- a/web/api/formatters/json/json.h +++ b/web/api/formatters/json/json.h @@ -6,5 +6,6 @@ #include "../rrd2json.h" void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable); +void rrdr2json_v2(RRDR *r, BUFFER *wb); #endif //NETDATA_API_FORMATTER_JSON_H diff --git a/web/api/formatters/json_wrapper.c b/web/api/formatters/json_wrapper.c index 5e1edc67d0..b7c5720589 100644 --- a/web/api/formatters/json_wrapper.c +++ b/web/api/formatters/json_wrapper.c @@ -676,7 +676,7 @@ static inline long query_target_metrics_latest_values(BUFFER *wb, const char *ke return i; } -static inline size_t rrdr_latest_values(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) { +static inline size_t rrdr_dimension_latest_values(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) { size_t c, i; buffer_json_member_add_array(wb, key); @@ -732,11 +732,54 @@ static inline size_t rrdr_latest_values(BUFFER *wb, const char *key, RRDR *r, RR return i; } -void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options, - RRDR_TIME_GROUPING group_method) -{ +static inline void rrdr_dimension_average_values(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) { + if(!r->dv) + return; + + buffer_json_member_add_array(wb, key); + + bool percentage = r->internal.qt->request.options & RRDR_OPTION_PERCENTAGE; + NETDATA_DOUBLE total = 0; + if(percentage) { + for(size_t c = 0; c < r->d ; c++) { + if(!(r->od[c] & RRDR_DIMENSION_QUERIED)) + continue; + + total += r->dv[c]; + } + } + + for(size_t c = 0; c < r->d ; c++) { + if(!rrdr_dimension_should_be_exposed(r->od[c], options)) + continue; + + if(percentage) + buffer_json_add_array_item_double(wb, r->dv[c] * 100.0 / total); + else + buffer_json_add_array_item_double(wb, r->dv[c]); + } + + buffer_json_array_close(wb); +} + +static void rrdr_timings_v12(BUFFER *wb, const char *key, RRDR *r) { QUERY_TARGET *qt = r->internal.qt; + qt->timings.finished_ut = now_monotonic_usec(); + buffer_json_member_add_object(wb, key); + buffer_json_member_add_double(wb, "prep_ms", (NETDATA_DOUBLE)(qt->timings.preprocessed_ut - qt->timings.received_ut) / USEC_PER_MS); + buffer_json_member_add_double(wb, "query_ms", (NETDATA_DOUBLE)(qt->timings.executed_ut - qt->timings.preprocessed_ut) / USEC_PER_MS); + buffer_json_member_add_double(wb, "group_by_ms", (NETDATA_DOUBLE)(qt->timings.group_by_ut - qt->timings.executed_ut) / USEC_PER_MS); + buffer_json_member_add_double(wb, "output_ms", (NETDATA_DOUBLE)(qt->timings.finished_ut - qt->timings.group_by_ut) / USEC_PER_MS); + buffer_json_member_add_double(wb, "total_ms", (NETDATA_DOUBLE)(qt->timings.finished_ut - qt->timings.received_ut) / USEC_PER_MS); + buffer_json_object_close(wb); +} + +void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb) { + QUERY_TARGET *qt = r->internal.qt; + DATASOURCE_FORMAT format = qt->request.format; + RRDR_OPTIONS options = qt->request.options; + long rows = rrdr_rows(r); char kq[2] = "", // key quote @@ -762,7 +805,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR buffer_json_member_add_time_t(wb, "last_entry", qt->db.last_time_s); buffer_json_member_add_time_t(wb, "after", r->view.after); buffer_json_member_add_time_t(wb, "before", r->view.before); - buffer_json_member_add_string(wb, "group", time_grouping_tostring(group_method)); + buffer_json_member_add_string(wb, "group", time_grouping_tostring(qt->request.time_group_method)); web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", r->view.options); if(!rrdr_dimension_names(wb, "dimension_names", r, options)) @@ -788,7 +831,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR if(!query_target_metrics_latest_values(wb, "latest_values", r, options)) rows = 0; - size_t dimensions = rrdr_latest_values(wb, "view_latest_values", r, options); + size_t dimensions = rrdr_dimension_latest_values(wb, "view_latest_values", r, options); if(!dimensions) rows = 0; @@ -858,13 +901,53 @@ static void query_target_combined_chart_type(BUFFER *wb, QUERY_TARGET *qt, size_ buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(rrdcontext_acquired_chart_type(qt->contexts.array[0].rca))); } -static void rrdr_dimension_units_array_v2(BUFFER *wb, RRDR *r, RRDR_OPTIONS options) { +static void rrdr_grouped_by_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options __maybe_unused) { + QUERY_TARGET *qt = r->internal.qt; + + buffer_json_member_add_array(wb, key); + + if(qt->request.group_by & RRDR_GROUP_BY_SELECTED) + buffer_json_add_array_item_string(wb, "selected"); + + else { + + if(qt->request.group_by & RRDR_GROUP_BY_DIMENSION) + buffer_json_add_array_item_string(wb, "dimension"); + + if(qt->request.group_by & RRDR_GROUP_BY_INSTANCE) + buffer_json_add_array_item_string(wb, "instance"); + + if(qt->request.group_by & RRDR_GROUP_BY_LABEL) { + BUFFER *b = buffer_create(0, NULL); + for (size_t l = 0; l < qt->group_by.used; l++) { + buffer_flush(b); + buffer_fast_strcat(b, "label:", 6); + buffer_strcat(b, qt->group_by.label_keys[l]); + buffer_json_add_array_item_string(wb, buffer_tostring(b)); + } + buffer_free(b); + } + + if(qt->request.group_by & RRDR_GROUP_BY_NODE) + buffer_json_add_array_item_string(wb, "node"); + + if(qt->request.group_by & RRDR_GROUP_BY_CONTEXT) + buffer_json_add_array_item_string(wb, "context"); + + if(qt->request.group_by & RRDR_GROUP_BY_UNITS) + buffer_json_add_array_item_string(wb, "units"); + } + + buffer_json_array_close(wb); // group_by_order +} + +static void rrdr_dimension_units_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) { if(!r->du) return; bool percentage = query_target_has_percentage_units(r->internal.qt); - buffer_json_member_add_array(wb, "units"); + buffer_json_member_add_array(wb, key); for(size_t c = 0; c < r->d ; c++) { if(!rrdr_dimension_should_be_exposed(r->od[c], options)) continue; @@ -877,11 +960,11 @@ static void rrdr_dimension_units_array_v2(BUFFER *wb, RRDR *r, RRDR_OPTIONS opti buffer_json_array_close(wb); } -static void rrdr_dimension_priority_array(BUFFER *wb, RRDR *r, RRDR_OPTIONS options) { +static void rrdr_dimension_priority_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) { if(!r->dp) return; - buffer_json_member_add_array(wb, "priorities"); + buffer_json_member_add_array(wb, key); for(size_t c = 0; c < r->d ; c++) { if(!rrdr_dimension_should_be_exposed(r->od[c], options)) continue; @@ -891,11 +974,11 @@ static void rrdr_dimension_priority_array(BUFFER *wb, RRDR *r, RRDR_OPTIONS opti buffer_json_array_close(wb); } -static void rrdr_dimension_grouped_array(BUFFER *wb, RRDR *r, RRDR_OPTIONS options) { +static void rrdr_dimension_aggregated_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) { if(!r->dgbc) return; - buffer_json_member_add_array(wb, "grouped"); + buffer_json_member_add_array(wb, key); for(size_t c = 0; c < r->d ;c++) { if(!rrdr_dimension_should_be_exposed(r->od[c], options)) continue; @@ -1089,12 +1172,9 @@ static void query_target_detailed_objects_tree(BUFFER *wb, RRDR *r, RRDR_OPTIONS buffer_json_object_close(wb); // hosts } -void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options, - RRDR_TIME_GROUPING group_method) -{ +void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb) { QUERY_TARGET *qt = r->internal.qt; - - long rows = rrdr_rows(r); + RRDR_OPTIONS options = qt->request.options; char kq[2] = "\"", // key quote sq[2] = "\""; // string quote @@ -1181,7 +1261,6 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD buffer_json_member_add_uint64(wb, "contexts_soft_hash", qt->versions.contexts_soft_hash); buffer_json_object_close(wb); - size_t contexts; buffer_json_member_add_object(wb, "summary"); struct summary_total_counts nodes_totals = { 0 }, @@ -1192,7 +1271,7 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD label_key_value_totals = { 0 }; { query_target_summary_nodes_v2(wb, qt, "nodes", &nodes_totals); - contexts = query_target_summary_contexts_v2(wb, qt, "contexts", &contexts_totals); + r->internal.contexts = query_target_summary_contexts_v2(wb, qt, "contexts", &contexts_totals); query_target_summary_instances_v2(wb, qt, "instances", &instances_totals); |