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
+
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);