added support for /nonzero option to chart, which shows only dimensions which have data; this still needs some work on the javascript to be fully supported
Costa Tsaousis (ktsaou) committed
May 14, 2014 at 01:23 UTC
9fde950a89dea7edb6faf8a4839dfbb20678f87b
1 file changed
+161
-119
netdata.c
+161
-119
@@ -1769,6 +1769,7 @@ struct web_buffer {
1769
};
1770
1771
#define web_buffer_printf(wb, args...) wb->bytes += snprintf(&wb->buffer[wb->bytes], (wb->size - wb->bytes), ##args)
1772
+#define web_buffer_reset(wb) wb->buffer[wb->bytes = 0] = '\0'
1773
1774
void web_buffer_strcpy(struct web_buffer *wb, const char *txt)
1775
{
@@ -2168,6 +2169,7 @@ void rrd_stats_all_json(struct web_buffer *wb)
2169
2170
web_buffer_printf(wb, RRD_GRAPH_JSON_HEADER);
2171
2172
+ pthread_rwlock_rdlock(&root_rwlock);
2173
for(st = root, c = 0; st ; st = st->next) {
2174
if(st->enabled) {
2175
if(c) web_buffer_printf(wb, "%s", ",\n");
@@ -2175,6 +2177,7 @@ void rrd_stats_all_json(struct web_buffer *wb)
2177
c++;
2178
}
2179
}
2180
+ pthread_rwlock_unlock(&root_rwlock);
2181
2182
web_buffer_printf(wb, "\n\t],\n"
2183
"\t\"hostname\": \"%s\",\n"
@@ -2189,7 +2192,7 @@ void rrd_stats_all_json(struct web_buffer *wb)
2192
);
2193
}
2194
2192
-unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, int entries_to_show, int group, int group_method, time_t after, time_t before)
2195
+unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, int entries_to_show, int group, int group_method, time_t after, time_t before, int only_non_zero)
2196
{
2197
int c;
2198
pthread_rwlock_rdlock(&st->rwlock);
@@ -2230,6 +2233,12 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, int
2233
if(before == 0) before = st->last_updated.tv_sec;
2234
if(after == 0) after = rrd_stats_first_entry_t(st);
2235
2236
+ // ---
2237
+
2238
+ // our return value (the last timestamp printed)
2239
+ // this is required to detect re-transmit in google JSONP
2240
+ time_t last_timestamp = 0;
2241
+
2242
2243
// -------------------------------------------------------------------------
2244
// find how many dimensions we have
@@ -2244,24 +2253,6 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, int
2253
}
2254
2255
2247
- // -------------------------------------------------------------------------
2248
- // print the JSON header
2249
-
2250
- web_buffer_printf(wb, "{\n %scols%s:\n [\n", kq, kq);
2251
- web_buffer_printf(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);
2252
- web_buffer_printf(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);
2253
- web_buffer_printf(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);
2254
-
2255
- // print the header for each dimension
2256
- // and update the print_hidden array for the dimensions that should be hidden
2257
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
2258
- if(!rd->hidden) web_buffer_printf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq);
2259
- }
2260
-
2261
- // print the begin of row data
2262
- web_buffer_printf(wb, "\n ],\n %srows%s:\n [\n", kq, kq);
2263
-
2264
-
2256
// -------------------------------------------------------------------------
2257
// prepare various strings, to speed up the loop
2258
@@ -2304,139 +2295,184 @@ unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, int
2295
calculated_number group_values[dimensions]; // keep sums when grouping
2296
storage_number print_values[dimensions]; // keep the final value to be printed
2297
int print_hidden[dimensions]; // keep hidden flags
2298
+ int found_non_zero[dimensions];
2299
2300
// initialize them
2301
for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
2302
group_values[c] = print_values[c] = 0;
2303
print_hidden[c] = rd->hidden;
2304
+ found_non_zero[c] = 0;
2305
}
2306
2307
// -------------------------------------------------------------------------
2315
- // the main loop
2308
+ // remove dimensions that contain only zeros
2309
2317
- // our return value (the last timestamp printed)
2318
- // this is required to detect re-transmit in google JSONP
2319
- time_t last_timestamp = 0;
2320
-
2321
- int annotate_reset = 0;
2322
- int annotation_count = 0;
2323
-
2324
- // to allow grouping on the same values, we need a pad
2325
- long pad = before % group;
2310
+ int max_loop = 1;
2311
+ if(only_non_zero) max_loop = 2;
2312
2327
- // the minimum line length we expect
2328
- int line_size = 4096 + (dimensions * 200);
2313
+ for(; max_loop ; max_loop--) {
2314
2330
- unsigned long long time_usec = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
2331
- long t, count, printed, group_count;
2332
-
2333
- for (count = printed = group_count = 0, t = current_entry; max_entries ; time_usec -= st->timediff[t], t--, max_entries--) {
2334
- if(t < 0) t = st->entries - 1;
2335
-
2336
- if(st->debug)
2337
- if(st->timediff[t] == 0) debug(D_RRD_STATS, "WARNING: %s time diff is zero at position %ld", st->id, t);
2338
-
2339
- int print_this = 0;
2340
- time_t now = time_usec / 1000000ULL;
2341
-
2342
- if(st->debug) {
2343
- debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %lu, %s %s"
2344
- , st->id
2345
- , t
2346
- , count + 1
2347
- , group_count + 1
2348
- , printed
2349
- , now
2350
- , (((count + 1 - pad) % group) == 0)?"PRINT":" - "
2351
- , (now >= after && now <= before)?"RANGE":" - "
2352
- );
2315
+ // -------------------------------------------------------------------------
2316
+ // print the JSON header
2317
+
2318
+ web_buffer_printf(wb, "{\n %scols%s:\n [\n", kq, kq);
2319
+ web_buffer_printf(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);
2320
+ web_buffer_printf(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);
2321
+ web_buffer_printf(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);
2322
+
2323
+ // print the header for each dimension
2324
+ // and update the print_hidden array for the dimensions that should be hidden
2325
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
2326
+ if(!print_hidden[c])
2327
+ web_buffer_printf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq);
2328
}
2329
2355
- // make sure we return data in the proper time range
2356
- if(now < after || now > before) continue;
2330
+ // print the begin of row data
2331
+ web_buffer_printf(wb, "\n ],\n %srows%s:\n [\n", kq, kq);
2332
2358
- count++;
2359
- group_count++;
2333
2361
- if(((count - pad) % group) == 0) {
2362
- if(printed >= entries_to_show) {
2363
- // debug(D_RRD_STATS, "Already printed all rows. Stopping.");
2364
- break;
2365
- }
2366
-
2367
- if(group_count != group) {
2368
- // this is an incomplete group, skip it.
2369
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++)
2370
- group_values[c] = 0;
2371
-
2372
- group_count = 0;
2373
- continue;
2374
- }
2334
+ // -------------------------------------------------------------------------
2335
+ // the main loop
2336
2376
- // check if we may exceed the buffer provided
2377
- web_buffer_increase(wb, line_size);
2337
+ int annotate_reset = 0;
2338
+ int annotation_count = 0;
2339
+
2340
+ // to allow grouping on the same values, we need a pad
2341
+ long pad = before % group;
2342
2379
- // generate the local date time
2380
- struct tm *tm = localtime(&now);
2381
- if(!tm) { error("localtime() failed."); continue; }
2382
- if(now > last_timestamp) last_timestamp = now;
2343
+ // the minimum line length we expect
2344
+ int line_size = 4096 + (dimensions * 200);
2345
2384
- if(printed) web_buffer_strcpy(wb, "]},\n");
2385
- web_buffer_strcpy(wb, pre_date);
2386
- web_buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)((time_usec % 1000000ULL) / 1000));
2387
- web_buffer_strcpy(wb, post_date);
2346
+ unsigned long long time_usec = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
2347
+ long t;
2348
2389
- print_this = 1;
2390
- }
2349
+ long count = 0, printed = 0, group_count = 0;
2350
+ last_timestamp = 0;
2351
+ for(t = current_entry; max_entries ; time_usec -= st->timediff[t], t--, max_entries--) {
2352
+ if(t < 0) t = st->entries - 1;
2353
2392
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
2393
- long value = rd->values[t];
2394
-
2395
- switch(group_method) {
2396
- case GROUP_MAX:
2397
- if(abs(value) > abs(group_values[c])) group_values[c] = value;
2398
- break;
2354
+ if(st->debug)
2355
+ if(st->timediff[t] == 0) debug(D_RRD_STATS, "WARNING: %s time diff is zero at position %ld", st->id, t);
2356
2400
- default:
2401
- case GROUP_AVERAGE:
2402
- group_values[c] += value;
2403
- if(print_this) group_values[c] /= group_count;
2357
+ int print_this = 0;
2358
+ time_t now = time_usec / 1000000ULL;
2359
+
2360
+ if(st->debug) {
2361
+ debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %lu, %s %s"
2362
+ , st->id
2363
+ , t
2364
+ , count + 1
2365
+ , group_count + 1
2366
+ , printed
2367
+ , now
2368
+ , (((count + 1 - pad) % group) == 0)?"PRINT":" - "
2369
+ , (now >= after && now <= before)?"RANGE":" - "
2370
+ );
2371
+ }
2372
+
2373
+ // make sure we return data in the proper time range
2374
+ if(now < after || now > before) continue;
2375
+
2376
+ count++;
2377
+ group_count++;
2378
+
2379
+ if(((count - pad) % group) == 0) {
2380
+ if(printed >= entries_to_show) {
2381
+ // debug(D_RRD_STATS, "Already printed all rows. Stopping.");
2382
break;
2383
+ }
2384
+
2385
+ if(group_count != group) {
2386
+ // this is an incomplete group, skip it.
2387
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++)
2388
+ group_values[c] = 0;
2389
+
2390
+ group_count = 0;
2391
+ continue;
2392
+ }
2393
+
2394
+ // check if we may exceed the buffer provided
2395
+ web_buffer_increase(wb, line_size);
2396
+
2397
+ // generate the local date time
2398
+ struct tm *tm = localtime(&now);
2399
+ if(!tm) { error("localtime() failed."); continue; }
2400
+ if(now > last_timestamp) last_timestamp = now;
2401
+
2402
+ if(printed) web_buffer_strcpy(wb, "]},\n");
2403
+ web_buffer_strcpy(wb, pre_date);
2404
+ web_buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)((time_usec % 1000000ULL) / 1000));
2405
+ web_buffer_strcpy(wb, post_date);
2406
+
2407
+ print_this = 1;
2408
+ }
2409
+
2410
+ for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
2411
+ long value = rd->values[t];
2412
+
2413
+ switch(group_method) {
2414
+ case GROUP_MAX:
2415
+ if(abs(value) > abs(group_values[c])) group_values[c] = value;
2416
+ break;
2417
+
2418
+ default:
2419
+ case GROUP_AVERAGE:
2420
+ group_values[c] += value;
2421
+ if(print_this) group_values[c] /= group_count;
2422
+ break;
2423
+ }
2424
+
2425
+ if(print_this) {
2426
+ print_values[c] = group_values[c];
2427
+ group_values[c] = 0;
2428
+ }
2429
}
2430
2431
if(print_this) {
2408
- print_values[c] = group_values[c];
2409
- group_values[c] = 0;
2432
+ group_count = 0;
2433
+
2434
+ if(annotate_reset) {
2435
+ annotation_count++;
2436
+ web_buffer_strcpy(wb, overflow_annotation);
2437
+ annotate_reset = 0;
2438
+ }
2439
+ else
2440
+ web_buffer_strcpy(wb, normal_annotation);
2441
+
2442
+ for(c = 0 ; c < dimensions ; c++) {
2443
+ if(!print_hidden[c]) {
2444
+ web_buffer_strcpy(wb, pre_value);
2445
+ web_buffer_rrd_value(wb, print_values[c]);
2446
+ web_buffer_strcpy(wb, post_value);
2447
+
2448
+ if(print_values[c]) found_non_zero[c]++;
2449
+ }
2450
+ }
2451
+
2452
+ printed++;
2453
}
2454
}
2455
2413
- if(print_this) {
2414
- group_count = 0;
2415
-
2416
- if(annotate_reset) {
2417
- annotation_count++;
2418
- web_buffer_strcpy(wb, overflow_annotation);
2419
- annotate_reset = 0;
2420
- }
2421
- else
2422
- web_buffer_strcpy(wb, normal_annotation);
2456
+ if(printed) web_buffer_printf(wb, "]}");
2457
+ web_buffer_printf(wb, "\n ]\n}\n");
2458
2424
- for(c = 0 ; c < dimensions ; c++) {
2425
- if(!print_hidden[c]) {
2426
- web_buffer_strcpy(wb, pre_value);
2427
- web_buffer_rrd_value(wb, print_values[c]);
2428
- web_buffer_strcpy(wb, post_value);
2459
+ if(only_non_zero && max_loop > 1) {
2460
+ int changed = 0;
2461
+ for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
2462
+ if(!print_hidden[c] && !found_non_zero[c]) {
2463
+ changed = 1;
2464
+ print_hidden[c] = 1;
2465
}
2466
}
2467
2432
- printed++;
2468
+ if(changed) web_buffer_reset(wb);
2469
+ else break;
2470
}
2434
- }
2435
-
2436
- if(printed) web_buffer_printf(wb, "]}");
2437
- web_buffer_printf(wb, "\n ]\n}\n");
2471
+ else break;
2472
+
2473
+ } // max_loop
2474
2439
- debug(D_RRD_STATS, "RRD_STATS_JSON: %s Generated %ld rows, total %ld bytes", st->name, printed, wb->bytes);
2475
+ debug(D_RRD_STATS, "RRD_STATS_JSON: %s total %ld bytes", st->name, wb->bytes);
2476
2477
pthread_rwlock_unlock(&st->rwlock);
2478
return last_timestamp;
@@ -2733,6 +2769,7 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type
2769
long group_count = 1;
2770
time_t after = 0, before = 0;
2771
int group_method = GROUP_AVERAGE;
2772
+ int nonzero = 0;
2773
2774
if(url) {
2775
// parse the lines required
@@ -2766,6 +2803,11 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type
2803
if(tok) before = strtoul(tok, NULL, 10);
2804
if(before < 0) before = 0;
2805
}
2806
+ if(url) {
2807
+ // parse nonzero
2808
+ tok = mystrsep(&url, "/");
2809
+ if(tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
2810
+ }
2811
2812
w->data->contenttype = CT_APPLICATION_JSON;
2813
w->data->bytes = 0;
@@ -2835,7 +2877,7 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type
2877
}
2878
2879
debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %lu after, %lu before).", w->id, st->name, st->id, lines, group_count, group_method, after, before);
2838
- unsigned long timestamp_in_data = rrd_stats_json(datasource_type, st, w->data, lines, group_count, group_method, after, before);
2880
+ unsigned long timestamp_in_data = rrd_stats_json(datasource_type, st, w->data, lines, group_count, group_method, after, before, nonzero);
2881
2882
if(datasource_type == DATASOURCE_GOOGLE_JSONP) {
2883
if(timestamp_in_data > last_timestamp_in_data)