@cryptotaxi247 / netdata-1 / commits / 49aef8624

enable browser notifications for alarms; fixes #846; fixes #317; contributes to #318, #809

Costa Tsaousis (ktsaou) committed Sep 4, 2016 at 23:48 UTC 49aef8624fe1b50538d53f28722abadf7c149190
18 files changed +229 -35
src/health.c
+5 -3
@@ -1583,7 +1583,7 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
1583 buffer_strcat(wb, "\t}");
1584 }
1585
1586 -void health_alarm_log2json(RRDHOST *host, BUFFER *wb) {
1586 +void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after) {
1587 pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
1588
1589 buffer_strcat(wb, "[");
@@ -1592,8 +1592,10 @@ void health_alarm_log2json(RRDHOST *host, BUFFER *wb) {
1592 unsigned int count = 0;
1593 ALARM_ENTRY *ae;
1594 for(ae = host->health_log.alarms; ae && count < max ; count++, ae = ae->next) {
1595 - if(likely(count)) buffer_strcat(wb, ",");
1596 - health_alarm_entry2json_nolock(wb, ae);
1595 + if(ae->unique_id > after) {
1596 + if(likely(count)) buffer_strcat(wb, ",");
1597 + health_alarm_entry2json_nolock(wb, ae);
1598 + }
1599 }
1600
1601 buffer_strcat(wb, "\n]\n");
src/health.h
+1 -1
@@ -287,6 +287,6 @@ extern void health_reload(void);
287
288 extern int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result);
289 extern void health_alarms2json(RRDHOST *host, BUFFER *wb, int all);
290 -extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb);
290 +extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after);
291
292 #endif //NETDATA_HEALTH_H
src/web_client.c
+15 -4
@@ -680,17 +680,28 @@ int web_client_api_request_v1_alarms(struct web_client *w, char *url)
680
681 int web_client_api_request_v1_alarm_log(struct web_client *w, char *url)
682 {
683 - (void)url;
683 + uint32_t after = 0;
684 +
685 + while(url) {
686 + char *value = mystrsep(&url, "?&[]");
687 + if (!value || !*value) continue;
688 +
689 + char *name = mystrsep(&value, "=");
690 + if(!name || !*name) continue;
691 + if(!value || !*value) continue;
692 +
693 + if(!strcmp(name, "after")) after = strtoul(value, NULL, 0);
694 + }
695
696 buffer_flush(w->response.data);
697 w->response.data->contenttype = CT_APPLICATION_JSON;
687 - health_alarm_log2json(&localhost, w->response.data);
698 + health_alarm_log2json(&localhost, w->response.data, after);
699 return 200;
700 }
701
702 int web_client_api_request_v1_charts(struct web_client *w, char *url)
703 {
693 - if(url) { ; }
704 + (void)url;
705
706 buffer_flush(w->response.data);
707 w->response.data->contenttype = CT_APPLICATION_JSON;
@@ -1336,7 +1347,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
1347
1348 if(unlikely(cookie && person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID)))
1349 person_guid[0] = '\0';
1339 -
1350 +
1351 return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, time(NULL));
1352
1353 case 'D':
web/Makefile.am
+6
@@ -78,7 +78,13 @@ dist_webfonts_DATA = \
78
79 webimagesdir=$(webdir)/images
80 dist_webimages_DATA = \
81 + images/alert-128-orange.png \
82 + images/alert-128-red.png \
83 + images/alert-multi-size-orange.ico \
84 + images/alert-multi-size-red.ico \
85 images/animated.gif \
86 + images/check-mark-2-128-green.png \
87 + images/check-mark-2-multi-size-green.ico \
88 images/post.png \
89 images/seo-performance-16.png \
90 images/seo-performance-24.png \
web/dashboard.html
+1 -1
@@ -655,4 +655,4 @@ So, to avoid flashing the charts, we destroy and re-create the charts on each up
655 <!-- <script> netdataServer = "http://box:19999"; </script> -->
656
657 <!-- load the dashboard manager - it will do the rest -->
658 - <script type="text/javascript" src="dashboard.js?v42"></script>
658 + <script type="text/javascript" src="dashboard.js?v44"></script>
web/dashboard.js
+191 -19
@@ -159,7 +159,7 @@
159 netdataShowHelp = true;
160
161 if(typeof netdataShowAlarms === 'undefined')
162 - netdataShowAlarms = true;
162 + netdataShowAlarms = false;
163
164 NETDATA.colors = NETDATA.themes.current.colors;
165
@@ -3448,8 +3448,7 @@
3448 NETDATA.parseDom(NETDATA.chartRefresher);
3449
3450 // Alarms initialization
3451 - if(netdataShowAlarms === true)
3452 - setTimeout(NETDATA.alarms.init, 1000);
3451 + setTimeout(NETDATA.alarms.init, 1000);
3452
3453 // Registry initialization
3454 setTimeout(NETDATA.registry.init, 1500);
@@ -5576,9 +5575,179 @@
5575 // Registry of netdata hosts
5576
5577 NETDATA.alarms = {
5579 - server: null,
5580 - current: null,
5581 - callback: null,
5578 + notifications: false, // when true, the browser supports notifications (may not be granted though)
5579 + last_notification_id: 0, // the id of the last alarm_log we have raised an alarm for
5580 + notifications_shown: new Array(),
5581 +
5582 + server: null, // the server to connect to for fetching alarms
5583 + current: null, // the list of raised alarms
5584 + notified: { alarms: {} }, // the list of raised alarms for which we have shown notifications
5585 +
5586 + callback: null, // a callback function to call every time the list of raised alarms is refreshed
5587 +
5588 + notify: function(entry) {
5589 + // console.log('alarm ' + entry.unique_id);
5590 +
5591 + if(entry.updated === true) {
5592 + // console.log('alarm ' + entry.unique_id + ' has been updated by another alarm');
5593 + return;
5594 + }
5595 +
5596 + var name = entry.name.replace(/_/g, ' ');
5597 + var title = name + ' = ' + ((entry.value === null)?'NaN':Math.floor(entry.value)).toString() + ' ' + entry.units;
5598 + var body = entry.info + ' of ' + entry.chart + ' (' + entry.family + ')';
5599 + var tag = entry.alarm_id;
5600 + var icon = 'images/seo-performance-128.png';
5601 + var interaction = false;
5602 + var data = entry;
5603 +
5604 + switch(entry.status) {
5605 + case 'UNINITIALIZED':
5606 + // console.log('alarm ' + entry.unique_id + ' is UNINITIALIZED');
5607 + return;
5608 +
5609 + case 'UNDEFINED':
5610 + // console.log('alarm ' + entry.unique_id + ' is UNDEFINED');
5611 + return;
5612 +
5613 + case 'CLEAR':
5614 + if(NETDATA.alarms.last_notification_id === 0) {
5615 + // console.log('alarm ' + entry.unique_id + ' is not current');
5616 + return;
5617 + }
5618 + if(entry.old_status === 'UNINITIALIZED' || entry.old_status === 'UNDEFINED') {
5619 + // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
5620 + return;
5621 + }
5622 + title = entry.name + ' back to normal';
5623 + icon = 'images/check-mark-2-128-green.png'
5624 + interaction = false;
5625 + break;
5626 +
5627 + case 'WARNING':
5628 + icon = 'images/alert-128-orange.png';
5629 + interaction = false;
5630 + break;
5631 +
5632 + case 'CRITICAL':
5633 + icon = 'images/alert-128-red.png'
5634 + interaction = true;
5635 + break;
5636 +
5637 + default:
5638 + console.log('invalid alarm status ' + entry.status);
5639 + return;
5640 + }
5641 +
5642 + // cleanup old notifications with the same alarm_id as this one
5643 + var len = NETDATA.alarms.notifications_shown.length;
5644 + while(len--) {
5645 + var n = NETDATA.alarms.notifications_shown[len];
5646 + if(n.data.alarm_id === entry.alarm_id) {
5647 + // console.log('removing old alarm ' + n.data.unique_id);
5648 +
5649 + // close the notification
5650 + n.close.bind(n);
5651 +
5652 + // remove it from the array
5653 + NETDATA.alarms.notifications_shown.splice(len, 1);
5654 + len = NETDATA.alarms.notifications_shown.length;
5655 + }
5656 + }
5657 +
5658 + // show this notification
5659 + // console.log('new notification: ' + title);
5660 + var n = new Notification(title, {
5661 + body: body,
5662 + tag: tag,
5663 + requireInteraction: interaction,
5664 + icon: NETDATA.serverDefault + icon,
5665 + data: data
5666 + });
5667 +
5668 + // console.log(n);
5669 + NETDATA.alarms.notifications_shown.push(n);
5670 + // console.log(entry);
5671 + },
5672 +
5673 + notifyAll: function() {
5674 + NETDATA.alarms.notified = NETDATA.alarms.current;
5675 +
5676 + // console.log('FETCHING ALARM LOG');
5677 + NETDATA.alarms.get_log(NETDATA.alarms.last_notification_id, function(data) {
5678 + // console.log('ALARM LOG FETCHED');
5679 +
5680 + if(data === null || typeof data !== 'object') {
5681 + console.log('invalid alarms log response');
5682 + return;
5683 + }
5684 +
5685 + if(data.length === 0) {
5686 + console.log('received empty alarm log');
5687 + return;
5688 + }
5689 +
5690 + data.sort(function(a, b) {
5691 + if(a.unique_id > b.unique_id) return -1;
5692 + if(a.unique_id < b.unique_id) return 1;
5693 + return 0;
5694 + });
5695 +
5696 + var len = data.length;
5697 + while(len--) {
5698 + if(data[len].unique_id > NETDATA.alarms.last_notification_id) {
5699 + NETDATA.alarms.notify(data[len]);
5700 + }
5701 + }
5702 +
5703 + NETDATA.alarms.last_notification_id = data[0].unique_id;
5704 + })
5705 + },
5706 +
5707 + check_notifications: function() {
5708 + // returns true if we should fire 1+ notifications
5709 +
5710 + if(NETDATA.alarms.notifications !== true) {
5711 + // console.log('notifications not available');
5712 + return false;
5713 + }
5714 +
5715 + if(Notification.permission !== 'granted') {
5716 + // console.log('notifications not granted');
5717 + return false;
5718 + }
5719 +
5720 + if(typeof NETDATA.alarms.current !== 'undefined' && typeof NETDATA.alarms.current.alarms === 'object') {
5721 + // console.log('can do alarms');
5722 +
5723 + var current = NETDATA.alarms.current.alarms;
5724 + var old = NETDATA.alarms.notified.alarms;
5725 + var x;
5726 +
5727 + if(Object.keys(current).length !== Object.keys(old).length) {
5728 + // console.log('alarm count differs');
5729 + return true;
5730 + }
5731 + else {
5732 + for(x in current) {
5733 + if(!(x in old)) {
5734 + // console.log('new alarm detected: ' + x);
5735 + return true;
5736 + }
5737 +
5738 + if(current[x].status !== old[x].status) {
5739 + // console.log('alarm changed state: ' + x);
5740 + return true;
5741 + }
5742 + }
5743 + }
5744 +
5745 + //console.log('alarms did not change');
5746 + }
5747 + // else console.log('cannot process alarms');
5748 +
5749 + return false;
5750 + },
5751
5752 get: function(what, callback) {
5753 $.ajax({
@@ -5602,17 +5771,13 @@
5771 update_forever: function() {
5772 NETDATA.alarms.get('active', function(data) {
5773 if(data !== null) {
5605 - if('Notification' in window && NETDATA.alarms.current != null) {
5606 - if(Object.keys(NETDATA.alarms.current.alarms).length < Object.keys(data.alarms).length) {
5607 - if (Notification.permission === 'granted') {
5608 - new Notification('Netdata Alarm!', {body: 'Your Server needs attention!',
5609 - icon: 'images/seo-performance-128.png'});
5610 - }
5611 - }
5612 - }
5774
5775 NETDATA.alarms.current = data;
5776
5777 + if(NETDATA.alarms.check_notifications() === true) {
5778 + NETDATA.alarms.notifyAll();
5779 + }
5780 +
5781 if (typeof NETDATA.alarms.callback === 'function') {
5782 NETDATA.alarms.callback(data);
5783 }
@@ -5622,9 +5787,9 @@
5787 });
5788 },
5789
5625 - get_log: function(callback) {
5790 + get_log: function(last_id, callback) {
5791 $.ajax({
5627 - url: NETDATA.alarms.server + '/api/v1/alarm_log',
5792 + url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(),
5793 async: true,
5794 cache: false,
5795 xhrFields: { withCredentials: true } // required for the cookie
@@ -5647,9 +5812,16 @@
5812 host = host.substring(0, host.length - 1);
5813 NETDATA.alarms.server = host;
5814
5650 - NETDATA.alarms.update_forever();
5651 - if ('Notification' in window && Notification.permission === 'default') {
5652 - Notification.requestPermission();
5815 + if(netdataShowAlarms === true) {
5816 + NETDATA.alarms.update_forever();
5817 +
5818 + if('Notification' in window) {
5819 + // console.log('notifications available');
5820 + NETDATA.alarms.notifications = true;
5821 +
5822 + if(Notification.permission === 'default')
5823 + Notification.requestPermission();
5824 + }
5825 }
5826 }
5827 };
web/demo.html
+1 -1
@@ -20,7 +20,7 @@
20 <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
21 <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
22
23 - <script type="text/javascript" src="dashboard.js?v42"></script>
23 + <script type="text/javascript" src="dashboard.js?v44"></script>
24 </head>
25 <body>
26
web/demo2.html
+1 -1
@@ -21,7 +21,7 @@
21 <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
22
23 <script>var netdataTheme = 'slate';</script>
24 - <script type="text/javascript" src="http://my-netdata.io/dashboard.js?v42"></script>
24 + <script type="text/javascript" src="http://my-netdata.io/dashboard.js?v44"></script>
25 </head>
26 <body>
27
web/demosites.html
+1 -1
@@ -51,7 +51,7 @@
51 and that you have chown it to be owned by netdata:netdata
52 -->
53 <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
54 - <script type="text/javascript" src="dashboard.js?v42"></script>
54 + <script type="text/javascript" src="dashboard.js?v44"></script>
55
56 <script>
57 // --- OPTIONS FOR THE CHARTS --
web/images/alert-128-orange.png
Binary files /dev/null and b/web/images/alert-128-orange.png differ
web/images/alert-128-red.png
Binary files /dev/null and b/web/images/alert-128-red.png differ
web/images/alert-multi-size-orange.ico
Binary files /dev/null and b/web/images/alert-multi-size-orange.ico differ
web/images/alert-multi-size-red.ico
Binary files /dev/null and b/web/images/alert-multi-size-red.ico differ
web/images/check-mark-2-128-green.png
Binary files /dev/null and b/web/images/check-mark-2-128-green.png differ
web/images/check-mark-2-multi-size-green.ico
Binary files /dev/null and b/web/images/check-mark-2-multi-size-green.ico differ
web/index.html
+5 -2
@@ -344,6 +344,9 @@
344
345 <!-- check which theme to use -->
346 <script type="text/javascript">
347 + var netdataShowNotifications = true;
348 + var netdataShowAlarms = true;
349 +
350 // --------------------------------------------------------------------
351 // urlOptions
352
@@ -539,7 +542,7 @@
542 </script>
543
544 <!-- load the dashboard manager - it will do the rest -->
542 - <script type="text/javascript" src="dashboard.js?v43"></script>
545 + <script type="text/javascript" src="dashboard.js?v44"></script>
546 </head>
547 <body data-spy="scroll" data-target="#sidebar">
548 <div id="loadOverlay" class="loadOverlay" style="background-color: #888; color: #888;">
@@ -2718,7 +2721,7 @@ function alarmsUpdateModal() {
2721 $('#alarm_all_' + id.toString()).html('');
2722 });
2723
2721 - NETDATA.alarms.get_log(function(data) {
2724 + NETDATA.alarms.get_log(0, function(data) {
2725 if(data === null) {
2726 document.getElementById('alarms_log').innerHTML =
2727 'failed to load alarm data!';
web/registry.html
+1 -1
@@ -134,7 +134,7 @@
134 and that you have chown it to be owned by netdata:netdata
135 -->
136 <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
137 - <script type="text/javascript" src="dashboard.js?v42"></script>
137 + <script type="text/javascript" src="dashboard.js?v44"></script>
138
139 <script>
140 // Set options for TV operation
web/tv.html
+1 -1
@@ -49,7 +49,7 @@
49 and that you have chown it to be owned by netdata:netdata
50 -->
51 <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
52 - <script type="text/javascript" src="dashboard.js?v42"></script>
52 + <script type="text/javascript" src="dashboard.js?v44"></script>
53
54 <script>
55 // Set options for TV operation