pretty value formatting on all alarm notifications
Costa Tsaousis (ktsaou) committed
Jan 26, 2017 at 13:15 UTC
dcd95ef8fd7e665f99b567042afa1d1e500f14e5
7 files changed
+140
-109
src/health.c
+20
-5
@@ -290,6 +290,10 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char
290
ae->new_value = str2l(pointers[25]);
291
ae->old_value = str2l(pointers[26]);
292
293
+ static char value_string[100 + 1];
294
+ ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1));
295
+ ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1));
296
+
297
// add it to host if not already there
298
if(unlikely(*pointers[0] == 'A')) {
299
ae->next = host->health_log.alarms;
@@ -391,6 +395,11 @@ static inline void health_alarm_log(RRDHOST *host,
395
ae->when = when;
396
ae->old_value = old_value;
397
ae->new_value = new_value;
398
+
399
+ static char value_string[100 + 1];
400
+ ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1));
401
+ ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1));
402
+
403
ae->old_status = old_status;
404
ae->new_status = new_status;
405
ae->duration = duration;
@@ -2331,7 +2340,9 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, R
2340
"\t\t\"delay\": %d,\n"
2341
"\t\t\"delay_up_to_timestamp\": %lu,\n"
2342
"\t\t\"updated_by_id\": %u,\n"
2334
- "\t\t\"updates_id\": %u,\n",
2343
+ "\t\t\"updates_id\": %u,\n"
2344
+ "\t\t\"value_string\": \"%s\",\n"
2345
+ "\t\t\"old_value_string\": \"%s\",\n",
2346
host->hostname,
2347
ae->unique_id,
2348
ae->alarm_id,
@@ -2357,7 +2368,9 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, R
2368
ae->delay,
2369
(unsigned long)ae->delay_up_to_timestamp,
2370
ae->updated_by_id,
2360
- ae->updates_id
2371
+ ae->updates_id,
2372
+ ae->new_value_string,
2373
+ ae->old_value_string
2374
);
2375
2376
buffer_strcat(wb, "\t\t\"value\":");
@@ -2643,7 +2656,7 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
2656
const char *recipient = ae->recipient;
2657
if(!recipient) recipient = health.health_default_recipient;
2658
2646
- snprintfz(command_to_run, ALARM_EXEC_COMMAND_LENGTH, "exec %s '%s' '%s' '%u' '%u' '%u' '%lu' '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s'",
2659
+ snprintfz(command_to_run, ALARM_EXEC_COMMAND_LENGTH, "exec %s '%s' '%s' '%u' '%u' '%u' '%lu' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%u' '%u' '%s' '%s'",
2660
exec,
2661
recipient,
2662
host->hostname,
@@ -2656,8 +2669,8 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
2669
ae->family?ae->family:"NOFAMILY",
2670
rrdcalc_status2string(ae->new_status),
2671
rrdcalc_status2string(ae->old_status),
2659
- ae->new_value,
2660
- ae->old_value,
2672
+ ae->new_value_string,
2673
+ ae->old_value_string,
2674
ae->source?ae->source:"UNKNOWN",
2675
(uint32_t)ae->duration,
2676
(uint32_t)ae->non_clear_duration,
@@ -2754,6 +2767,8 @@ static inline void health_alarm_log_process(RRDHOST *host) {
2767
freez(ae->source);
2768
freez(ae->units);
2769
freez(ae->info);
2770
+ freez(ae->old_value_string);
2771
+ freez(ae->new_value_string);
2772
freez(ae);
2773
2774
ae = t;
src/health.h
+4
@@ -308,6 +308,10 @@ typedef struct alarm_entry {
308
309
calculated_number old_value;
310
calculated_number new_value;
311
+
312
+ char *old_value_string;
313
+ char *new_value_string;
314
+
315
int old_status;
316
int new_status;
317
src/web_buffer_svg.c
+84
-90
@@ -368,7 +368,64 @@ cleanup:
368
return len - i;
369
}
370
371
-static inline int fix_value_and_units(char *value_string, size_t value_string_len, calculated_number value, const char **units_ptr, int value_is_null) {
371
+static inline char *format_value_with_precision_and_unit(char *value_string, size_t value_string_len, calculated_number value, const char *units, int precision) {
372
+ if(unlikely(isnan(value) || isinf(value)))
373
+ value = 0.0;
374
+
375
+ char *separator = "";
376
+ if(unlikely(isalnum(*units)))
377
+ separator = " ";
378
+
379
+ if(precision < 0) {
380
+ int len, lstop = 0, trim_zeros = 1;
381
+
382
+ calculated_number abs = value;
383
+ if(isless(value, 0)) {
384
+ lstop = 1;
385
+ abs = -value;
386
+ }
387
+
388
+ if(isgreaterequal(abs, 1000)) {
389
+ len = snprintfz(value_string, value_string_len, "%0.0Lf", (long double) value);
390
+ trim_zeros = 0;
391
+ }
392
+ else if(isgreaterequal(abs, 100)) len = snprintfz(value_string, value_string_len, "%0.1Lf", (long double) value);
393
+ else if(isgreaterequal(abs, 1)) len = snprintfz(value_string, value_string_len, "%0.2Lf", (long double) value);
394
+ else if(isgreaterequal(abs, 0.1)) len = snprintfz(value_string, value_string_len, "%0.3Lf", (long double) value);
395
+ else len = snprintfz(value_string, value_string_len, "%0.4Lf", (long double) value);
396
+
397
+ if(unlikely(trim_zeros)) {
398
+ int l;
399
+ // remove trailing zeros from the decimal part
400
+ for(l = len - 1; l > lstop; l--) {
401
+ if(likely(value_string[l] == '0')) {
402
+ value_string[l] = '\0';
403
+ len--;
404
+ }
405
+
406
+ else if(unlikely(value_string[l] == '.')) {
407
+ value_string[l] = '\0';
408
+ len--;
409
+ break;
410
+ }
411
+
412
+ else
413
+ break;
414
+ }
415
+ }
416
+
417
+ if(unlikely(len <= 0)) len = 1;
418
+ snprintfz(&value_string[len], value_string_len - len, "%s%s", separator, units);
419
+ }
420
+ else {
421
+ if(precision > 50) precision = 50;
422
+ snprintfz(value_string, value_string_len, "%0.*Lf%s%s", precision, (long double) value, separator, units);
423
+ }
424
+
425
+ return value_string;
426
+}
427
+
428
+inline char *format_value_and_unit(char *value_string, size_t value_string_len, calculated_number value, const char *units, int precision) {
429
static uint32_t
430
hash_seconds = 0,
431
hash_seconds_ago = 0,
@@ -404,7 +461,6 @@ static inline int fix_value_and_units(char *value_string, size_t value_string_le
461
hash_pcent = simple_hash("pcent");
462
}
463
407
- const char *units = (units_ptr)?*units_ptr:NULL;
464
if(unlikely(!units)) units = "";
465
466
uint32_t hash_units = simple_hash(units);
@@ -412,13 +468,11 @@ static inline int fix_value_and_units(char *value_string, size_t value_string_le
468
if(unlikely((hash_units == hash_seconds && !strcmp(units, "seconds")) || (hash_units == hash_seconds_ago && !strcmp(units, "seconds ago")))) {
469
if(value == 0.0) {
470
snprintfz(value_string, value_string_len, "%s", "now");
415
- units = "";
416
- goto finish;
471
+ return value_string;
472
}
473
else if(isnan(value) || isinf(value)) {
474
snprintfz(value_string, value_string_len, "%s", "never");
420
- units = "";
421
- goto finish;
475
+ return value_string;
476
}
477
478
const char *suffix = (hash_units == hash_seconds_ago)?" ago":"";
@@ -438,19 +492,17 @@ static inline int fix_value_and_units(char *value_string, size_t value_string_le
492
else
493
snprintfz(value_string, value_string_len, "%02zu:%02zu:%02zu%s", h, m, s, suffix);
494
441
- units = "";
495
+ return value_string;
496
}
497
498
else if(unlikely((hash_units == hash_minutes && !strcmp(units, "minutes")) || (hash_units == hash_minutes_ago && !strcmp(units, "minutes ago")))) {
499
if(value == 0.0) {
500
snprintfz(value_string, value_string_len, "%s", "now");
447
- units = "";
448
- goto finish;
501
+ return value_string;
502
}
503
else if(isnan(value) || isinf(value)) {
504
snprintfz(value_string, value_string_len, "%s", "never");
452
- units = "";
453
- goto finish;
505
+ return value_string;
506
}
507
508
const char *suffix = (hash_units == hash_minutes_ago)?" ago":"";
@@ -467,19 +519,17 @@ static inline int fix_value_and_units(char *value_string, size_t value_string_le
519
else
520
snprintfz(value_string, value_string_len, "%zuh %zum%s", h, m, suffix);
521
470
- units = "";
522
+ return value_string;
523
}
524
525
else if(unlikely((hash_units == hash_hours && !strcmp(units, "hours")) || (hash_units == hash_hours_ago && !strcmp(units, "hours ago")))) {
526
if(value == 0.0) {
527
snprintfz(value_string, value_string_len, "%s", "now");
476
- units = "";
477
- goto finish;
528
+ return value_string;
529
}
530
else if(isnan(value) || isinf(value)) {
531
snprintfz(value_string, value_string_len, "%s", "never");
481
- units = "";
482
- goto finish;
532
+ return value_string;
533
}
534
535
const char *suffix = (hash_units == hash_hours_ago)?" ago":"";
@@ -493,27 +543,27 @@ static inline int fix_value_and_units(char *value_string, size_t value_string_le
543
else
544
snprintfz(value_string, value_string_len, "%zuh%s", h, suffix);
545
496
- units = "";
546
+ return value_string;
547
}
548
549
else if(unlikely(hash_units == hash_onoff && !strcmp(units, "on/off"))) {
550
snprintfz(value_string, value_string_len, "%s", (value != 0.0)?"on":"off");
501
- units = "";
551
+ return value_string;
552
}
553
554
else if(unlikely(hash_units == hash_updown && !strcmp(units, "up/down"))) {
555
snprintfz(value_string, value_string_len, "%s", (value != 0.0)?"up":"down");
506
- units = "";
556
+ return value_string;
557
}
558
559
else if(unlikely(hash_units == hash_okerror && !strcmp(units, "ok/error"))) {
560
snprintfz(value_string, value_string_len, "%s", (value != 0.0)?"ok":"error");
511
- units = "";
561
+ return value_string;
562
}
563
564
else if(unlikely(hash_units == hash_okfailed && !strcmp(units, "ok/failed"))) {
565
snprintfz(value_string, value_string_len, "%s", (value != 0.0)?"ok":"failed");
516
- units = "";
566
+ return value_string;
567
}
568
569
else if(unlikely(hash_units == hash_empty && !strcmp(units, "empty")))
@@ -531,16 +581,13 @@ static inline int fix_value_and_units(char *value_string, size_t value_string_le
581
else if(unlikely(hash_units == hash_pcent && !strcmp(units, "pcent")))
582
units = "%";
583
534
- else if(unlikely(value_is_null)) {
584
+
585
+ if(unlikely(isnan(value) || isinf(value))) {
586
strcpy(value_string, "-");
536
- units = "";
587
+ return value_string;
588
}
538
- else
539
- return 0;
589
541
-finish:
542
- if(units_ptr) *units_ptr = units;
543
- return 1;
590
+ return format_value_with_precision_and_unit(value_string, value_string_len, value, units, precision);
591
}
592
593
static inline const char *color_map(const char *color) {
@@ -560,9 +607,12 @@ static inline const char *color_map(const char *color) {
607
return color;
608
}
609
563
-static inline void calc_colorz(const char *color, char *final, size_t len, calculated_number value, int value_is_null) {
564
- if(isnan(value) || isinf(value))
610
+static inline void calc_colorz(const char *color, char *final, size_t len, calculated_number value) {
611
+ int value_is_null = 0;
612
+ if(isnan(value) || isinf(value)) {
613
value = 0.0;
614
+ value_is_null = 1;
615
+ }
616
617
char color_buffer[256 + 1] = "";
618
char value_buffer[256 + 1] = "";
@@ -673,7 +723,7 @@ static inline void calc_colorz(const char *color, char *final, size_t len, calcu
723
// colors
724
#define COLOR_STRING_SIZE 100
725
676
-void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int value_is_null, int precision) {
726
+void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision) {
727
char label_buffer[LABEL_STRING_SIZE + 1]
728
, value_color_buffer[COLOR_STRING_SIZE + 1]
729
, value_string[VALUE_STRING_SIZE + 1]
@@ -688,66 +738,10 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
738
label_color = "#555";
739
740
if(unlikely(!value_color || !*value_color))
691
- value_color = (value_is_null)?"#999":"#4c1";
692
-
693
- calc_colorz(value_color, value_color_buffer, COLOR_STRING_SIZE, value, value_is_null);
694
-
695
- if(!fix_value_and_units(value_string, VALUE_STRING_SIZE, value, &units, value_is_null)) {
696
- // we have to print the value
697
-
698
- if(isnan(value) || isinf(value))
699
- value = 0.0;
700
-
701
- char *separator = "";
702
- if(unlikely(isalnum(*units)))
703
- separator = " ";
704
-
705
- if(precision < 0) {
706
- int len, lstop = 0, trim_zeros = 1;
707
-
708
- calculated_number abs = value;
709
- if(isless(value, 0)) {
710
- lstop = 1;
711
- abs = -value;
712
- }
741
+ value_color = (isnan(value) || isinf(value))?"#999":"#4c1";
742
714
- if(isgreaterequal(abs, 1000)) {
715
- len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.0Lf", (long double) value);
716
- trim_zeros = 0;
717
- }
718
- else if(isgreaterequal(abs, 100)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.1Lf", (long double) value);
719
- else if(isgreaterequal(abs, 1)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.2Lf", (long double) value);
720
- else if(isgreaterequal(abs, 0.1)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.3Lf", (long double) value);
721
- else len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.4Lf", (long double) value);
722
-
723
- if(unlikely(trim_zeros)) {
724
- int l;
725
- // remove trailing zeros from the decimal part
726
- for(l = len - 1; l > lstop; l--) {
727
- if(likely(value_string[l] == '0')) {
728
- value_string[l] = '\0';
729
- len--;
730
- }
731
-
732
- else if(unlikely(value_string[l] == '.')) {
733
- value_string[l] = '\0';
734
- len--;
735
- break;
736
- }
737
-
738
- else
739
- break;
740
- }
741
- }
742
-
743
- if(len >= 0)
744
- snprintfz(&value_string[len], VALUE_STRING_SIZE - len, "%s%s", separator, units);
745
- }
746
- else {
747
- if(precision > 50) precision = 50;
748
- snprintfz(value_string, VALUE_STRING_SIZE, "%0.*Lf%s%s", precision, (long double) value, separator, units);
749
- }
750
- }
743
+ calc_colorz(value_color, value_color_buffer, COLOR_STRING_SIZE, value);
744
+ format_value_and_unit(value_string, VALUE_STRING_SIZE, value, units, precision);
745
746
// we need to copy the label, since verdana11_width may write to it
747
strncpyz(label_buffer, label, LABEL_STRING_SIZE);
src/web_buffer_svg.h
+2
-1
@@ -1,6 +1,7 @@
1
#ifndef NETDATA_WEB_BUFFER_SVG_H
2
#define NETDATA_WEB_BUFFER_SVG_H 1
3
4
-extern void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int value_is_null, int precision);
4
+extern void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision);
5
+extern char *format_value_and_unit(char *value_string, size_t value_string_len, calculated_number value, const char *units, int precision);
6
7
#endif /* NETDATA_WEB_BUFFER_SVG_H */
src/web_client.c
+9
-11
@@ -896,7 +896,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
896
if(!st) st = rrdset_find_byname(chart);
897
if(!st) {
898
buffer_no_cacheable(w->response.data);
899
- buffer_svg(w->response.data, "chart not found", 0, "", NULL, NULL, 1, -1);
899
+ buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1);
900
ret = 200;
901
goto cleanup;
902
}
@@ -906,7 +906,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
906
rc = rrdcalc_find(st, alarm);
907
if (!rc) {
908
buffer_no_cacheable(w->response.data);
909
- buffer_svg(w->response.data, "alarm not found", 0, "", NULL, NULL, 1, -1);
909
+ buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1);
910
ret = 200;
911
goto cleanup;
912
}
@@ -1022,14 +1022,13 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
1022
units,
1023
label_color,
1024
value_color,
1025
- 0,
1025
precision);
1026
ret = 200;
1027
}
1028
else {
1029
time_t latest_timestamp = 0;
1030
int value_is_null = 1;
1032
- calculated_number n = 0;
1031
+ calculated_number n = NAN;
1032
ret = 500;
1033
1034
// if the collected value is too old, don't calculate its value
@@ -1062,13 +1061,12 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
1061
1062
// render the badge
1063
buffer_svg(w->response.data,
1065
- label,
1066
- n * multiply / divide,
1067
- units,
1068
- label_color,
1069
- value_color,
1070
- value_is_null,
1071
- precision);
1064
+ label,
1065
+ (value_is_null)?NAN:(n * multiply / divide),
1066
+ units,
1067
+ label_color,
1068
+ value_color,
1069
+ precision);
1070
}
1071
1072
cleanup:
web/dashboard.js
+2
-2
@@ -6075,7 +6075,7 @@ var NETDATA = window.NETDATA || {};
6075
6076
var name = entry.name.replace(/_/g, ' ');
6077
var status = entry.status.toLowerCase();
6078
- var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units;
6078
+ var title = name + ' = ' + entry.value_string.toString();
6079
var tag = entry.alarm_id;
6080
var icon = 'images/seo-performance-128.png';
6081
var interaction = false;
@@ -6104,7 +6104,7 @@ var NETDATA = window.NETDATA || {};
6104
// console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
6105
return;
6106
}
6107
- title = name + ' back to normal';
6107
+ title = name + ' back to normal (' + entry.value_string.toString() + ')';
6108
icon = 'images/check-mark-2-128-green.png'
6109
interaction = false;
6110
break;
web/index.html
+19
@@ -1855,6 +1855,23 @@
1855
switchable: false,
1856
sortable: true
1857
},
1858
+ {
1859
+ field: 'value_string',
1860
+ title: 'Friendly Value',
1861
+ titleTooltip: 'The value of the alarm, that triggered this event',
1862
+ align: 'right',
1863
+ valign: 'middle',
1864
+ sortable: true
1865
+ },
1866
+ {
1867
+ field: 'old_value_string',
1868
+ title: 'Friendly Old Value',
1869
+ titleTooltip: 'The value of the alarm, just before this event',
1870
+ align: 'right',
1871
+ valign: 'middle',
1872
+ visible: false,
1873
+ sortable: true
1874
+ },
1875
{
1876
field: 'old_value',
1877
title: 'Old Value',
@@ -1876,6 +1893,7 @@
1893
},
1894
align: 'right',
1895
valign: 'middle',
1896
+ visible: false,
1897
sortable: true
1898
},
1899
{
@@ -1884,6 +1902,7 @@
1902
titleTooltip: 'The units of the value of the alarm',
1903
align: 'left',
1904
valign: 'middle',
1905
+ visible: false,
1906
sortable: true
1907
},
1908
{