@cryptotaxi247 / netdata-1 / commits / 798c141c4

Split the API formatters in modules (#4504)

* split all API formatters in modules * added markdown formatting * updated csv readme * updated csv readme * more documentation * added more documentation * updated documentation * fixed typo * fixed typo

Costa Tsaousis committed Oct 27, 2018 at 19:44 UTC 798c141c49ee85bddc8f48f25d2cb593ec96da07
38 files changed +1857 -1205
CMakeLists.txt
+16 -2
@@ -363,8 +363,6 @@ set(WEB_PLUGIN_FILES
363 )
364
365 set(API_PLUGIN_FILES
366 - web/api/rrd2json.c
367 - web/api/rrd2json.h
366 web/api/web_api_v1.c
367 web/api/web_api_v1.h
368 web/api/badges/web_buffer_svg.c
@@ -395,6 +393,22 @@ set(API_PLUGIN_FILES
393 web/api/queries/ses/ses.h
394 web/api/queries/des/des.c
395 web/api/queries/des/des.h
396 + web/api/formatters/rrd2json.c
397 + web/api/formatters/rrd2json.h
398 + web/api/formatters/csv/csv.c
399 + web/api/formatters/csv/csv.h
400 + web/api/formatters/json/json.c
401 + web/api/formatters/json/json.h
402 + web/api/formatters/ssv/ssv.c
403 + web/api/formatters/ssv/ssv.h
404 + web/api/formatters/value/value.c
405 + web/api/formatters/value/value.h
406 + web/api/formatters/json_wrapper.c
407 + web/api/formatters/json_wrapper.h
408 + web/api/formatters/charts2json.c
409 + web/api/formatters/charts2json.h
410 + web/api/formatters/rrdset2json.c
411 + web/api/formatters/rrdset2json.h
412 )
413
414 set(STREAMING_PLUGIN_FILES
Makefile.am
+16 -2
@@ -311,8 +311,22 @@ API_PLUGIN_FILES = \
311 web/api/queries/stddev/stddev.h \
312 web/api/queries/sum/sum.c \
313 web/api/queries/sum/sum.h \
314 - web/api/rrd2json.c \
315 - web/api/rrd2json.h \
314 + web/api/formatters/rrd2json.c \
315 + web/api/formatters/rrd2json.h \
316 + web/api/formatters/csv/csv.c \
317 + web/api/formatters/csv/csv.h \
318 + web/api/formatters/json/json.c \
319 + web/api/formatters/json/json.h \
320 + web/api/formatters/ssv/ssv.c \
321 + web/api/formatters/ssv/ssv.h \
322 + web/api/formatters/value/value.c \
323 + web/api/formatters/value/value.h \
324 + web/api/formatters/json_wrapper.c \
325 + web/api/formatters/json_wrapper.h \
326 + web/api/formatters/charts2json.c \
327 + web/api/formatters/charts2json.h \
328 + web/api/formatters/rrdset2json.c \
329 + web/api/formatters/rrdset2json.h \
330 web/api/web_api_v1.c \
331 web/api/web_api_v1.h \
332 $(NULL)
configure.ac
+5
@@ -607,6 +607,11 @@ AC_CONFIG_FILES([
607 web/api/exporters/Makefile
608 web/api/exporters/shell/Makefile
609 web/api/exporters/prometheus/Makefile
610 + web/api/formatters/Makefile
611 + web/api/formatters/csv/Makefile
612 + web/api/formatters/json/Makefile
613 + web/api/formatters/ssv/Makefile
614 + web/api/formatters/value/Makefile
615 web/api/queries/Makefile
616 web/api/queries/average/Makefile
617 web/api/queries/des/Makefile
web/api/exporters/allmetrics.h
+1 -1
@@ -3,7 +3,7 @@
3 #ifndef NETDATA_API_ALLMETRICS_H
4 #define NETDATA_API_ALLMETRICS_H
5
6 -#include "../rrd2json.h"
6 +#include "web/api/formatters/rrd2json.h"
7 #include "shell/allmetrics_shell.h"
8
9 extern int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url);
web/api/formatters/Makefile.am new
+15
@@ -0,0 +1,15 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +SUBDIRS = \
7 + csv \
8 + json \
9 + ssv \
10 + value \
11 + $(NULL)
12 +
13 +dist_noinst_DATA = \
14 + README.md \
15 + $(NULL)
web/api/formatters/README.md new
+72
@@ -0,0 +1,72 @@
1 +# Query formatting
2 +
3 +API data queries need to be formatted before returned to the caller.
4 +Using API parameters, the caller may define the format he/she wishes to get back.
5 +
6 +The following formats are supported:
7 +
8 +format|module|content type|description
9 +:---:|:---:|:---:|:-----
10 +`array`|[ssv](ssv)|application/json|a JSON array
11 +`csv`|[csv](csv)|text/plain|a text table, comma separated, with a header line (dimension names) and `\r\n` at the end of the lines
12 +`csvjsonarray`|[csv](csv)|application/json|a JSON array, with each row as another array (the first row has the dimension names)
13 +`datasource`|[json](json)|application/json|a Google Visualization Provider `datasource` javascript callback
14 +`datatable`|[json](json)|application/json|a Google `datatable`
15 +`html`|[csv](csv)|text/html|an html table
16 +`json`|[json](json)|application/json|a JSON object
17 +`jsonp`|[json](json)|application/json|a JSONP javascript callback
18 +`markdown`|[csv](csv)|text/plain|a markdown table
19 +`ssv`|[ssv](ssv)|text/plain|a space separated list of values
20 +`ssvcomma`|[ssv](ssv)|text/plain|a comma separated list of values
21 +`tsv`|[csv](csv)|text/plain|a TAB delimited `csv` (MS Excel flavor)
22 +
23 +For examples of each format, check the relative module documentation.
24 +
25 +## Metadata with the `jsonwrap` option
26 +
27 +All data queries can be encapsulated to JSON object having metadata about the query and the results.
28 +
29 +This is done by adding the `options=jsonwrap` to the API URL (if there are other `options` append
30 +`,jsonwrap` to the existing ones).
31 +
32 +This is such an object:
33 +
34 +```bash
35 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=system.cpu&after=-3600&points=6&group=average&format=csv&options=nonzero,jsonwrap'
36 +{
37 + "api": 1,
38 + "id": "system.cpu",
39 + "name": "system.cpu",
40 + "view_update_every": 600,
41 + "update_every": 1,
42 + "first_entry": 1540387074,
43 + "last_entry": 1540647070,
44 + "before": 1540647000,
45 + "after": 1540644000,
46 + "dimension_names": ["steal", "softirq", "user", "system", "iowait"],
47 + "dimension_ids": ["steal", "softirq", "user", "system", "iowait"],
48 + "latest_values": [0, 0.2493766, 1.745636, 0.4987531, 0],
49 + "view_latest_values": [0.0158314, 0.0516506, 0.866549, 0.7196127, 0.0050002],
50 + "dimensions": 5,
51 + "points": 6,
52 + "format": "csv",
53 + "result": "time,steal,softirq,user,system,iowait\n2018-10-27 13:30:00,0.0158314,0.0516506,0.866549,0.7196127,0.0050002\n2018-10-27 13:20:00,0.0149856,0.0529183,0.8673155,0.7121144,0.0049979\n2018-10-27 13:10:00,0.0137501,0.053315,0.8578097,0.7197613,0.0054209\n2018-10-27 13:00:00,0.0154252,0.0554688,0.899432,0.7200638,0.0067252\n2018-10-27 12:50:00,0.0145866,0.0495922,0.8404341,0.7011141,0.0041688\n2018-10-27 12:40:00,0.0162366,0.0595954,0.8827475,0.7020573,0.0041636\n",
54 + "min": 0,
55 + "max": 0
56 +}
57 +```
58 +
59 +## Downloading data query result files
60 +
61 +Following the [Google Visualization Provider guidelines](https://developers.google.com/chart/interactive/docs/dev/implementing_data_source),
62 +netdata supports parsing `tqx` options.
63 +
64 +Using these options, any netdata data query can instruct the web browser to download
65 +the result and save it under a given filename.
66 +
67 +For example, to download a CSV file with CPU utilization of the last hour,
68 +[click here](https://registry.my-netdata.io/api/v1/data?chart=system.cpu&after=-3600&format=csv&options=nonzero&tqx=outFileName:system+cpu+utilization+of+the+last_hour.csv).
69 +
70 +
71 +This is done by appending `&tqx=outFileName:FILENAME` to any data query.
72 +The output will be in the format given with `&format=`.
web/api/formatters/charts2json.c new
+103
@@ -0,0 +1,103 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "charts2json.h"
4 +
5 +// generate JSON for the /api/v1/charts API call
6 +
7 +void charts2json(RRDHOST *host, BUFFER *wb) {
8 + static char *custom_dashboard_info_js_filename = NULL;
9 + size_t c, dimensions = 0, memory = 0, alarms = 0;
10 + RRDSET *st;
11 +
12 + time_t now = now_realtime_sec();
13 +
14 + if(unlikely(!custom_dashboard_info_js_filename))
15 + custom_dashboard_info_js_filename = config_get(CONFIG_SECTION_WEB, "custom dashboard_info.js", "");
16 +
17 + buffer_sprintf(wb, "{\n"
18 + "\t\"hostname\": \"%s\""
19 + ",\n\t\"version\": \"%s\""
20 + ",\n\t\"os\": \"%s\""
21 + ",\n\t\"timezone\": \"%s\""
22 + ",\n\t\"update_every\": %d"
23 + ",\n\t\"history\": %ld"
24 + ",\n\t\"custom_info\": \"%s\""
25 + ",\n\t\"charts\": {"
26 + , host->hostname
27 + , host->program_version
28 + , host->os
29 + , host->timezone
30 + , host->rrd_update_every
31 + , host->rrd_history_entries
32 + , custom_dashboard_info_js_filename
33 + );
34 +
35 + c = 0;
36 + rrdhost_rdlock(host);
37 + rrdset_foreach_read(st, host) {
38 + if(rrdset_is_available_for_viewers(st)) {
39 + if(c) buffer_strcat(wb, ",");
40 + buffer_strcat(wb, "\n\t\t\"");
41 + buffer_strcat(wb, st->id);
42 + buffer_strcat(wb, "\": ");
43 + rrdset2json(st, wb, &dimensions, &memory);
44 +
45 + c++;
46 + st->last_accessed_time = now;
47 + }
48 + }
49 +
50 + RRDCALC *rc;
51 + for(rc = host->alarms; rc ; rc = rc->next) {
52 + if(rc->rrdset)
53 + alarms++;
54 + }
55 + rrdhost_unlock(host);
56 +
57 + buffer_sprintf(wb
58 + , "\n\t}"
59 + ",\n\t\"charts_count\": %zu"
60 + ",\n\t\"dimensions_count\": %zu"
61 + ",\n\t\"alarms_count\": %zu"
62 + ",\n\t\"rrd_memory_bytes\": %zu"
63 + ",\n\t\"hosts_count\": %zu"
64 + ",\n\t\"hosts\": ["
65 + , c
66 + , dimensions
67 + , alarms
68 + , memory
69 + , rrd_hosts_available
70 + );
71 +
72 + if(unlikely(rrd_hosts_available > 1)) {
73 + rrd_rdlock();
74 +
75 + size_t found = 0;
76 + RRDHOST *h;
77 + rrdhost_foreach_read(h) {
78 + if(!rrdhost_should_be_removed(h, host, now)) {
79 + buffer_sprintf(wb
80 + , "%s\n\t\t{"
81 + "\n\t\t\t\"hostname\": \"%s\""
82 + "\n\t\t}"
83 + , (found > 0) ? "," : ""
84 + , h->hostname
85 + );
86 +
87 + found++;
88 + }
89 + }
90 +
91 + rrd_unlock();
92 + }
93 + else {
94 + buffer_sprintf(wb
95 + , "\n\t\t{"
96 + "\n\t\t\t\"hostname\": \"%s\""
97 + "\n\t\t}"
98 + , host->hostname
99 + );
100 + }
101 +
102 + buffer_sprintf(wb, "\n\t]\n}\n");
103 +}
web/api/formatters/charts2json.h new
+10
@@ -0,0 +1,10 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_FORMATTER_CHARTS2JSON_H
4 +#define NETDATA_API_FORMATTER_CHARTS2JSON_H
5 +
6 +#include "rrd2json.h"
7 +
8 +extern void charts2json(RRDHOST *host, BUFFER *wb);
9 +
10 +#endif //NETDATA_API_FORMATTER_CHARTS2JSON_H
web/api/formatters/csv/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/formatters/csv/README.md new
+139
@@ -0,0 +1,139 @@
1 +# CSV formatter
2 +
3 +The CSV formatter presents [results of database queries](../../queries) in the following formats:
4 +
5 +format|content type|description
6 +:---:|:---:|:-----
7 +`csv`|text/plain|a text table, comma separated, with a header line (dimension names) and `\r\n` at the end of the lines
8 +`csvjsonarray`|application/json|a JSON array, with each row as another array (the first row has the dimension names)
9 +`tsv`|text/plain|like `csv` but TAB is used instead of comma to separate values (MS Excel flavor)
10 +`html`|text/html|an html table
11 +`markdown`|text/plain|markdown table
12 +
13 +In all formats the date and time is the first column.
14 +
15 +The CSV formatter respects the following API `&options=`:
16 +
17 +option|supported|description
18 +:---:|:---:|:---
19 +`nonzero`|yes|to return only the dimensions that have at least a non-zero value
20 +`flip`|yes|to return the rows older to newer (the default is newer to older)
21 +`seconds`|yes|to return the date and time in unix timestamp
22 +`ms`|yes|to return the date and time in unit timestamp as milliseconds
23 +`percent`|yes|to replace all values with their percentage over the row total
24 +`abs`|yes|to turn all values positive
25 +`null2zero`|yes|to replace gaps with zeros (the default prints the string `null`
26 +
27 +
28 +## Examples
29 +
30 +Get the system total bandwidth for all physical network interfaces, over the last hour,
31 +in 6 rows (one for every 10 minutes), in `csv` format:
32 +
33 +Netdata always returns bandwidth in `kilobits`.
34 +
35 +```bash
36 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=system.net&format=csv&after=-3600&group=sum&points=6&options=abs'
37 +time,received,sent
38 +2018-10-26 23:50:00,90214.67847,215137.79762
39 +2018-10-26 23:40:00,90126.32286,238587.57522
40 +2018-10-26 23:30:00,86061.22688,213389.23526
41 +2018-10-26 23:20:00,85590.75164,206129.01608
42 +2018-10-26 23:10:00,83163.30691,194311.77384
43 +2018-10-26 23:00:00,85167.29657,197538.07773
44 +```
45 +
46 +---
47 +
48 +Get the max RAM used by the SQL server and any cron jobs, over the last hour, in 2 rows (one for every 30
49 +minutes), in `tsv` format, and format the date and time as unix timestamp:
50 +
51 +Netdata always returns memory in `MB`.
52 +
53 +```bash
54 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=apps.mem&format=tsv&after=-3600&group=max&points=2&options=nonzero,seconds&dimensions=sql,cron'
55 +time sql cron
56 +1540598400 61.95703 0.25
57 +1540596600 61.95703 0.25
58 +```
59 +
60 +---
61 +
62 +Get an HTML table of the last 4 values (4 seconds) of system CPU utilization:
63 +
64 +Netdata always returns CPU utilization as `%`.
65 +
66 +```bash
67 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=system.cpu&format=html&after=-4&options=nonzero'
68 +<html>
69 +<center>
70 +<table border="0" cellpadding="5" cellspacing="5">
71 +<tr><td>time</td><td>softirq</td><td>user</td><td>system</td></tr>
72 +<tr><td>2018-10-27 00:16:07</td><td>0.25</td><td>1</td><td>0.75</td></tr>
73 +<tr><td>2018-10-27 00:16:06</td><td>0</td><td>1.0025063</td><td>0.5012531</td></tr>
74 +<tr><td>2018-10-27 00:16:05</td><td>0</td><td>1</td><td>0.75</td></tr>
75 +<tr><td>2018-10-27 00:16:04</td><td>0</td><td>1.0025063</td><td>0.7518797</td></tr>
76 +</table>
77 +</center>
78 +</html>
79 +```
80 +
81 +This is how it looks when rendered by a web browser:
82 +
83 +![image](https://user-images.githubusercontent.com/2662304/47597887-bafbf480-d99c-11e8-864a-d880bb8d2e5b.png)
84 +
85 +
86 +---
87 +
88 +Get a JSON array with the average bandwidth rate of the mysql server, over the last hour, in 6 values
89 +(one every 10 minutes), and return the date and time in milliseconds:
90 +
91 +Netdata always returns bandwidth rates in `kilobits/s`.
92 +
93 +```bash
94 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=mysql_local.net&format=csvjsonarray&after=-3600&points=6&group=average&options=abs,ms'
95 +[
96 +["time","in","out"],
97 +[1540599600000,0.7499986,120.2810185],
98 +[1540599000000,0.7500019,120.2815509],
99 +[1540598400000,0.7499999,120.2812319],
100 +[1540597800000,0.7500044,120.2819634],
101 +[1540597200000,0.7499968,120.2807337],
102 +[1540596600000,0.7499988,120.2810527]
103 +]
104 +```
105 +
106 +---
107 +
108 +Get the number of processes started per minute, for the last 10 minutes, in `markdown` format:
109 +
110 +```bash
111 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=system.forks&format=markdown&after=-600&points=10&group=sum'
112 +time|started
113 +:---:|:---:
114 +2018-10-27 03:52:00|245.1706149
115 +2018-10-27 03:51:00|152.6654636
116 +2018-10-27 03:50:00|163.1755789
117 +2018-10-27 03:49:00|176.1574766
118 +2018-10-27 03:48:00|178.0137076
119 +2018-10-27 03:47:00|183.8306543
120 +2018-10-27 03:46:00|264.1635621
121 +2018-10-27 03:45:00|205.001551
122 +2018-10-27 03:44:00|7026.9852167
123 +2018-10-27 03:43:00|205.9904794
124 +```
125 +
126 +And this is how it looks when formatted:
127 +
128 +time|started
129 +:---:|:---:
130 +2018-10-27 03:52:00|245.1706149
131 +2018-10-27 03:51:00|152.6654636
132 +2018-10-27 03:50:00|163.1755789
133 +2018-10-27 03:49:00|176.1574766
134 +2018-10-27 03:48:00|178.0137076
135 +2018-10-27 03:47:00|183.8306543
136 +2018-10-27 03:46:00|264.1635621
137 +2018-10-27 03:45:00|205.001551
138 +2018-10-27 03:44:00|7026.9852167
139 +2018-10-27 03:43:00|205.9904794
web/api/formatters/csv/csv.c new
+143
@@ -0,0 +1,143 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "libnetdata/libnetdata.h"
4 +#include "csv.h"
5 +
6 +void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines) {
7 + rrdset_check_rdlock(r->st);
8 +
9 + //info("RRD2CSV(): %s: BEGIN", r->st->id);
10 + long c, i;
11 + RRDDIM *d;
12 +
13 + // print the csv header
14 + for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
15 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
16 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
17 +
18 + if(!i) {
19 + buffer_strcat(wb, startline);
20 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
21 + buffer_strcat(wb, "time");
22 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
23 + }
24 + buffer_strcat(wb, separator);
25 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
26 + buffer_strcat(wb, d->name);
27 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
28 + i++;
29 + }
30 + buffer_strcat(wb, endline);
31 +
32 + if(format == DATASOURCE_CSV_MARKDOWN) {
33 + // print the --- line after header
34 + for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
35 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
36 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
37 +
38 + if(!i) {
39 + buffer_strcat(wb, startline);
40 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
41 + buffer_strcat(wb, ":---:");
42 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
43 + }
44 + buffer_strcat(wb, separator);
45 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
46 + buffer_strcat(wb, ":---:");
47 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
48 + i++;
49 + }
50 + buffer_strcat(wb, endline);
51 + }
52 +
53 + if(!i) {
54 + // no dimensions present
55 + return;
56 + }
57 +
58 + long start = 0, end = rrdr_rows(r), step = 1;
59 + if(!(options & RRDR_OPTION_REVERSED)) {
60 + start = rrdr_rows(r) - 1;
61 + end = -1;
62 + step = -1;
63 + }
64 +
65 + // for each line in the array
66 + calculated_number total = 1;
67 + for(i = start; i != end ;i += step) {
68 + calculated_number *cn = &r->v[ i * r->d ];
69 + RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
70 +
71 + buffer_strcat(wb, betweenlines);
72 + buffer_strcat(wb, startline);
73 +
74 + time_t now = r->t[i];
75 +
76 + if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
77 + // print the timestamp of the line
78 + buffer_rrd_value(wb, (calculated_number)now);
79 + // in ms
80 + if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
81 + }
82 + else {
83 + // generate the local date time
84 + struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
85 + if(!tm) { error("localtime() failed."); continue; }
86 + buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
87 + }
88 +
89 + int set_min_max = 0;
90 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
91 + total = 0;
92 + for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
93 + calculated_number n = cn[c];
94 +
95 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
96 + n = -n;
97 +
98 + total += n;
99 + }
100 + // prevent a division by zero
101 + if(total == 0) total = 1;
102 + set_min_max = 1;
103 + }
104 +
105 + // for each dimension
106 + for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
107 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
108 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
109 +
110 + buffer_strcat(wb, separator);
111 +
112 + calculated_number n = cn[c];
113 +
114 + if(co[c] & RRDR_VALUE_EMPTY) {
115 + if(options & RRDR_OPTION_NULL2ZERO)
116 + buffer_strcat(wb, "0");
117 + else
118 + buffer_strcat(wb, "null");
119 + }
120 + else {
121 + if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
122 + n = -n;
123 +
124 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
125 + n = n * 100 / total;
126 +
127 + if(unlikely(set_min_max)) {
128 + r->min = r->max = n;
129 + set_min_max = 0;
130 + }
131 +
132 + if(n < r->min) r->min = n;
133 + if(n > r->max) r->max = n;
134 + }
135 +
136 + buffer_rrd_value(wb, n);
137 + }
138 + }
139 +
140 + buffer_strcat(wb, endline);
141 + }
142 + //info("RRD2CSV(): %s: END", r->st->id);
143 +}
web/api/formatters/csv/csv.h new
+12
@@ -0,0 +1,12 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_FORMATTER_CSV_H
4 +#define NETDATA_API_FORMATTER_CSV_H
5 +
6 +#include "web/api/queries/rrdr.h"
7 +
8 +extern void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines);
9 +
10 +#include "../rrd2json.h"
11 +
12 +#endif //NETDATA_API_FORMATTER_CSV_H
web/api/formatters/json/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/formatters/json/README.md new
+150
@@ -0,0 +1,150 @@
1 +# JSON formatter
2 +
3 +The CSV formatter presents [results of database queries](../../queries) in the following formats:
4 +
5 +format|content type|description
6 +:---:|:---:|:-----
7 +`json`|application/json|return the query result as a json object
8 +`jsonp`|application/json|return the query result as a JSONP javascript callback
9 +`datatable`|application/json|return the query result as a Google `datatable`
10 +`datasource`|application/json|return the query result as a Google Visualization Provider `datasource` javascript callback
11 +
12 +The CSV formatter respects the following API `&options=`:
13 +
14 +option|supported|description
15 +:---:|:---:|:---
16 +`google_json`|yes|enable the Google flavor of JSON (using double quotes for strings and `Date()` function for dates
17 +`objectrows`|yes|return each row as an object, instead of an array
18 +`nonzero`|yes|to return only the dimensions that have at least a non-zero value
19 +`flip`|yes|to return the rows older to newer (the default is newer to older)
20 +`seconds`|yes|to return the date and time in unix timestamp
21 +`ms`|yes|to return the date and time in unit timestamp as milliseconds
22 +`percent`|yes|to replace all values with their percentage over the row total
23 +`abs`|yes|to turn all values positive
24 +`null2zero`|yes|to replace gaps with zeros (the default prints the string `null`
25 +
26 +## Examples
27 +
28 +To show the differences between each format, in the following examples we query the same
29 +chart (having just one dimension called `active`), changing only the query `format` and its `options`.
30 +
31 +> Using `format=json` and `options=`
32 +
33 +```bash
34 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=nginx_local.connections&after=-3600&points=6&group=average&format=json&options='
35 +{
36 + "labels": ["time", "active"],
37 + "data":
38 + [
39 + [ 1540644600, 224.2516667],
40 + [ 1540644000, 229.29],
41 + [ 1540643400, 222.41],
42 + [ 1540642800, 226.6816667],
43 + [ 1540642200, 246.4083333],
44 + [ 1540641600, 241.0966667]
45 + ]
46 +}
47 +```
48 +
49 +> Using `format=json` and `options=objectrows`
50 +
51 +```bash
52 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=nginx_local.connections&after=-3600&points=6&group=average&format=json&options=objectrows'
53 +{
54 + "labels": ["time", "active"],
55 + "data":
56 + [
57 + { "time": 1540644600, "active": 224.2516667},
58 + { "time": 1540644000, "active": 229.29},
59 + { "time": 1540643400, "active": 222.41},
60 + { "time": 1540642800, "active": 226.6816667},
61 + { "time": 1540642200, "active": 246.4083333},
62 + { "time": 1540641600, "active": 241.0966667}
63 + ]
64 +}
65 +```
66 +
67 +> Using `format=json` and `options=objectrows,google_json`
68 +
69 +```bash
70 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=nginx_local.connections&after=-3600&points=6&group=average&formatjson&options=objectrows,google_json'
71 +{
72 + "labels": ["time", "active"],
73 + "data":
74 + [
75 + { "time": new Date(2018,9,27,12,50,0), "active": 224.2516667},
76 + { "time": new Date(2018,9,27,12,40,0), "active": 229.29},
77 + { "time": new Date(2018,9,27,12,30,0), "active": 222.41},
78 + { "time": new Date(2018,9,27,12,20,0), "active": 226.6816667},
79 + { "time": new Date(2018,9,27,12,10,0), "active": 246.4083333},
80 + { "time": new Date(2018,9,27,12,0,0), "active": 241.0966667}
81 + ]
82 +}
83 +```
84 +
85 +> Using `format=jsonp` and `options=`
86 +
87 +```bash
88 +curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=nginx_local.connections&after=-3600&points=6&group=average&formjsonp&options='
89 +callback({
90 + "labels": ["time", "active"],
91 + "data":
92 + [
93 + [ 1540645200, 235.885],
94 + [ 1540644600, 224.2516667],
95 + [ 1540644000, 229.29],
96 + [ 1540643400, 222.41],
97 + [ 1540642800, 226.6816667],
98 + [ 1540642200, 246.4083333]
99 + ]
100 +});
101 +```
102 +
103 +> Using `format=datatable` and `options=`
104 +
105 +```bash
106 +$ curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=nginx_local.connections&after=-3600&points=6&group=average&formdatatable&options='
107 +{
108 + "cols":
109 + [
110 + {"id":"","label":"time","pattern":"","type":"datetime"},
111 + {"id":"","label":"","pattern":"","type":"string","p":{"role":"annotation"}},
112 + {"id":"","label":"","pattern":"","type":"string","p":{"role":"annotationText"}},
113 + {"id":"","label":"active","pattern":"","type":"number"}
114 + ],
115 + "rows":
116 + [
117 + {"c":[{"v":"Date(2018,9,27,13,0,0)"},{"v":null},{"v":null},{"v":235.885}]},
118 + {"c":[{"v":"Date(2018,9,27,12,50,0)"},{"v":null},{"v":null},{"v":224.2516667}]},
119 + {"c":[{"v":"Date(2018,9,27,12,40,0)"},{"v":null},{"v":null},{"v":229.29}]},
120 + {"c":[{"v":"Date(2018,9,27,12,30,0)"},{"v":null},{"v":null},{"v":222.41}]},
121 + {"c":[{"v":"Date(2018,9,27,12,20,0)"},{"v":null},{"v":null},{"v":226.6816667}]},
122 + {"c":[{"v":"Date(2018,9,27,12,10,0)"},{"v":null},{"v":null},{"v":246.4083333}]}
123 + ]
124 +}
125 +```
126 +
127 +> Using `format=datasource` and `options=`
128 +
129 +```bash
130 +curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=nginx_local.connections&after=-3600&points=6&group=average&format=datasource&options='
131 +google.visualization.Query.setResponse({version:'0.6',reqId:'0',status:'ok',sig:'1540645368',table:{
132 + "cols":
133 + [
134 + {"id":"","label":"time","pattern":"","type":"datetime"},
135 + {"id":"","label":"","pattern":"","type":"string","p":{"role":"annotation"}},
136 + {"id":"","label":"","pattern":"","type":"string","p":{"role":"annotationText"}},
137 + {"id":"","label":"active","pattern":"","type":"number"}
138 + ],
139 + "rows":
140 + [
141 + {"c":[{"v":"Date(2018,9,27,13,0,0)"},{"v":null},{"v":null},{"v":235.885}]},
142 + {"c":[{"v":"Date(2018,9,27,12,50,0)"},{"v":null},{"v":null},{"v":224.2516667}]},
143 + {"c":[{"v":"Date(2018,9,27,12,40,0)"},{"v":null},{"v":null},{"v":229.29}]},
144 + {"c":[{"v":"Date(2018,9,27,12,30,0)"},{"v":null},{"v":null},{"v":222.41}]},
145 + {"c":[{"v":"Date(2018,9,27,12,20,0)"},{"v":null},{"v":null},{"v":226.6816667}]},
146 + {"c":[{"v":"Date(2018,9,27,12,10,0)"},{"v":null},{"v":null},{"v":246.4083333}]}
147 + ]
148 +}});
149 +```
150 +
web/api/formatters/json/json.c new
+244
@@ -0,0 +1,244 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "json.h"
4 +
5 +#define JSON_DATES_JS 1
6 +#define JSON_DATES_TIMESTAMP 2
7 +
8 +void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
9 + rrdset_check_rdlock(r->st);
10 +
11 + //info("RRD2JSON(): %s: BEGIN", r->st->id);
12 + int row_annotations = 0, dates, dates_with_new = 0;
13 + char kq[2] = "", // key quote
14 + sq[2] = "", // string quote
15 + pre_label[101] = "", // before each label
16 + post_label[101] = "", // after each label
17 + pre_date[101] = "", // the beginning of line, to the date
18 + post_date[101] = "", // closing the date
19 + pre_value[101] = "", // before each value
20 + post_value[101] = "", // after each value
21 + post_line[101] = "", // at the end of each row
22 + normal_annotation[201] = "", // default row annotation
23 + overflow_annotation[201] = "", // overflow row annotation
24 + data_begin[101] = "", // between labels and values
25 + finish[101] = ""; // at the end of everything
26 +
27 + if(datatable) {
28 + dates = JSON_DATES_JS;
29 + if( options & RRDR_OPTION_GOOGLE_JSON ) {
30 + kq[0] = '\0';
31 + sq[0] = '\'';
32 + }
33 + else {
34 + kq[0] = '"';
35 + sq[0] = '"';
36 + }
37 + row_annotations = 1;
38 + snprintfz(pre_date, 100, " {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
39 + snprintfz(post_date, 100, "%s}", sq);
40 + snprintfz(pre_label, 100, ",\n {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
41 + snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
42 + snprintfz(pre_value, 100, ",{%sv%s:", kq, kq);
43 + strcpy(post_value, "}");
44 + strcpy(post_line, "]}");
45 + snprintfz(data_begin, 100, "\n ],\n %srows%s:\n [\n", kq, kq);
46 + strcpy(finish, "\n ]\n}");
47 +
48 + snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
49 + snprintfz(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
50 +
51 + buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
52 + buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
53 + buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
54 + buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
55 +
56 + // remove the valueobjects flag
57 + // google wants its own keys
58 + if(options & RRDR_OPTION_OBJECTSROWS)
59 + options &= ~RRDR_OPTION_OBJECTSROWS;
60 + }
61 + else {
62 + kq[0] = '"';
63 + sq[0] = '"';
64 + if(options & RRDR_OPTION_GOOGLE_JSON) {
65 + dates = JSON_DATES_JS;
66 + dates_with_new = 1;
67 + }
68 + else {
69 + dates = JSON_DATES_TIMESTAMP;
70 + dates_with_new = 0;
71 + }
72 + if( options & RRDR_OPTION_OBJECTSROWS )
73 + strcpy(pre_date, " { ");
74 + else
75 + strcpy(pre_date, " [ ");
76 + strcpy(pre_label, ", \"");
77 + strcpy(post_label, "\"");
78 + strcpy(pre_value, ", ");
79 + if( options & RRDR_OPTION_OBJECTSROWS )
80 + strcpy(post_line, "}");
81 + else
82 + strcpy(post_line, "]");
83 + snprintfz(data_begin, 100, "],\n %sdata%s:\n [\n", kq, kq);
84 + strcpy(finish, "\n ]\n}");
85 +
86 + buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
87 + buffer_sprintf(wb, "%stime%s", sq, sq);
88 + }
89 +
90 + // -------------------------------------------------------------------------
91 + // print the JSON header
92 +
93 + long c, i;
94 + RRDDIM *rd;
95 +
96 + // print the header lines
97 + for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
98 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
99 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
100 +
101 + buffer_strcat(wb, pre_label);
102 + buffer_strcat(wb, rd->name);
103 + buffer_strcat(wb, post_label);
104 + i++;
105 + }
106 + if(!i) {
107 + buffer_strcat(wb, pre_label);
108 + buffer_strcat(wb, "no data");
109 + buffer_strcat(wb, post_label);
110 + }
111 +
112 + // print the begin of row data
113 + buffer_strcat(wb, data_begin);
114 +
115 + // if all dimensions are hidden, print a null
116 + if(!i) {
117 + buffer_strcat(wb, finish);
118 + return;
119 + }
120 +
121 + long start = 0, end = rrdr_rows(r), step = 1;
122 + if(!(options & RRDR_OPTION_REVERSED)) {
123 + start = rrdr_rows(r) - 1;
124 + end = -1;
125 + step = -1;
126 + }
127 +
128 + // for each line in the array
129 + calculated_number total = 1;
130 + for(i = start; i != end ;i += step) {
131 + calculated_number *cn = &r->v[ i * r->d ];
132 + RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
133 +
134 + time_t now = r->t[i];
135 +
136 + if(dates == JSON_DATES_JS) {
137 + // generate the local date time
138 + struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
139 + if(!tm) { error("localtime_r() failed."); continue; }
140 +
141 + if(likely(i != start)) buffer_strcat(wb, ",\n");
142 + buffer_strcat(wb, pre_date);
143 +
144 + if( options & RRDR_OPTION_OBJECTSROWS )
145 + buffer_sprintf(wb, "%stime%s: ", kq, kq);
146 +
147 + if(dates_with_new)
148 + buffer_strcat(wb, "new ");
149 +
150 + buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
151 +
152 + buffer_strcat(wb, post_date);
153 +
154 + if(row_annotations) {
155 + // google supports one annotation per row
156 + int annotation_found = 0;
157 + for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
158 + if(co[c] & RRDR_VALUE_RESET) {
159 + buffer_strcat(wb, overflow_annotation);
160 + annotation_found = 1;
161 + break;
162 + }
163 + }
164 + if(!annotation_found)
165 + buffer_strcat(wb, normal_annotation);
166 + }
167 + }
168 + else {
169 + // print the timestamp of the line
170 + if(likely(i != start)) buffer_strcat(wb, ",\n");
171 + buffer_strcat(wb, pre_date);
172 +
173 + if( options & RRDR_OPTION_OBJECTSROWS )
174 + buffer_sprintf(wb, "%stime%s: ", kq, kq);
175 +
176 + buffer_rrd_value(wb, (calculated_number)r->t[i]);
177 + // in ms
178 + if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
179 +
180 + buffer_strcat(wb, post_date);
181 + }
182 +
183 + int set_min_max = 0;
184 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
185 + total = 0;
186 + for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
187 + calculated_number n = cn[c];
188 +
189 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
190 + n = -n;
191 +
192 + total += n;
193 + }
194 + // prevent a division by zero
195 + if(total == 0) total = 1;
196 + set_min_max = 1;
197 + }
198 +
199 + // for each dimension
200 + for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
201 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
202 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
203 +
204 + calculated_number n = cn[c];
205 +
206 + buffer_strcat(wb, pre_value);
207 +
208 + if( options & RRDR_OPTION_OBJECTSROWS )
209 + buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
210 +
211 + if(co[c] & RRDR_VALUE_EMPTY) {
212 + if(options & RRDR_OPTION_NULL2ZERO)
213 + buffer_strcat(wb, "0");
214 + else
215 + buffer_strcat(wb, "null");
216 + }
217 + else {
218 + if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
219 + n = -n;
220 +
221 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
222 + n = n * 100 / total;
223 +
224 + if(unlikely(set_min_max)) {
225 + r->min = r->max = n;
226 + set_min_max = 0;
227 + }
228 +
229 + if(n < r->min) r->min = n;
230 + if(n > r->max) r->max = n;
231 + }
232 +
233 + buffer_rrd_value(wb, n);
234 + }
235 +
236 + buffer_strcat(wb, post_value);
237 + }
238 +
239 + buffer_strcat(wb, post_line);
240 + }
241 +
242 + buffer_strcat(wb, finish);
243 + //info("RRD2JSON(): %s: END", r->st->id);
244 +}
web/api/formatters/json/json.h new
+10
@@ -0,0 +1,10 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_FORMATTER_JSON_H
4 +#define NETDATA_API_FORMATTER_JSON_H
5 +
6 +#include "../rrd2json.h"
7 +
8 +extern void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable);
9 +
10 +#endif //NETDATA_API_FORMATTER_JSON_H
web/api/formatters/json_wrapper.c new
+210
@@ -0,0 +1,210 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "json_wrapper.h"
4 +
5 +void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value) {
6 + rrdset_check_rdlock(r->st);
7 +
8 + long rows = rrdr_rows(r);
9 + long c, i;
10 + RRDDIM *rd;
11 +
12 + //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
13 + char kq[2] = "", // key quote
14 + sq[2] = ""; // string quote
15 +
16 + if( options & RRDR_OPTION_GOOGLE_JSON ) {
17 + kq[0] = '\0';
18 + sq[0] = '\'';
19 + }
20 + else {
21 + kq[0] = '"';
22 + sq[0] = '"';
23 + }
24 +
25 + buffer_sprintf(wb, "{\n"
26 + " %sapi%s: 1,\n"
27 + " %sid%s: %s%s%s,\n"
28 + " %sname%s: %s%s%s,\n"
29 + " %sview_update_every%s: %d,\n"
30 + " %supdate_every%s: %d,\n"
31 + " %sfirst_entry%s: %u,\n"
32 + " %slast_entry%s: %u,\n"
33 + " %sbefore%s: %u,\n"
34 + " %safter%s: %u,\n"
35 + " %sdimension_names%s: ["
36 + , kq, kq
37 + , kq, kq, sq, r->st->id, sq
38 + , kq, kq, sq, r->st->name, sq
39 + , kq, kq, r->update_every
40 + , kq, kq, r->st->update_every
41 + , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
42 + , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
43 + , kq, kq, (uint32_t)r->before
44 + , kq, kq, (uint32_t)r->after
45 + , kq, kq);
46 +
47 + for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
48 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
49 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
50 +
51 + if(i) buffer_strcat(wb, ", ");
52 + buffer_strcat(wb, sq);
53 + buffer_strcat(wb, rd->name);
54 + buffer_strcat(wb, sq);
55 + i++;
56 + }
57 + if(!i) {
58 +#ifdef NETDATA_INTERNAL_CHECKS
59 + error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
60 +#endif
61 + rows = 0;
62 + buffer_strcat(wb, sq);
63 + buffer_strcat(wb, "no data");
64 + buffer_strcat(wb, sq);
65 + }
66 +
67 + buffer_sprintf(wb, "],\n"
68 + " %sdimension_ids%s: ["
69 + , kq, kq);
70 +
71 + for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
72 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
73 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
74 +
75 + if(i) buffer_strcat(wb, ", ");
76 + buffer_strcat(wb, sq);
77 + buffer_strcat(wb, rd->id);
78 + buffer_strcat(wb, sq);
79 + i++;
80 + }
81 + if(!i) {
82 + rows = 0;
83 + buffer_strcat(wb, sq);
84 + buffer_strcat(wb, "no data");
85 + buffer_strcat(wb, sq);
86 + }
87 +
88 + buffer_sprintf(wb, "],\n"
89 + " %slatest_values%s: ["
90 + , kq, kq);
91 +
92 + for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
93 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
94 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
95 +
96 + if(i) buffer_strcat(wb, ", ");
97 + i++;
98 +
99 + storage_number n = rd->values[rrdset_last_slot(r->st)];
100 +
101 + if(!does_storage_number_exist(n))
102 + buffer_strcat(wb, "null");
103 + else
104 + buffer_rrd_value(wb, unpack_storage_number(n));
105 + }
106 + if(!i) {
107 + rows = 0;
108 + buffer_strcat(wb, "null");
109 + }
110 +
111 + buffer_sprintf(wb, "],\n"
112 + " %sview_latest_values%s: ["
113 + , kq, kq);
114 +
115 + i = 0;
116 + if(rows) {
117 + calculated_number total = 1;
118 +
119 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
120 + total = 0;
121 + for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
122 + calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
123 + calculated_number n = cn[c];
124 +
125 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
126 + n = -n;
127 +
128 + total += n;
129 + }
130 + // prevent a division by zero
131 + if(total == 0) total = 1;
132 + }
133 +
134 + for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
135 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
136 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
137 +
138 + if(i) buffer_strcat(wb, ", ");
139 + i++;
140 +
141 + calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
142 + RRDR_VALUE_FLAGS *co = &r->o[ (rrdr_rows(r) - 1) * r->d ];
143 + calculated_number n = cn[c];
144 +
145 + if(co[c] & RRDR_VALUE_EMPTY) {
146 + if(options & RRDR_OPTION_NULL2ZERO)
147 + buffer_strcat(wb, "0");
148 + else
149 + buffer_strcat(wb, "null");
150 + }
151 + else {
152 + if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
153 + n = -n;
154 +
155 + if(unlikely(options & RRDR_OPTION_PERCENTAGE))
156 + n = n * 100 / total;
157 +
158 + buffer_rrd_value(wb, n);
159 + }
160 + }
161 + }
162 + if(!i) {
163 + rows = 0;
164 + buffer_strcat(wb, "null");
165 + }
166 +
167 + buffer_sprintf(wb, "],\n"
168 + " %sdimensions%s: %ld,\n"
169 + " %spoints%s: %ld,\n"
170 + " %sformat%s: %s"
171 + , kq, kq, i
172 + , kq, kq, rows
173 + , kq, kq, sq
174 + );
175 +
176 + rrdr_buffer_print_format(wb, format);
177 +
178 + buffer_sprintf(wb, "%s,\n"
179 + " %sresult%s: "
180 + , sq
181 + , kq, kq
182 + );
183 +
184 + if(string_value) buffer_strcat(wb, sq);
185 + //info("JSONWRAPPER(): %s: END", r->st->id);
186 +}
187 +
188 +void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value) {
189 + (void)format;
190 +
191 + char kq[2] = "", // key quote
192 + sq[2] = ""; // string quote
193 +
194 + if( options & RRDR_OPTION_GOOGLE_JSON ) {
195 + kq[0] = '\0';
196 + sq[0] = '\'';
197 + }
198 + else {
199 + kq[0] = '"';
200 + sq[0] = '"';
201 + }
202 +
203 + if(string_value) buffer_strcat(wb, sq);
204 +
205 + buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
206 + buffer_rrd_value(wb, r->min);
207 + buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
208 + buffer_rrd_value(wb, r->max);
209 + buffer_strcat(wb, "\n}\n");
210 +}
web/api/formatters/json_wrapper.h new
+11
@@ -0,0 +1,11 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_FORMATTER_JSON_WRAPPER_H
4 +#define NETDATA_API_FORMATTER_JSON_WRAPPER_H
5 +
6 +#include "rrd2json.h"
7 +
8 +extern void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value);
9 +extern void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value);
10 +
11 +#endif //NETDATA_API_FORMATTER_JSON_WRAPPER_H
web/api/formatters/rrd2json.c new
+298
@@ -0,0 +1,298 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "web/api/web_api_v1.h"
4 +
5 +void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) {
6 + rrdset2json(st, wb, NULL, NULL);
7 +}
8 +
9 +void rrdr_buffer_print_format(BUFFER *wb, uint32_t format) {
10 + switch(format) {
11 + case DATASOURCE_JSON:
12 + buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
13 + break;
14 +
15 + case DATASOURCE_DATATABLE_JSON:
16 + buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
17 + break;
18 +
19 + case DATASOURCE_DATATABLE_JSONP:
20 + buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
21 + break;
22 +
23 + case DATASOURCE_JSONP:
24 + buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
25 + break;
26 +
27 + case DATASOURCE_SSV:
28 + buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
29 + break;
30 +
31 + case DATASOURCE_CSV:
32 + buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
33 + break;
34 +
35 + case DATASOURCE_TSV:
36 + buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
37 + break;
38 +
39 + case DATASOURCE_HTML:
40 + buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
41 + break;
42 +
43 + case DATASOURCE_JS_ARRAY:
44 + buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
45 + break;
46 +
47 + case DATASOURCE_SSV_COMMA:
48 + buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
49 + break;
50 +
51 + default:
52 + buffer_strcat(wb, "unknown");
53 + break;
54 + }
55 +}
56 +
57 +int rrdset2value_api_v1(
58 + RRDSET *st
59 + , BUFFER *wb
60 + , calculated_number *n
61 + , const char *dimensions
62 + , long points
63 + , long long after
64 + , long long before
65 + , int group_method
66 + , long group_time
67 + , uint32_t options
68 + , time_t *db_after
69 + , time_t *db_before
70 + , int *value_is_null
71 +) {
72 + RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions);
73 + if(!r) {
74 + if(value_is_null) *value_is_null = 1;
75 + return 500;
76 + }
77 +
78 + if(rrdr_rows(r) == 0) {
79 + rrdr_free(r);
80 +
81 + if(db_after) *db_after = 0;
82 + if(db_before) *db_before = 0;
83 + if(value_is_null) *value_is_null = 1;
84 +
85 + return 400;
86 + }
87 +
88 + if(wb) {
89 + if (r->result_options & RRDR_RESULT_OPTION_RELATIVE)
90 + buffer_no_cacheable(wb);
91 + else if (r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
92 + buffer_cacheable(wb);
93 + }
94 +
95 + if(db_after) *db_after = r->after;
96 + if(db_before) *db_before = r->before;
97 +
98 + long i = (!(options & RRDR_OPTION_REVERSED))?rrdr_rows(r) - 1:0;
99 + *n = rrdr2value(r, i, options, value_is_null);
100 +
101 + rrdr_free(r);
102 + return 200;
103 +}
104 +
105 +int rrdset2anything_api_v1(
106 + RRDSET *st
107 + , BUFFER *wb
108 + , BUFFER *dimensions
109 + , uint32_t format
110 + , long points
111 + , long long after
112 + , long long before
113 + , int group_method
114 + , long group_time
115 + , uint32_t options
116 + , time_t *latest_timestamp
117 +) {
118 + st->last_accessed_time = now_realtime_sec();
119 +
120 + RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions?buffer_tostring(dimensions):NULL);
121 + if(!r) {
122 + buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
123 + return 500;
124 + }
125 +
126 + if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
127 + buffer_no_cacheable(wb);
128 + else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
129 + buffer_cacheable(wb);
130 +
131 + if(latest_timestamp && rrdr_rows(r) > 0)
132 + *latest_timestamp = r->before;
133 +
134 + switch(format) {
135 + case DATASOURCE_SSV:
136 + if(options & RRDR_OPTION_JSON_WRAP) {
137 + wb->contenttype = CT_APPLICATION_JSON;
138 + rrdr_json_wrapper_begin(r, wb, format, options, 1);
139 + rrdr2ssv(r, wb, options, "", " ", "");
140 + rrdr_json_wrapper_end(r, wb, format, options, 1);
141 + }
142 + else {
143 + wb->contenttype = CT_TEXT_PLAIN;
144 + rrdr2ssv(r, wb, options, "", " ", "");
145 + }
146 + break;
147 +
148 + case DATASOURCE_SSV_COMMA:
149 + if(options & RRDR_OPTION_JSON_WRAP) {
150 + wb->contenttype = CT_APPLICATION_JSON;
151 + rrdr_json_wrapper_begin(r, wb, format, options, 1);
152 + rrdr2ssv(r, wb, options, "", ",", "");
153 + rrdr_json_wrapper_end(r, wb, format, options, 1);
154 + }
155 + else {
156 + wb->contenttype = CT_TEXT_PLAIN;
157 + rrdr2ssv(r, wb, options, "", ",", "");
158 + }
159 + break;
160 +
161 + case DATASOURCE_JS_ARRAY:
162 + if(options & RRDR_OPTION_JSON_WRAP) {
163 + wb->contenttype = CT_APPLICATION_JSON;
164 + rrdr_json_wrapper_begin(r, wb, format, options, 0);
165 + rrdr2ssv(r, wb, options, "[", ",", "]");
166 + rrdr_json_wrapper_end(r, wb, format, options, 0);
167 + }
168 + else {
169 + wb->contenttype = CT_APPLICATION_JSON;
170 + rrdr2ssv(r, wb, options, "[", ",", "]");
171 + }
172 + break;
173 +
174 + case DATASOURCE_CSV:
175 + if(options & RRDR_OPTION_JSON_WRAP) {
176 + wb->contenttype = CT_APPLICATION_JSON;
177 + rrdr_json_wrapper_begin(r, wb, format, options, 1);
178 + rrdr2csv(r, wb, format, options, "", ",", "\\n", "");
179 + rrdr_json_wrapper_end(r, wb, format, options, 1);
180 + }
181 + else {
182 + wb->contenttype = CT_TEXT_PLAIN;
183 + rrdr2csv(r, wb, format, options, "", ",", "\r\n", "");
184 + }
185 + break;
186 +
187 + case DATASOURCE_CSV_MARKDOWN:
188 + if(options & RRDR_OPTION_JSON_WRAP) {
189 + wb->contenttype = CT_APPLICATION_JSON;
190 + rrdr_json_wrapper_begin(r, wb, format, options, 1);
191 + rrdr2csv(r, wb, format, options, "", "|", "\\n", "");
192 + rrdr_json_wrapper_end(r, wb, format, options, 1);
193 + }
194 + else {
195 + wb->contenttype = CT_TEXT_PLAIN;
196 + rrdr2csv(r, wb, format, options, "", "|", "\r\n", "");
197 + }
198 + break;
199 +
200 + case DATASOURCE_CSV_JSON_ARRAY:
201 + wb->contenttype = CT_APPLICATION_JSON;
202 + if(options & RRDR_OPTION_JSON_WRAP) {
203 + rrdr_json_wrapper_begin(r, wb, format, options, 0);
204 + buffer_strcat(wb, "[\n");
205 + rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
206 + buffer_strcat(wb, "\n]");
207 + rrdr_json_wrapper_end(r, wb, format, options, 0);
208 + }
209 + else {
210 + wb->contenttype = CT_APPLICATION_JSON;
211 + buffer_strcat(wb, "[\n");
212 + rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
213 + buffer_strcat(wb, "\n]");
214 + }
215 + break;
216 +
217 + case DATASOURCE_TSV:
218 + if(options & RRDR_OPTION_JSON_WRAP) {
219 + wb->contenttype = CT_APPLICATION_JSON;
220 + rrdr_json_wrapper_begin(r, wb, format, options, 1);
221 + rrdr2csv(r, wb, format, options, "", "\t", "\\n", "");
222 + rrdr_json_wrapper_end(r, wb, format, options, 1);
223 + }
224 + else {
225 + wb->contenttype = CT_TEXT_PLAIN;
226 + rrdr2csv(r, wb, format, options, "", "\t", "\r\n", "");
227 + }
228 + break;
229 +
230 + case DATASOURCE_HTML:
231 + if(options & RRDR_OPTION_JSON_WRAP) {
232 + wb->contenttype = CT_APPLICATION_JSON;
233 + rrdr_json_wrapper_begin(r, wb, format, options, 1);
234 + buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
235 + rrdr2csv(r, wb, format, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
236 + buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
237 + rrdr_json_wrapper_end(r, wb, format, options, 1);
238 + }
239 + else {
240 + wb->contenttype = CT_TEXT_HTML;
241 + buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
242 + rrdr2csv(r, wb, format, options, "<tr><td>", "</td><td>", "</td></tr>\n", "");
243 + buffer_strcat(wb, "</table>\n</center>\n</html>\n");
244 + }
245 + break;
246 +
247 + case DATASOURCE_DATATABLE_JSONP:
248 + wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
249 +
250 + if(options & RRDR_OPTION_JSON_WRAP)
251 + rrdr_json_wrapper_begin(r, wb, format, options, 0);
252 +
253 + rrdr2json(r, wb, options, 1);
254 +
255 + if(options & RRDR_OPTION_JSON_WRAP)
256 + rrdr_json_wrapper_end(r, wb, format, options, 0);
257 + break;
258 +
259 + case DATASOURCE_DATATABLE_JSON:
260 + wb->contenttype = CT_APPLICATION_JSON;
261 +
262 + if(options & RRDR_OPTION_JSON_WRAP)
263 + rrdr_json_wrapper_begin(r, wb, format, options, 0);
264 +
265 + rrdr2json(r, wb, options, 1);
266 +
267 + if(options & RRDR_OPTION_JSON_WRAP)
268 + rrdr_json_wrapper_end(r, wb, format, options, 0);
269 + break;
270 +
271 + case DATASOURCE_JSONP:
272 + wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
273 + if(options & RRDR_OPTION_JSON_WRAP)
274 + rrdr_json_wrapper_begin(r, wb, format, options, 0);
275 +
276 + rrdr2json(r, wb, options, 0);
277 +
278 + if(options & RRDR_OPTION_JSON_WRAP)
279 + rrdr_json_wrapper_end(r, wb, format, options, 0);
280 + break;
281 +
282 + case DATASOURCE_JSON:
283 + default:
284 + wb->contenttype = CT_APPLICATION_JSON;
285 +
286 + if(options & RRDR_OPTION_JSON_WRAP)
287 + rrdr_json_wrapper_begin(r, wb, format, options, 0);
288 +
289 + rrdr2json(r, wb, options, 0);
290 +
291 + if(options & RRDR_OPTION_JSON_WRAP)
292 + rrdr_json_wrapper_end(r, wb, format, options, 0);
293 + break;
294 + }
295 +
296 + rrdr_free(r);
297 + return 200;
298 +}
web/api/formatters/rrd2json.h renamed
+15 -4
@@ -3,9 +3,18 @@
3 #ifndef NETDATA_RRD2JSON_H
4 #define NETDATA_RRD2JSON_H 1
5
6 -#include "web_api_v1.h"
7 -#include "exporters/allmetrics.h"
8 -#include "queries/rrdr.h"
6 +#include "web/api/web_api_v1.h"
7 +#include "web/api/exporters/allmetrics.h"
8 +#include "web/api/queries/rrdr.h"
9 +
10 +#include "web/api/formatters/csv/csv.h"
11 +#include "web/api/formatters/ssv/ssv.h"
12 +#include "web/api/formatters/json/json.h"
13 +#include "web/api/formatters/value/value.h"
14 +
15 +#include "web/api/formatters/rrdset2json.h"
16 +#include "web/api/formatters/charts2json.h"
17 +#include "web/api/formatters/json_wrapper.h"
18
19 #define HOSTNAME_MAX 1024
20
@@ -24,6 +33,7 @@
33 #define DATASOURCE_JS_ARRAY 8
34 #define DATASOURCE_SSV_COMMA 9
35 #define DATASOURCE_CSV_JSON_ARRAY 10
36 +#define DATASOURCE_CSV_MARKDOWN 11
37
38 #define DATASOURCE_FORMAT_JSON "json"
39 #define DATASOURCE_FORMAT_DATATABLE_JSON "datatable"
@@ -36,9 +46,10 @@
46 #define DATASOURCE_FORMAT_JS_ARRAY "array"
47 #define DATASOURCE_FORMAT_SSV_COMMA "ssvcomma"
48 #define DATASOURCE_FORMAT_CSV_JSON_ARRAY "csvjsonarray"
49 +#define DATASOURCE_FORMAT_CSV_MARKDOWN "markdown"
50
51 extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb);
41 -extern void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb);
52 +extern void rrdr_buffer_print_format(BUFFER *wb, uint32_t format);
53
54 extern int rrdset2anything_api_v1(
55 RRDSET *st
web/api/formatters/rrdset2json.c new
+108
@@ -0,0 +1,108 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "rrdset2json.h"
4 +
5 +// generate JSON for the /api/v1/chart API call
6 +
7 +void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used) {
8 + rrdset_rdlock(st);
9 +
10 + buffer_sprintf(wb,
11 + "\t\t{\n"
12 + "\t\t\t\"id\": \"%s\",\n"
13 + "\t\t\t\"name\": \"%s\",\n"
14 + "\t\t\t\"type\": \"%s\",\n"
15 + "\t\t\t\"family\": \"%s\",\n"
16 + "\t\t\t\"context\": \"%s\",\n"
17 + "\t\t\t\"title\": \"%s (%s)\",\n"
18 + "\t\t\t\"priority\": %ld,\n"
19 + "\t\t\t\"plugin\": \"%s\",\n"
20 + "\t\t\t\"module\": \"%s\",\n"
21 + "\t\t\t\"enabled\": %s,\n"
22 + "\t\t\t\"units\": \"%s\",\n"
23 + "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n"
24 + "\t\t\t\"chart_type\": \"%s\",\n"
25 + "\t\t\t\"duration\": %ld,\n"
26 + "\t\t\t\"first_entry\": %ld,\n"
27 + "\t\t\t\"last_entry\": %ld,\n"
28 + "\t\t\t\"update_every\": %d,\n"
29 + "\t\t\t\"dimensions\": {\n"
30 + , st->id
31 + , st->name
32 + , st->type
33 + , st->family
34 + , st->context
35 + , st->title, st->name
36 + , st->priority
37 + , st->plugin_name?st->plugin_name:""
38 + , st->module_name?st->module_name:""
39 + , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?"true":"false"
40 + , st->units
41 + , st->name
42 + , rrdset_type_name(st->chart_type)
43 + , st->entries * st->update_every
44 + , rrdset_first_entry_t(st)
45 + , rrdset_last_entry_t(st)
46 + , st->update_every
47 + );
48 +
49 + unsigned long memory = st->memsize;
50 +
51 + size_t dimensions = 0;
52 + RRDDIM *rd;
53 + rrddim_foreach_read(rd, st) {
54 + if(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) continue;
55 +
56 + memory += rd->memsize;
57 +
58 + buffer_sprintf(
59 + wb
60 + , "%s"
61 + "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
62 + , dimensions ? ",\n" : ""
63 + , rd->id
64 + , rd->name
65 + );
66 +
67 + dimensions++;
68 + }
69 +
70 + if(dimensions_count) *dimensions_count += dimensions;
71 + if(memory_used) *memory_used += memory;
72 +
73 + buffer_strcat(wb, "\n\t\t\t},\n\t\t\t\"green\": ");
74 + buffer_rrd_value(wb, st->green);
75 + buffer_strcat(wb, ",\n\t\t\t\"red\": ");
76 + buffer_rrd_value(wb, st->red);
77 +
78 + buffer_strcat(wb, ",\n\t\t\t\"alarms\": {\n");
79 + size_t alarms = 0;
80 + RRDCALC *rc;
81 + for(rc = st->alarms; rc ; rc = rc->rrdset_next) {
82 +
83 + buffer_sprintf(
84 + wb
85 + , "%s"
86 + "\t\t\t\t\"%s\": {\n"
87 + "\t\t\t\t\t\"id\": %u,\n"
88 + "\t\t\t\t\t\"status\": \"%s\",\n"
89 + "\t\t\t\t\t\"units\": \"%s\",\n"
90 + "\t\t\t\t\t\"update_every\": %d\n"
91 + "\t\t\t\t}"
92 + , (alarms) ? ",\n" : ""
93 + , rc->name
94 + , rc->id
95 + , rrdcalc_status2string(rc->status)
96 + , rc->units
97 + , rc->update_every
98 + );
99 +
100 + alarms++;
101 + }
102 +
103 + buffer_sprintf(wb,
104 + "\n\t\t\t}\n\t\t}"
105 + );
106 +
107 + rrdset_unlock(st);
108 +}
web/api/formatters/rrdset2json.h new
+10
@@ -0,0 +1,10 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_FORMATTER_RRDSET2JSON_H
4 +#define NETDATA_API_FORMATTER_RRDSET2JSON_H
5 +
6 +#include "rrd2json.h"
7 +
8 +extern void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used);
9 +
10 +#endif //NETDATA_API_FORMATTER_RRDSET2JSON_H
web/api/formatters/ssv/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/formatters/ssv/README.md new
+52
@@ -0,0 +1,52 @@
1 +# SSV formatter
2 +
3 +The SSV formatter sums all dimensions in [results of database queries](../../queries)
4 +to a single value and returns a list of such values showing how it changes through time.
5 +
6 +It supports the following formats:
7 +
8 +format|content type|description
9 +:---:|:---:|:-----
10 +`ssv`|text/plain|a space separated list of values
11 +`ssvcomma`|text/plain|a comma separated list of values
12 +`array`|application/json|a JSON array
13 +
14 +The CSV formatter respects the following API `&options=`:
15 +
16 +option|supported|description
17 +:---:|:---:|:---
18 +`nonzero`|yes|to return only the dimensions that have at least a non-zero value
19 +`flip`|yes|to return the numbers older to newer (the default is newer to older)
20 +`percent`|yes|to replace all values with their percentage over the row total
21 +`abs`|yes|to turn all values positive, before using them
22 +`min2max`|yes|to return the delta from the minimum value to the maximum value (across dimensions)
23 +
24 +## Examples
25 +
26 +Get the average system CPU utilization of the last hour, in 6 values (one every 10 minutes):
27 +
28 +```bash
29 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=system.cpu&format=ssv&after=-3600&points=6&group=average'
30 +1.741352 1.6800467 1.769411 1.6761112 1.629862 1.6807968
31 +```
32 +
33 +---
34 +
35 +Get the total mysql bandwidth (in + out) for the last hour, in 6 values (one every 10 minutes):
36 +
37 +Netdata returns bandwidth in `kilobits`.
38 +
39 +```bash
40 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=mysql_local.net&format=ssvcomma&after=-3600&points=6&group=sum&options=abs'
41 +72618.7936215,72618.778889,72618.788084,72618.9195918,72618.7760612,72618.6712421
42 +```
43 +
44 +---
45 +
46 +Get the web server max connections for the last hour, in 12 values (one every 5 minutes)
47 +in a JSON array:
48 +
49 +```bash
50 +# curl -Ss 'https://registry.my-netdata.io/api/v1/data?chart=nginx_local.connections&format=array&after=-3600&points=12&group=max'
51 +[278,258,268,239,259,260,243,266,278,318,264,258]
52 +```
web/api/formatters/ssv/ssv.c new
+45
@@ -0,0 +1,45 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "ssv.h"
4 +
5 +void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix) {
6 + //info("RRD2SSV(): %s: BEGIN", r->st->id);
7 + long i;
8 +
9 + buffer_strcat(wb, prefix);
10 + long start = 0, end = rrdr_rows(r), step = 1;
11 + if(!(options & RRDR_OPTION_REVERSED)) {
12 + start = rrdr_rows(r) - 1;
13 + end = -1;
14 + step = -1;
15 + }
16 +
17 + // for each line in the array
18 + for(i = start; i != end ;i += step) {
19 + int all_values_are_null = 0;
20 + calculated_number v = rrdr2value(r, i, options, &all_values_are_null);
21 +
22 + if(likely(i != start)) {
23 + if(r->min > v) r->min = v;
24 + if(r->max < v) r->max = v;
25 + }
26 + else {
27 + r->min = v;
28 + r->max = v;
29 + }
30 +
31 + if(likely(i != start))
32 + buffer_strcat(wb, separator);
33 +
34 + if(all_values_are_null) {
35 + if(options & RRDR_OPTION_NULL2ZERO)
36 + buffer_strcat(wb, "0");
37 + else
38 + buffer_strcat(wb, "null");
39 + }
40 + else
41 + buffer_rrd_value(wb, v);
42 + }
43 + buffer_strcat(wb, suffix);
44 + //info("RRD2SSV(): %s: END", r->st->id);
45 +}
web/api/formatters/ssv/ssv.h new
+10
@@ -0,0 +1,10 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_FORMATTER_SSV_H
4 +#define NETDATA_API_FORMATTER_SSV_H
5 +
6 +#include "../rrd2json.h"
7 +
8 +extern void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix);
9 +
10 +#endif //NETDATA_API_FORMATTER_SSV_H
web/api/formatters/value/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/formatters/value/README.md new
+17
@@ -0,0 +1,17 @@
1 +# Value formatter
2 +
3 +The Value formatter presents [results of database queries](../../queries) as a single value.
4 +
5 +To calculate the single value to be returned, it sums the values of all dimensions.
6 +
7 +The Value formatter respects the following API `&options=`:
8 +
9 +option|supported|description
10 +:---:|:---:|:---
11 +`percent`|yes|to replace all values with their percentage over the row total
12 +`abs`|yes|to turn all values positive, before using them
13 +`min2max`|yes|to return the delta from the minimum value to the maximum value (across dimensions)
14 +
15 +The Value formatter is not exposed by the API by itself.
16 +Instead it is used by the [`ssv`](../ssv) formatter
17 +and [health monitoring queries](../../../../health).
web/api/formatters/value/value.c new
+94
@@ -0,0 +1,94 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "value.h"
4 +
5 +
6 +inline calculated_number rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null) {
7 + rrdset_check_rdlock(r->st);
8 +
9 + long c;
10 + RRDDIM *d;
11 +
12 + calculated_number *cn = &r->v[ i * r->d ];
13 + RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
14 +
15 + calculated_number sum = 0, min = 0, max = 0, v;
16 + int all_null = 1, init = 1;
17 +
18 + calculated_number total = 1;
19 + int set_min_max = 0;
20 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
21 + total = 0;
22 + for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
23 + calculated_number n = cn[c];
24 +
25 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
26 + n = -n;
27 +
28 + total += n;
29 + }
30 + // prevent a division by zero
31 + if(total == 0) total = 1;
32 + set_min_max = 1;
33 + }
34 +
35 + // for each dimension
36 + for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
37 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
38 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
39 +
40 + calculated_number n = cn[c];
41 +
42 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
43 + n = -n;
44 +
45 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
46 + n = n * 100 / total;
47 +
48 + if(unlikely(set_min_max)) {
49 + r->min = r->max = n;
50 + set_min_max = 0;
51 + }
52 +
53 + if(n < r->min) r->min = n;
54 + if(n > r->max) r->max = n;
55 + }
56 +
57 + if(unlikely(init)) {
58 + if(n > 0) {
59 + min = 0;
60 + max = n;
61 + }
62 + else {
63 + min = n;
64 + max = 0;
65 + }
66 + init = 0;
67 + }
68 +
69 + if(likely(!(co[c] & RRDR_VALUE_EMPTY))) {
70 + all_null = 0;
71 + sum += n;
72 + }
73 +
74 + if(n < min) min = n;
75 + if(n > max) max = n;
76 + }
77 +
78 + if(unlikely(all_null)) {
79 + if(likely(all_values_are_null))
80 + *all_values_are_null = 1;
81 + return 0;
82 + }
83 + else {
84 + if(likely(all_values_are_null))
85 + *all_values_are_null = 0;
86 + }
87 +
88 + if(options & RRDR_OPTION_MIN2MAX)
89 + v = max - min;
90 + else
91 + v = sum;
92 +
93 + return v;
94 +}
web/api/formatters/value/value.h new
+10
@@ -0,0 +1,10 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_FORMATTER_VALUE_H
4 +#define NETDATA_API_FORMATTER_VALUE_H
5 +
6 +#include "../rrd2json.h"
7 +
8 +extern calculated_number rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null);
9 +
10 +#endif //NETDATA_API_FORMATTER_VALUE_H
web/api/netdata-swagger.json
+1
@@ -162,6 +162,7 @@
162 "datatable",
163 "datasource",
164 "html",
165 + "markdown",
166 "array",
167 "csvjsonarray"
168 ],
web/api/netdata-swagger.yaml
+1 -1
@@ -110,7 +110,7 @@ paths:
110 description: 'The format of the data to be returned.'
111 required: true
112 type: string
113 - enum: [ 'json', 'jsonp', 'csv', 'tsv', 'tsv-excel', 'ssv', 'ssvcomma', 'datatable', 'datasource', 'html', 'array', 'csvjsonarray' ]
113 + enum: [ 'json', 'jsonp', 'csv', 'tsv', 'tsv-excel', 'ssv', 'ssvcomma', 'datatable', 'datasource', 'html', 'markdown', 'array', 'csvjsonarray' ]
114 default: json
115 allowEmptyValue: false
116 - name: options
web/api/queries/query.c
+1 -1
@@ -1,7 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "query.h"
4 -#include "../rrd2json.h"
4 +#include "web/api/formatters/rrd2json.h"
5 #include "rrdr.h"
6
7 #include "average/average.h"
web/api/queries/rrdr.c
-488
@@ -58,496 +58,8 @@ static void rrdr_dump(RRDR *r)
58 }
59 */
60
61 -#define JSON_DATES_JS 1
62 -#define JSON_DATES_TIMESTAMP 2
61
64 -void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
65 - rrdset_check_rdlock(r->st);
62
67 - //info("RRD2JSON(): %s: BEGIN", r->st->id);
68 - int row_annotations = 0, dates, dates_with_new = 0;
69 - char kq[2] = "", // key quote
70 - sq[2] = "", // string quote
71 - pre_label[101] = "", // before each label
72 - post_label[101] = "", // after each label
73 - pre_date[101] = "", // the beginning of line, to the date
74 - post_date[101] = "", // closing the date
75 - pre_value[101] = "", // before each value
76 - post_value[101] = "", // after each value
77 - post_line[101] = "", // at the end of each row
78 - normal_annotation[201] = "", // default row annotation
79 - overflow_annotation[201] = "", // overflow row annotation
80 - data_begin[101] = "", // between labels and values
81 - finish[101] = ""; // at the end of everything
82 -
83 - if(datatable) {
84 - dates = JSON_DATES_JS;
85 - if( options & RRDR_OPTION_GOOGLE_JSON ) {
86 - kq[0] = '\0';
87 - sq[0] = '\'';
88 - }
89 - else {
90 - kq[0] = '"';
91 - sq[0] = '"';
92 - }
93 - row_annotations = 1;
94 - snprintfz(pre_date, 100, " {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
95 - snprintfz(post_date, 100, "%s}", sq);
96 - snprintfz(pre_label, 100, ",\n {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
97 - snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
98 - snprintfz(pre_value, 100, ",{%sv%s:", kq, kq);
99 - strcpy(post_value, "}");
100 - strcpy(post_line, "]}");
101 - snprintfz(data_begin, 100, "\n ],\n %srows%s:\n [\n", kq, kq);
102 - strcpy(finish, "\n ]\n}");
103 -
104 - snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
105 - snprintfz(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
106 -
107 - buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
108 - buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
109 - buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
110 - buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
111 -
112 - // remove the valueobjects flag
113 - // google wants its own keys
114 - if(options & RRDR_OPTION_OBJECTSROWS)
115 - options &= ~RRDR_OPTION_OBJECTSROWS;
116 - }
117 - else {
118 - kq[0] = '"';
119 - sq[0] = '"';
120 - if(options & RRDR_OPTION_GOOGLE_JSON) {
121 - dates = JSON_DATES_JS;
122 - dates_with_new = 1;
123 - }
124 - else {
125 - dates = JSON_DATES_TIMESTAMP;
126 - dates_with_new = 0;
127 - }
128 - if( options & RRDR_OPTION_OBJECTSROWS )
129 - strcpy(pre_date, " { ");
130 - else
131 - strcpy(pre_date, " [ ");
132 - strcpy(pre_label, ", \"");
133 - strcpy(post_label, "\"");
134 - strcpy(pre_value, ", ");
135 - if( options & RRDR_OPTION_OBJECTSROWS )
136 - strcpy(post_line, "}");
137 - else
138 - strcpy(post_line, "]");
139 - snprintfz(data_begin, 100, "],\n %sdata%s:\n [\n", kq, kq);
140 - strcpy(finish, "\n ]\n}");
141 -
142 - buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
143 - buffer_sprintf(wb, "%stime%s", sq, sq);
144 - }
145 -
146 - // -------------------------------------------------------------------------
147 - // print the JSON header
148 -
149 - long c, i;
150 - RRDDIM *rd;
151 -
152 - // print the header lines
153 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
154 - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
155 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
156 -
157 - buffer_strcat(wb, pre_label);
158 - buffer_strcat(wb, rd->name);
159 - buffer_strcat(wb, post_label);
160 - i++;
161 - }
162 - if(!i) {
163 - buffer_strcat(wb, pre_label);
164 - buffer_strcat(wb, "no data");
165 - buffer_strcat(wb, post_label);
166 - }
167 -
168 - // print the begin of row data
169 - buffer_strcat(wb, data_begin);
170 -
171 - // if all dimensions are hidden, print a null
172 - if(!i) {
173 - buffer_strcat(wb, finish);
174 - return;
175 - }
176 -
177 - long start = 0, end = rrdr_rows(r), step = 1;
178 - if(!(options & RRDR_OPTION_REVERSED)) {
179 - start = rrdr_rows(r) - 1;
180 - end = -1;
181 - step = -1;
182 - }
183 -
184 - // for each line in the array
185 - calculated_number total = 1;
186 - for(i = start; i != end ;i += step) {
187 - calculated_number *cn = &r->v[ i * r->d ];
188 - RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
189 -
190 - time_t now = r->t[i];
191 -
192 - if(dates == JSON_DATES_JS) {
193 - // generate the local date time
194 - struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
195 - if(!tm) { error("localtime_r() failed."); continue; }
196 -
197 - if(likely(i != start)) buffer_strcat(wb, ",\n");
198 - buffer_strcat(wb, pre_date);
199 -
200 - if( options & RRDR_OPTION_OBJECTSROWS )
201 - buffer_sprintf(wb, "%stime%s: ", kq, kq);
202 -
203 - if(dates_with_new)
204 - buffer_strcat(wb, "new ");
205 -
206 - buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
207 -
208 - buffer_strcat(wb, post_date);
209 -
210 - if(row_annotations) {
211 - // google supports one annotation per row
212 - int annotation_found = 0;
213 - for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
214 - if(co[c] & RRDR_VALUE_RESET) {
215 - buffer_strcat(wb, overflow_annotation);
216 - annotation_found = 1;
217 - break;
218 - }
219 - }
220 - if(!annotation_found)
221 - buffer_strcat(wb, normal_annotation);
222 - }
223 - }
224 - else {
225 - // print the timestamp of the line
226 - if(likely(i != start)) buffer_strcat(wb, ",\n");
227 - buffer_strcat(wb, pre_date);
228 -
229 - if( options & RRDR_OPTION_OBJECTSROWS )
230 - buffer_sprintf(wb, "%stime%s: ", kq, kq);
231 -
232 - buffer_rrd_value(wb, (calculated_number)r->t[i]);
233 - // in ms
234 - if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
235 -
236 - buffer_strcat(wb, post_date);
237 - }
238 -
239 - int set_min_max = 0;
240 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
241 - total = 0;
242 - for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
243 - calculated_number n = cn[c];
244 -
245 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
246 - n = -n;
247 -
248 - total += n;
249 - }
250 - // prevent a division by zero
251 - if(total == 0) total = 1;
252 - set_min_max = 1;
253 - }
254 -
255 - // for each dimension
256 - for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
257 - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
258 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
259 -
260 - calculated_number n = cn[c];
261 -
262 - buffer_strcat(wb, pre_value);
263 -
264 - if( options & RRDR_OPTION_OBJECTSROWS )
265 - buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
266 -
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 -
277 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
278 - n = n * 100 / total;
279 -
280 - if(unlikely(set_min_max)) {
281 - r->min = r->max = n;
282 - set_min_max = 0;
283 - }
284 -
285 - if(n < r->min) r->min = n;
286 - if(n > r->max) r->max = n;
287 - }
288 -
289 - buffer_rrd_value(wb, n);
290 - }
291 -
292 - buffer_strcat(wb, post_value);
293 - }
294 -
295 - buffer_strcat(wb, post_line);
296 - }
297 -
298 - buffer_strcat(wb, finish);
299 - //info("RRD2JSON(): %s: END", r->st->id);
300 -}
301 -
302 -void rrdr2csv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines) {
303 - rrdset_check_rdlock(r->st);
304 -
305 - //info("RRD2CSV(): %s: BEGIN", r->st->id);
306 - long c, i;
307 - RRDDIM *d;
308 -
309 - // print the csv header
310 - for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
311 - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
312 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
313 -
314 - if(!i) {
315 - buffer_strcat(wb, startline);
316 - if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
317 - buffer_strcat(wb, "time");
318 - if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
319 - }
320 - buffer_strcat(wb, separator);
321 - if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
322 - buffer_strcat(wb, d->name);
323 - if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
324 - i++;
325 - }
326 - buffer_strcat(wb, endline);
327 -
328 - if(!i) {
329 - // no dimensions present
330 - return;
331 - }
332 -
333 - long start = 0, end = rrdr_rows(r), step = 1;
334 - if(!(options & RRDR_OPTION_REVERSED)) {
335 - start = rrdr_rows(r) - 1;
336 - end = -1;
337 - step = -1;
338 - }
339 -
340 - // for each line in the array
341 - calculated_number total = 1;
342 - for(i = start; i != end ;i += step) {
343 - calculated_number *cn = &r->v[ i * r->d ];
344 - RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
345 -
346 - buffer_strcat(wb, betweenlines);
347 - buffer_strcat(wb, startline);
348 -
349 - time_t now = r->t[i];
350 -
351 - if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
352 - // print the timestamp of the line
353 - buffer_rrd_value(wb, (calculated_number)now);
354 - // in ms
355 - if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
356 - }
357 - else {
358 - // generate the local date time
359 - struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
360 - if(!tm) { error("localtime() failed."); continue; }
361 - buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
362 - }
363 -
364 - int set_min_max = 0;
365 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
366 - total = 0;
367 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
368 - calculated_number n = cn[c];
369 -
370 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
371 - n = -n;
372 -
373 - total += n;
374 - }
375 - // prevent a division by zero
376 - if(total == 0) total = 1;
377 - set_min_max = 1;
378 - }
379 -
380 - // for each dimension
381 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
382 - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
383 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
384 -
385 - buffer_strcat(wb, separator);
386 -
387 - calculated_number n = cn[c];
388 -
389 - if(co[c] & RRDR_VALUE_EMPTY) {
390 - if(options & RRDR_OPTION_NULL2ZERO)
391 - buffer_strcat(wb, "0");
392 - else
393 - buffer_strcat(wb, "null");
394 - }
395 - else {
396 - if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
397 - n = -n;
398 -
399 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
400 - n = n * 100 / total;
401 -
402 - if(unlikely(set_min_max)) {
403 - r->min = r->max = n;
404 - set_min_max = 0;
405 - }
406 -
407 - if(n < r->min) r->min = n;
408 - if(n > r->max) r->max = n;
409 - }
410 -
411 - buffer_rrd_value(wb, n);
412 - }
413 - }
414 -
415 - buffer_strcat(wb, endline);
416 - }
417 - //info("RRD2CSV(): %s: END", r->st->id);
418 -}
419 -
420 -inline calculated_number rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null) {
421 - rrdset_check_rdlock(r->st);
422 -
423 - long c;
424 - RRDDIM *d;
425 -
426 - calculated_number *cn = &r->v[ i * r->d ];
427 - RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
428 -
429 - calculated_number sum = 0, min = 0, max = 0, v;
430 - int all_null = 1, init = 1;
431 -
432 - calculated_number total = 1;
433 - int set_min_max = 0;
434 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
435 - total = 0;
436 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
437 - calculated_number n = cn[c];
438 -
439 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
440 - n = -n;
441 -
442 - total += n;
443 - }
444 - // prevent a division by zero
445 - if(total == 0) total = 1;
446 - set_min_max = 1;
447 - }
448 -
449 - // for each dimension
450 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
451 - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
452 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
453 -
454 - calculated_number n = cn[c];
455 -
456 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
457 - n = -n;
458 -
459 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
460 - n = n * 100 / total;
461 -
462 - if(unlikely(set_min_max)) {
463 - r->min = r->max = n;
464 - set_min_max = 0;
465 - }
466 -
467 - if(n < r->min) r->min = n;
468 - if(n > r->max) r->max = n;
469 - }
470 -
471 - if(unlikely(init)) {
472 - if(n > 0) {
473 - min = 0;
474 - max = n;
475 - }
476 - else {
477 - min = n;
478 - max = 0;
479 - }
480 - init = 0;
481 - }
482 -
483 - if(likely(!(co[c] & RRDR_VALUE_EMPTY))) {
484 - all_null = 0;
485 - sum += n;
486 - }
487 -
488 - if(n < min) min = n;
489 - if(n > max) max = n;
490 - }
491 -
492 - if(unlikely(all_null)) {
493 - if(likely(all_values_are_null))
494 - *all_values_are_null = 1;
495 - return 0;
496 - }
497 - else {
498 - if(likely(all_values_are_null))
499 - *all_values_are_null = 0;
500 - }
501 -
502 - if(options & RRDR_OPTION_MIN2MAX)
503 - v = max - min;
504 - else
505 - v = sum;
506 -
507 - return v;
508 -}
509 -
510 -void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix) {
511 - //info("RRD2SSV(): %s: BEGIN", r->st->id);
512 - long i;
513 -
514 - buffer_strcat(wb, prefix);
515 - long start = 0, end = rrdr_rows(r), step = 1;
516 - if(!(options & RRDR_OPTION_REVERSED)) {
517 - start = rrdr_rows(r) - 1;
518 - end = -1;
519 - step = -1;
520 - }
521 -
522 - // for each line in the array
523 - for(i = start; i != end ;i += step) {
524 - int all_values_are_null = 0;
525 - calculated_number v = rrdr2value(r, i, options, &all_values_are_null);
526 -
527 - if(likely(i != start)) {
528 - if(r->min > v) r->min = v;
529 - if(r->max < v) r->max = v;
530 - }
531 - else {
532 - r->min = v;
533 - r->max = v;
534 - }
535 -
536 - if(likely(i != start))
537 - buffer_strcat(wb, separator);
538 -
539 - if(all_values_are_null) {
540 - if(options & RRDR_OPTION_NULL2ZERO)
541 - buffer_strcat(wb, "0");
542 - else
543 - buffer_strcat(wb, "null");
544 - }
545 - else
546 - buffer_rrd_value(wb, v);
547 - }
548 - buffer_strcat(wb, suffix);
549 - //info("RRD2SSV(): %s: END", r->st->id);
550 -}
63
64 inline static void rrdr_lock_rrdset(RRDR *r) {
65 if(unlikely(!r)) {
web/api/queries/rrdr.h
+3 -9
@@ -4,7 +4,6 @@
4 #define NETDATA_QUERIES_RRDR_H
5
6 #include "libnetdata/libnetdata.h"
7 -#include "../web_api_v1.h"
7
8 typedef enum rrdr_options {
9 RRDR_OPTION_NONZERO = 0x00000001, // don't output dimensions will just zero values
@@ -44,7 +43,7 @@ typedef enum rrdr_result_flags {
43 } RRDR_RESULT_FLAGS;
44
45 typedef struct rrdresult {
47 - RRDSET *st; // the chart this result refers to
46 + struct rrdset *st; // the chart this result refers to
47
48 RRDR_RESULT_FLAGS result_options; // RRDR_RESULT_OPTION_*
49
@@ -93,15 +92,10 @@ typedef struct rrdresult {
92
93 #define rrdr_rows(r) ((r)->rows)
94
96 -// formatters
97 -extern void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable);
98 -extern void rrdr2csv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines);
99 -extern calculated_number rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null);
100 -extern void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix);
101 -
95 extern void rrdr_free(RRDR *r);
103 -extern RRDR *rrdr_create(RRDSET *st, long n);
96 +extern RRDR *rrdr_create(struct rrdset *st, long n);
97
98 +#include "../web_api_v1.h"
99 #include "web/api/queries/query.h"
100
101 extern RRDR *rrd2rrdr(RRDSET *st, long points_requested, long long after_requested, long long before_requested, RRDR_GROUPING group_method, long resampling_time_requested, RRDR_OPTIONS options, const char *dimensions);
web/api/rrd2json.c deleted
-695
@@ -1,695 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "web_api_v1.h"
4 -
5 -void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used)
6 -{
7 - rrdset_rdlock(st);
8 -
9 - buffer_sprintf(wb,
10 - "\t\t{\n"
11 - "\t\t\t\"id\": \"%s\",\n"
12 - "\t\t\t\"name\": \"%s\",\n"
13 - "\t\t\t\"type\": \"%s\",\n"
14 - "\t\t\t\"family\": \"%s\",\n"
15 - "\t\t\t\"context\": \"%s\",\n"
16 - "\t\t\t\"title\": \"%s (%s)\",\n"
17 - "\t\t\t\"priority\": %ld,\n"
18 - "\t\t\t\"plugin\": \"%s\",\n"
19 - "\t\t\t\"module\": \"%s\",\n"
20 - "\t\t\t\"enabled\": %s,\n"
21 - "\t\t\t\"units\": \"%s\",\n"
22 - "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n"
23 - "\t\t\t\"chart_type\": \"%s\",\n"
24 - "\t\t\t\"duration\": %ld,\n"
25 - "\t\t\t\"first_entry\": %ld,\n"
26 - "\t\t\t\"last_entry\": %ld,\n"
27 - "\t\t\t\"update_every\": %d,\n"
28 - "\t\t\t\"dimensions\": {\n"
29 - , st->id
30 - , st->name
31 - , st->type
32 - , st->family
33 - , st->context
34 - , st->title, st->name
35 - , st->priority
36 - , st->plugin_name?st->plugin_name:""
37 - , st->module_name?st->module_name:""
38 - , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?"true":"false"
39 - , st->units
40 - , st->name
41 - , rrdset_type_name(st->chart_type)
42 - , st->entries * st->update_every
43 - , rrdset_first_entry_t(st)
44 - , rrdset_last_entry_t(st)
45 - , st->update_every
46 - );
47 -
48 - unsigned long memory = st->memsize;
49 -
50 - size_t dimensions = 0;
51 - RRDDIM *rd;
52 - rrddim_foreach_read(rd, st) {
53 - if(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) continue;
54 -
55 - memory += rd->memsize;
56 -
57 - buffer_sprintf(
58 - wb
59 - , "%s"
60 - "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
61 - , dimensions ? ",\n" : ""
62 - , rd->id
63 - , rd->name
64 - );
65 -
66 - dimensions++;
67 - }
68 -
69 - if(dimensions_count) *dimensions_count += dimensions;
70 - if(memory_used) *memory_used += memory;
71 -
72 - buffer_strcat(wb, "\n\t\t\t},\n\t\t\t\"green\": ");
73 - buffer_rrd_value(wb, st->green);
74 - buffer_strcat(wb, ",\n\t\t\t\"red\": ");
75 - buffer_rrd_value(wb, st->red);
76 -
77 - buffer_strcat(wb, ",\n\t\t\t\"alarms\": {\n");
78 - size_t alarms = 0;
79 - RRDCALC *rc;
80 - for(rc = st->alarms; rc ; rc = rc->rrdset_next) {
81 -
82 - buffer_sprintf(
83 - wb
84 - , "%s"
85 - "\t\t\t\t\"%s\": {\n"
86 - "\t\t\t\t\t\"id\": %u,\n"
87 - "\t\t\t\t\t\"status\": \"%s\",\n"
88 - "\t\t\t\t\t\"units\": \"%s\",\n"
89 - "\t\t\t\t\t\"update_every\": %d\n"
90 - "\t\t\t\t}"
91 - , (alarms) ? ",\n" : ""
92 - , rc->name
93 - , rc->id
94 - , rrdcalc_status2string(rc->status)
95 - , rc->units
96 - , rc->update_every
97 - );
98 -
99 - alarms++;
100 - }
101 -
102 - buffer_sprintf(wb,
103 - "\n\t\t\t}\n\t\t}"
104 - );
105 -
106 - rrdset_unlock(st);
107 -}
108 -
109 -void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) {
110 - rrd_stats_api_v1_chart_with_data(st, wb, NULL, NULL);
111 -}
112 -
113 -void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb) {
114 - static char *custom_dashboard_info_js_filename = NULL;
115 - size_t c, dimensions = 0, memory = 0, alarms = 0;
116 - RRDSET *st;
117 -
118 - time_t now = now_realtime_sec();
119 -
120 - if(unlikely(!custom_dashboard_info_js_filename))
121 - custom_dashboard_info_js_filename = config_get(CONFIG_SECTION_WEB, "custom dashboard_info.js", "");
122 -
123 - buffer_sprintf(wb, "{\n"
124 - "\t\"hostname\": \"%s\""
125 - ",\n\t\"version\": \"%s\""
126 - ",\n\t\"os\": \"%s\""
127 - ",\n\t\"timezone\": \"%s\""
128 - ",\n\t\"update_every\": %d"
129 - ",\n\t\"history\": %ld"
130 - ",\n\t\"custom_info\": \"%s\""
131 - ",\n\t\"charts\": {"
132 - , host->hostname
133 - , host->program_version
134 - , host->os
135 - , host->timezone
136 - , host->rrd_update_every
137 - , host->rrd_history_entries
138 - , custom_dashboard_info_js_filename
139 - );
140 -
141 - c = 0;
142 - rrdhost_rdlock(host);
143 - rrdset_foreach_read(st, host) {
144 - if(rrdset_is_available_for_viewers(st)) {
145 - if(c) buffer_strcat(wb, ",");
146 - buffer_strcat(wb, "\n\t\t\"");
147 - buffer_strcat(wb, st->id);
148 - buffer_strcat(wb, "\": ");
149 - rrd_stats_api_v1_chart_with_data(st, wb, &dimensions, &memory);
150 -
151 - c++;
152 - st->last_accessed_time = now;
153 - }
154 - }
155 -
156 - RRDCALC *rc;
157 - for(rc = host->alarms; rc ; rc = rc->next) {
158 - if(rc->rrdset)
159 - alarms++;
160 - }
161 - rrdhost_unlock(host);
162 -
163 - buffer_sprintf(wb
164 - , "\n\t}"
165 - ",\n\t\"charts_count\": %zu"
166 - ",\n\t\"dimensions_count\": %zu"
167 - ",\n\t\"alarms_count\": %zu"
168 - ",\n\t\"rrd_memory_bytes\": %zu"
169 - ",\n\t\"hosts_count\": %zu"
170 - ",\n\t\"hosts\": ["
171 - , c
172 - , dimensions
173 - , alarms
174 - , memory
175 - , rrd_hosts_available
176 - );
177 -
178 - if(unlikely(rrd_hosts_available > 1)) {
179 - rrd_rdlock();
180 -
181 - size_t found = 0;
182 - RRDHOST *h;
183 - rrdhost_foreach_read(h) {
184 - if(!rrdhost_should_be_removed(h, host, now)) {
185 - buffer_sprintf(wb
186 - , "%s\n\t\t{"
187 - "\n\t\t\t\"hostname\": \"%s\""
188 - "\n\t\t}"
189 - , (found > 0) ? "," : ""
190 - , h->hostname
191 - );
192 -
193 - found++;
194 - }
195 - }
196 -
197 - rrd_unlock();
198 - }
199 - else {
200 - buffer_sprintf(wb
201 - , "\n\t\t{"
202 - "\n\t\t\t\"hostname\": \"%s\""
203 - "\n\t\t}"
204 - , host->hostname
205 - );
206 - }
207 -
208 - buffer_sprintf(wb, "\n\t]\n}\n");
209 -}
210 -
211 -
212 -static void rrdr_buffer_print_format(BUFFER *wb, uint32_t format) {
213 - switch(format) {
214 - case DATASOURCE_JSON:
215 - buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
216 - break;
217 -
218 - case DATASOURCE_DATATABLE_JSON:
219 - buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
220 - break;
221 -
222 - case DATASOURCE_DATATABLE_JSONP:
223 - buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
224 - break;
225 -
226 - case DATASOURCE_JSONP:
227 - buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
228 - break;
229 -
230 - case DATASOURCE_SSV:
231 - buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
232 - break;
233 -
234 - case DATASOURCE_CSV:
235 - buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
236 - break;
237 -
238 - case DATASOURCE_TSV:
239 - buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
240 - break;
241 -
242 - case DATASOURCE_HTML:
243 - buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
244 - break;
245 -
246 - case DATASOURCE_JS_ARRAY:
247 - buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
248 - break;
249 -
250 - case DATASOURCE_SSV_COMMA:
251 - buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
252 - break;
253 -
254 - default:
255 - buffer_strcat(wb, "unknown");
256 - break;
257 - }
258 -}
259 -
260 -static void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value) {
261 - rrdset_check_rdlock(r->st);
262 -
263 - long rows = rrdr_rows(r);
264 - long c, i;
265 - RRDDIM *rd;
266 -
267 - //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
268 - char kq[2] = "", // key quote
269 - sq[2] = ""; // string quote
270 -
271 - if( options & RRDR_OPTION_GOOGLE_JSON ) {
272 - kq[0] = '\0';
273 - sq[0] = '\'';
274 - }
275 - else {
276 - kq[0] = '"';
277 - sq[0] = '"';
278 - }
279 -
280 - buffer_sprintf(wb, "{\n"
281 - " %sapi%s: 1,\n"
282 - " %sid%s: %s%s%s,\n"
283 - " %sname%s: %s%s%s,\n"
284 - " %sview_update_every%s: %d,\n"
285 - " %supdate_every%s: %d,\n"
286 - " %sfirst_entry%s: %u,\n"
287 - " %slast_entry%s: %u,\n"
288 - " %sbefore%s: %u,\n"
289 - " %safter%s: %u,\n"
290 - " %sdimension_names%s: ["
291 - , kq, kq
292 - , kq, kq, sq, r->st->id, sq
293 - , kq, kq, sq, r->st->name, sq
294 - , kq, kq, r->update_every
295 - , kq, kq, r->st->update_every
296 - , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
297 - , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
298 - , kq, kq, (uint32_t)r->before
299 - , kq, kq, (uint32_t)r->after
300 - , kq, kq);
301 -
302 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
303 - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
304 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
305 -
306 - if(i) buffer_strcat(wb, ", ");
307 - buffer_strcat(wb, sq);
308 - buffer_strcat(wb, rd->name);
309 - buffer_strcat(wb, sq);
310 - i++;
311 - }
312 - if(!i) {
313 -#ifdef NETDATA_INTERNAL_CHECKS
314 - error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
315 -#endif
316 - rows = 0;
317 - buffer_strcat(wb, sq);
318 - buffer_strcat(wb, "no data");
319 - buffer_strcat(wb, sq);
320 - }
321 -
322 - buffer_sprintf(wb, "],\n"
323 - " %sdimension_ids%s: ["
324 - , kq, kq);
325 -
326 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
327 - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
328 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
329 -
330 - if(i) buffer_strcat(wb, ", ");
331 - buffer_strcat(wb, sq);
332 - buffer_strcat(wb, rd->id);
333 - buffer_strcat(wb, sq);
334 - i++;
335 - }
336 - if(!i) {
337 - rows = 0;
338 - buffer_strcat(wb, sq);
339 - buffer_strcat(wb, "no data");
340 - buffer_strcat(wb, sq);
341 - }
342 -
343 - buffer_sprintf(wb, "],\n"
344 - " %slatest_values%s: ["
345 - , kq, kq);
346 -
347 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
348 - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
349 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
350 -
351 - if(i) buffer_strcat(wb, ", ");
352 - i++;
353 -
354 - storage_number n = rd->values[rrdset_last_slot(r->st)];
355 -
356 - if(!does_storage_number_exist(n))
357 - buffer_strcat(wb, "null");
358 - else
359 - buffer_rrd_value(wb, unpack_storage_number(n));
360 - }
361 - if(!i) {
362 - rows = 0;
363 - buffer_strcat(wb, "null");
364 - }
365 -
366 - buffer_sprintf(wb, "],\n"
367 - " %sview_latest_values%s: ["
368 - , kq, kq);
369 -
370 - i = 0;
371 - if(rows) {
372 - calculated_number total = 1;
373 -
374 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
375 - total = 0;
376 - for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
377 - calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
378 - calculated_number n = cn[c];
379 -
380 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
381 - n = -n;
382 -
383 - total += n;
384 - }
385 - // prevent a division by zero
386 - if(total == 0) total = 1;
387 - }
388 -
389 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
390 - if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
391 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
392 -
393 - if(i) buffer_strcat(wb, ", ");
394 - i++;
395 -
396 - calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
397 - RRDR_VALUE_FLAGS *co = &r->o[ (rrdr_rows(r) - 1) * r->d ];
398 - calculated_number n = cn[c];
399 -
400 - if(co[c] & RRDR_VALUE_EMPTY) {
401 - if(options & RRDR_OPTION_NULL2ZERO)
402 - buffer_strcat(wb, "0");
403 - else
404 - buffer_strcat(wb, "null");
405 - }
406 - else {
407 - if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
408 - n = -n;
409 -
410 - if(unlikely(options & RRDR_OPTION_PERCENTAGE))
411 - n = n * 100 / total;
412 -
413 - buffer_rrd_value(wb, n);
414 - }
415 - }
416 - }
417 - if(!i) {
418 - rows = 0;
419 - buffer_strcat(wb, "null");
420 - }
421 -
422 - buffer_sprintf(wb, "],\n"
423 - " %sdimensions%s: %ld,\n"
424 - " %spoints%s: %ld,\n"
425 - " %sformat%s: %s"
426 - , kq, kq, i
427 - , kq, kq, rows
428 - , kq, kq, sq
429 - );
430 -
431 - rrdr_buffer_print_format(wb, format);
432 -
433 - buffer_sprintf(wb, "%s,\n"
434 - " %sresult%s: "
435 - , sq
436 - , kq, kq
437 - );
438 -
439 - if(string_value) buffer_strcat(wb, sq);
440 - //info("JSONWRAPPER(): %s: END", r->st->id);
441 -}
442 -
443 -static void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value) {
444 - (void)format;
445 -
446 - char kq[2] = "", // key quote
447 - sq[2] = ""; // string quote
448 -
449 - if( options & RRDR_OPTION_GOOGLE_JSON ) {
450 - kq[0] = '\0';
451 - sq[0] = '\'';
452 - }
453 - else {
454 - kq[0] = '"';
455 - sq[0] = '"';
456 - }
457 -
458 - if(string_value) buffer_strcat(wb, sq);
459 -
460 - buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
461 - buffer_rrd_value(wb, r->min);
462 - buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
463 - buffer_rrd_value(wb, r->max);
464 - buffer_strcat(wb, "\n}\n");
465 -}
466 -
467 -int rrdset2value_api_v1(
468 - RRDSET *st
469 - , BUFFER *wb
470 - , calculated_number *n
471 - , const char *dimensions
472 - , long points
473 - , long long after
474 - , long long before
475 - , int group_method
476 - , long group_time
477 - , uint32_t options
478 - , time_t *db_after
479 - , time_t *db_before
480 - , int *value_is_null
481 -) {
482 - RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions);
483 - if(!r) {
484 - if(value_is_null) *value_is_null = 1;
485 - return 500;
486 - }
487 -
488 - if(rrdr_rows(r) == 0) {
489 - rrdr_free(r);
490 -
491 - if(db_after) *db_after = 0;
492 - if(db_before) *db_before = 0;
493 - if(value_is_null) *value_is_null = 1;
494 -
495 - return 400;
496 - }
497 -
498 - if(wb) {
499 - if (r->result_options & RRDR_RESULT_OPTION_RELATIVE)
500 - buffer_no_cacheable(wb);
501 - else if (r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
502 - buffer_cacheable(wb);
503 - }
504 -
505 - if(db_after) *db_after = r->after;
506 - if(db_before) *db_before = r->before;
507 -
508 - long i = (!(options & RRDR_OPTION_REVERSED))?rrdr_rows(r) - 1:0;
509 - *n = rrdr2value(r, i, options, value_is_null);
510 -
511 - rrdr_free(r);
512 - return 200;
513 -}
514 -
515 -int rrdset2anything_api_v1(
516 - RRDSET *st
517 - , BUFFER *wb
518 - , BUFFER *dimensions
519 - , uint32_t format
520 - , long points
521 - , long long after
522 - , long long before
523 - , int group_method
524 - , long group_time
525 - , uint32_t options
526 - , time_t *latest_timestamp
527 -) {
528 - st->last_accessed_time = now_realtime_sec();
529 -
530 - RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions?buffer_tostring(dimensions):NULL);
531 - if(!r) {
532 - buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
533 - return 500;
534 - }
535 -
536 - if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
537 - buffer_no_cacheable(wb);
538 - else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
539 - buffer_cacheable(wb);
540 -
541 - if(latest_timestamp && rrdr_rows(r) > 0)
542 - *latest_timestamp = r->before;
543 -
544 - switch(format) {
545 - case DATASOURCE_SSV:
546 - if(options & RRDR_OPTION_JSON_WRAP) {
547 - wb->contenttype = CT_APPLICATION_JSON;
548 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
549 - rrdr2ssv(r, wb, options, "", " ", "");
550 - rrdr_json_wrapper_end(r, wb, format, options, 1);
551 - }
552 - else {
553 - wb->contenttype = CT_TEXT_PLAIN;
554 - rrdr2ssv(r, wb, options, "", " ", "");
555 - }
556 - break;
557 -
558 - case DATASOURCE_SSV_COMMA:
559 - if(options & RRDR_OPTION_JSON_WRAP) {
560 - wb->contenttype = CT_APPLICATION_JSON;
561 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
562 - rrdr2ssv(r, wb, options, "", ",", "");
563 - rrdr_json_wrapper_end(r, wb, format, options, 1);
564 - }
565 - else {
566 - wb->contenttype = CT_TEXT_PLAIN;
567 - rrdr2ssv(r, wb, options, "", ",", "");
568 - }
569 - break;
570 -
571 - case DATASOURCE_JS_ARRAY:
572 - if(options & RRDR_OPTION_JSON_WRAP) {
573 - wb->contenttype = CT_APPLICATION_JSON;
574 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
575 - rrdr2ssv(r, wb, options, "[", ",", "]");
576 - rrdr_json_wrapper_end(r, wb, format, options, 0);
577 - }
578 - else {
579 - wb->contenttype = CT_APPLICATION_JSON;
580 - rrdr2ssv(r, wb, options, "[", ",", "]");
581 - }
582 - break;
583 -
584 - case DATASOURCE_CSV:
585 - if(options & RRDR_OPTION_JSON_WRAP) {
586 - wb->contenttype = CT_APPLICATION_JSON;
587 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
588 - rrdr2csv(r, wb, options, "", ",", "\\n", "");
589 - rrdr_json_wrapper_end(r, wb, format, options, 1);
590 - }
591 - else {
592 - wb->contenttype = CT_TEXT_PLAIN;
593 - rrdr2csv(r, wb, options, "", ",", "\r\n", "");
594 - }
595 - break;
596 -
597 - case DATASOURCE_CSV_JSON_ARRAY:
598 - wb->contenttype = CT_APPLICATION_JSON;
599 - if(options & RRDR_OPTION_JSON_WRAP) {
600 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
601 - buffer_strcat(wb, "[\n");
602 - rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
603 - buffer_strcat(wb, "\n]");
604 - rrdr_json_wrapper_end(r, wb, format, options, 0);
605 - }
606 - else {
607 - wb->contenttype = CT_TEXT_PLAIN;
608 - buffer_strcat(wb, "[\n");
609 - rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
610 - buffer_strcat(wb, "\n]");
611 - }
612 - break;
613 -
614 - case DATASOURCE_TSV:
615 - if(options & RRDR_OPTION_JSON_WRAP) {
616 - wb->contenttype = CT_APPLICATION_JSON;
617 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
618 - rrdr2csv(r, wb, options, "", "\t", "\\n", "");
619 - rrdr_json_wrapper_end(r, wb, format, options, 1);
620 - }
621 - else {
622 - wb->contenttype = CT_TEXT_PLAIN;
623 - rrdr2csv(r, wb, options, "", "\t", "\r\n", "");
624 - }
625 - break;
626 -
627 - case DATASOURCE_HTML:
628 - if(options & RRDR_OPTION_JSON_WRAP) {
629 - wb->contenttype = CT_APPLICATION_JSON;
630 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
631 - buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
632 - rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
633 - buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
634 - rrdr_json_wrapper_end(r, wb, format, options, 1);
635 - }
636 - else {
637 - wb->contenttype = CT_TEXT_HTML;
638 - buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
639 - rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\n", "");
640 - buffer_strcat(wb, "</table>\n</center>\n</html>\n");
641 - }
642 - break;
643 -
644 - case DATASOURCE_DATATABLE_JSONP:
645 - wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
646 -
647 - if(options & RRDR_OPTION_JSON_WRAP)
648 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
649 -
650 - rrdr2json(r, wb, options, 1);
651 -
652 - if(options & RRDR_OPTION_JSON_WRAP)
653 - rrdr_json_wrapper_end(r, wb, format, options, 0);
654 - break;
655 -
656 - case DATASOURCE_DATATABLE_JSON:
657 - wb->contenttype = CT_APPLICATION_JSON;
658 -
659 - if(options & RRDR_OPTION_JSON_WRAP)
660 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
661 -
662 - rrdr2json(r, wb, options, 1);
663 -
664 - if(options & RRDR_OPTION_JSON_WRAP)
665 - rrdr_json_wrapper_end(r, wb, format, options, 0);
666 - break;
667 -
668 - case DATASOURCE_JSONP:
669 - wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
670 - if(options & RRDR_OPTION_JSON_WRAP)
671 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
672 -
673 - rrdr2json(r, wb, options, 0);
674 -
675 - if(options & RRDR_OPTION_JSON_WRAP)
676 - rrdr_json_wrapper_end(r, wb, format, options, 0);
677 - break;
678 -
679 - case DATASOURCE_JSON:
680 - default:
681 - wb->contenttype = CT_APPLICATION_JSON;
682 -
683 - if(options & RRDR_OPTION_JSON_WRAP)
684 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
685 -
686 - rrdr2json(r, wb, options, 0);
687 -
688 - if(options & RRDR_OPTION_JSON_WRAP)
689 - rrdr_json_wrapper_end(r, wb, format, options, 0);
690 - break;
691 - }
692 -
693 - rrdr_free(r);
694 - return 200;
695 -}
web/api/web_api_v1.c
+2 -1
@@ -52,6 +52,7 @@ static struct {
52 , {DATASOURCE_FORMAT_JS_ARRAY , 0 , DATASOURCE_JS_ARRAY}
53 , {DATASOURCE_FORMAT_SSV_COMMA , 0 , DATASOURCE_SSV_COMMA}
54 , {DATASOURCE_FORMAT_CSV_JSON_ARRAY , 0 , DATASOURCE_CSV_JSON_ARRAY}
55 + , {DATASOURCE_FORMAT_CSV_MARKDOWN , 0 , DATASOURCE_CSV_MARKDOWN}
56 , { NULL, 0, 0}
57 };
58
@@ -224,7 +225,7 @@ inline int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w,
225
226 buffer_flush(w->response.data);
227 w->response.data->contenttype = CT_APPLICATION_JSON;
227 - rrd_stats_api_v1_charts(host, w->response.data);
228 + charts2json(host, w->response.data);
229 return 200;
230 }
231
web/api/web_api_v1.h
+1 -1
@@ -5,7 +5,7 @@
5
6 #include "daemon/common.h"
7 #include "web/api/badges/web_buffer_svg.h"
8 -#include "rrd2json.h"
8 +#include "web/api/formatters/rrd2json.h"
9
10 extern uint32_t web_client_api_request_v1_data_options(char *o);
11 extern uint32_t web_client_api_request_v1_data_format(char *name);