added support for grouping values (average, max)
Costa Tsaousis (ktsaou) committed
Mar 23, 2014 at 03:31 UTC
4093d485190ffe324760707c9d837db34e5a6ba7
2 files changed
+106
-43
netdata.c
+97
-40
@@ -32,11 +32,11 @@
32
33
34
// internal defaults
35
-#define UPDATE_EVERY 2
35
+#define UPDATE_EVERY 1
36
#define UPDATE_EVERY_MAX 3600
37
#define LISTEN_PORT 19999
38
-#define HISTORY 120
39
-#define HISTORY_MAX 86400
38
+#define HISTORY 3600
39
+#define HISTORY_MAX (86400*10)
40
#define SAVE_PATH "/tmp"
41
42
#define D_WEB_BUFFER 0x00000001
@@ -405,36 +405,54 @@ void rrd_stats_dimension_set(RRD_STATS *st, const char *dimension, void *data)
405
}
406
407
if(rd->bytes == sizeof(unsigned long long)) {
408
- unsigned long long *dimension = rd->values, *value = data;
408
+ long long *dimension = rd->values, *value = data;
409
410
dimension[st->last_entry] = (*value);
411
}
412
else if(rd->bytes == sizeof(unsigned long)) {
413
- unsigned long *dimension = rd->values, *value = data;
413
+ long *dimension = rd->values, *value = data;
414
415
dimension[st->last_entry] = (*value);
416
}
417
else if(rd->bytes == sizeof(unsigned int)) {
418
- unsigned int *dimension = rd->values, *value = data;
418
+ int *dimension = rd->values, *value = data;
419
420
dimension[st->last_entry] = (*value);
421
}
422
else if(rd->bytes == sizeof(unsigned char)) {
423
- unsigned char *dimension = rd->values, *value = data;
423
+ char *dimension = rd->values, *value = data;
424
425
dimension[st->last_entry] = (*value);
426
}
427
else fatal("I don't know how to handle data of length %d bytes.", rd->bytes);
428
}
429
430
-size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_show)
430
+#define GROUP_AVERAGE 0
431
+#define GROUP_MAX 1
432
+
433
+size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_show, size_t group_count, int group_method)
434
{
432
- size_t i = 0, t, lt, c;
435
+ size_t i = 0, c, t, lt, printed = 0, dimensions = 0, last_entry = st->last_entry;
436
+ int pad = 0;
437
long count = st->entries;
438
RRD_DIMENSION *rd;
439
unsigned long long usec = 0;
440
+ long long value;
441
char dtm[1025];
442
443
+ // find how many dimensions we have
444
+ for( rd = st->dimensions ; rd ; rd = rd->next)
445
+ dimensions++;
446
+
447
+ if(!dimensions)
448
+ return sprintf(b, "No dimensions yet.");
449
+
450
+ // keep track of group values and counts
451
+ long long group_values[dimensions];
452
+ long long group_counts[dimensions];
453
+ for( rd = st->dimensions, c = 0 ; rd ; rd = rd->next, c++)
454
+ group_values[c] = group_counts[c] = 0;
455
+
456
i += sprintf(&b[i], "{\n \"cols\":\n [\n");
457
i += sprintf(&b[i], " {\"id\":\"\",\"label\":\"time\",\"pattern\":\"\",\"type\":\"timeofday\"},\n");
458
@@ -443,8 +461,11 @@ size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_s
461
462
i += sprintf(&b[i], " ],\n \"rows\":\n [\n");
463
446
- // find the old entry
447
- t = st->last_entry + 1;
464
+ // to allow grouping on the same values, we need a pad
465
+ pad = last_entry % group_count;
466
+
467
+ // find the old entry of the round-robin
468
+ t = last_entry + 1;
469
if(t >= st->entries) t = 0;
470
lt = t;
471
@@ -452,9 +473,9 @@ size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_s
473
t++;
474
if(t >= st->entries) t = 0;
475
455
- count -= 2;
476
// the loop in dimension data
457
- for ( c = 0 ; t != st->last_entry ; lt = t++, count--) {
477
+ count -= 2;
478
+ for ( ; t != last_entry ; lt = t++, count--) {
479
if(t >= st->entries) t = 0;
480
481
// if the last is empty, loop again
@@ -463,48 +484,69 @@ size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_s
484
// check if we may exceed the buffer provided
485
if((length - i) < 1024) break;
486
466
- if(count > entries_to_show) continue;
487
+ if(((count-pad) / group_count) > entries_to_show) continue;
488
489
// find how much usec since the previous entry
490
usec = usecdiff(&st->times[t], &st->times[lt]);
491
471
- // generate the local date time
472
- struct tm *tm = localtime(&st->times[t].tv_sec);
473
- if(!tm) { error("localtime() failed."); continue; }
492
+ if(((count-pad) % group_count) == 0) {
493
+ if(printed >= entries_to_show) break;
494
475
- strftime(dtm, 1024, "[%H, %M, %S, 0]", tm);
476
- i += sprintf(&b[i], "%s {\"c\":[{\"v\":%s},", c?",\n":"", dtm);
495
+ // generate the local date time
496
+ struct tm *tm = localtime(&st->times[t].tv_sec);
497
+ if(!tm) { error("localtime() failed."); continue; }
498
478
- for( rd = st->dimensions ; rd ; rd = rd->next) {
479
- if(rd->bytes == sizeof(unsigned long long)) {
480
- unsigned long long *dimension = rd->values, v;
481
- v = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
499
+ strftime(dtm, 1024, "[%H, %M, %S, 0]", tm);
500
+ i += sprintf(&b[i], "%s {\"c\":[{\"v\":%s},", printed?"]},\n":"", dtm);
501
+
502
+ printed++;
503
+ }
504
+
505
+ for( rd = st->dimensions, c = 0 ; rd ; rd = rd->next, c++) {
506
+ // check if new dimensions have been added since we started
507
+ if(c >= dimensions)
508
+ return sprintf(b, "New dimensions added while we were working on them. Try again later.");
509
483
- i += sprintf(&b[i], "{\"v\":%lld}%s", v, rd->next?",":"");
510
+ if(rd->bytes == sizeof(unsigned long long)) {
511
+ long long *dimension = rd->values;
512
+ value = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
513
}
514
else if(rd->bytes == sizeof(unsigned long)) {
486
- unsigned long *dimension = rd->values, v;
487
- v = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
488
-
489
- i += sprintf(&b[i], "{\"v\":%ld}%s", v, rd->next?",":"");
515
+ long *dimension = rd->values;
516
+ value = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
517
}
518
else if(rd->bytes == sizeof(unsigned int)) {
492
- unsigned int *dimension = rd->values, v;
493
- v = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
494
-
495
- i += sprintf(&b[i], "{\"v\":%d}%s", v, rd->next?",":"");
519
+ int *dimension = rd->values;
520
+ value = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
521
}
522
else if(rd->bytes == sizeof(unsigned char)) {
498
- unsigned char *dimension = rd->values, v;
499
- v = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
523
+ char *dimension = rd->values;
524
+ value = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
525
+ }
526
+ else fatal("Cannot produce JSON for size %d bytes dimension.", rd->bytes);
527
+
528
+ switch(group_method) {
529
+ case GROUP_MAX:
530
+ if(value > group_values[c]) group_values[c] = value;
531
+ break;
532
501
- i += sprintf(&b[i], "{\"v\":%d}%s", (int)v, rd->next?",":"");
533
+ default:
534
+ case GROUP_AVERAGE:
535
+ group_values[c] += value;
536
+ break;
537
+ }
538
+ group_counts[c]++;
539
+
540
+ if(((count-pad) % group_count) == 0) {
541
+ if(group_method == GROUP_AVERAGE) group_values[c] /= group_counts[c];
542
+
543
+ i += sprintf(&b[i], "{\"v\":%lld}%s", group_values[c], rd->next?",":"");
544
+
545
+ group_values[c] = group_counts[c] = 0;
546
}
503
- else fatal("Cannot produce JSON for size %d bytes data.", rd->bytes);
547
}
505
- i += sprintf(&b[i], "]}");
506
- c++;
548
}
549
+ if(printed) i += sprintf(&b[i], "]}");
550
i += sprintf(&b[i], "\n ]\n}\n");
551
552
if(count != 0) error("RRD count should be zero, but found to be %d.", count);
@@ -795,17 +837,32 @@ void web_client_process(struct web_client *w)
837
838
// how many entries does the client want?
839
size_t lines = save_history;
840
+ size_t group_count = 1;
841
+ int group_method = GROUP_AVERAGE;
842
+
843
if(url) {
844
tok = mystrsep(&url, "/?");
845
if(tok) lines = atoi(tok);
846
if(lines < 5) lines = save_history;
847
}
848
+ if(url) {
849
+ tok = mystrsep(&url, "/?");
850
+ if(tok) group_count = atoi(tok);
851
+ if(group_count < 1) group_count = 1;
852
+ if(group_count > save_history / 20) group_count = save_history / 20;
853
+ }
854
+ if(url) {
855
+ tok = mystrsep(&url, "/?");
856
+ if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
857
+ else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
858
+ else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", tok);
859
+ }
860
804
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (%d lines).", w->id, st->name, lines);
861
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (%d lines, %d group_count, %d group_method).", w->id, st->name, lines, group_count, group_method);
862
863
code = 200;
864
w->data->contenttype = CT_APPLICATION_JSON;
808
- w->data->bytes = rrd_stats_json(st, w->data->buffer, w->data->size, lines);
865
+ w->data->bytes = rrd_stats_json(st, w->data->buffer, w->data->size, lines, group_count, group_method);
866
}
867
}
868
else if(strcmp(tok, "mirror") == 0) {
netdata.start
+9
-3
@@ -9,9 +9,12 @@ then
9
fi
10
cd "$base" || exit 1
11
12
-# every how many seconds to update the JSON files
12
+# every how many seconds to show the graphs
13
NETDATA_CONFIG_UPDATE_EVERY=2
14
15
+# the detail of the data that will be kept in netdata
16
+NETDATA_CONFIG_INTERNAL_UPDATE_EVERY=1
17
+
18
# how many points should the graphs have
19
NETDATA_CONFIG_HISTORY_POINTS=120
20
@@ -28,6 +31,9 @@ then
31
source netdata.conf
32
fi
33
34
+# how many history lines to keep in netdata
35
+NETDATA_HISTORY_LINES=$[NETDATA_CONFIG_UPDATE_EVERY * NETDATA_CONFIG_HISTORY_POINTS / NETDATA_CONFIG_INTERNAL_UPDATE_EVERY]
36
+
37
echo "Creating a directory for netdata..."
38
data=
39
for x in /run/netdata /var/run/netdata /tmp/netdata
@@ -80,7 +86,7 @@ gcc -Wall -O3 -o netdata netdata.c -lpthread || exit 1
86
echo "Starting netdata"
87
p=`pwd`
88
cd data
83
-$p/netdata -d -u $NETDATA_CONFIG_UPDATE_EVERY -l $NETDATA_CONFIG_HISTORY_DATA || exit 1
89
+$p/netdata -d -u $NETDATA_CONFIG_INTERNAL_UPDATE_EVERY -l $NETDATA_HISTORY_LINES || exit 1
90
cd "$p"
91
92
sleep 1
@@ -175,7 +181,7 @@ do
181
if [ ! "$p" = "IGNORE" ]
182
then
183
cat >>${data}/index.html <<EOF2
178
- drawChart('${x}', '${y}_div', width, height, "data/${x}/${NETDATA_CONFIG_HISTORY_POINTS}", "${t}");
184
+ drawChart('${x}', '${y}_div', width, height, "data/${x}/${NETDATA_CONFIG_HISTORY_POINTS}/${NETDATA_CONFIG_UPDATE_EVERY}/average/", "${t}");
185
EOF2
186
fi
187
done