]> arthur.barton.de Git - netdata.git/commitdiff
enable browser notifications for alarms; fixes #846; fixes #317; contributes to ...
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 4 Sep 2016 20:48:00 +0000 (23:48 +0300)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 4 Sep 2016 20:48:00 +0000 (23:48 +0300)
18 files changed:
src/health.c
src/health.h
src/web_client.c
web/Makefile.am
web/dashboard.html
web/dashboard.js
web/demo.html
web/demo2.html
web/demosites.html
web/images/alert-128-orange.png [new file with mode: 0644]
web/images/alert-128-red.png [new file with mode: 0644]
web/images/alert-multi-size-orange.ico [new file with mode: 0644]
web/images/alert-multi-size-red.ico [new file with mode: 0644]
web/images/check-mark-2-128-green.png [new file with mode: 0644]
web/images/check-mark-2-multi-size-green.ico [new file with mode: 0644]
web/index.html
web/registry.html
web/tv.html

index edc2849ee8e7a73fbbb1b11b7f838d5f389cc62f..613f10606116c2e4f85cc557ace15c9b8f357892 100644 (file)
@@ -1583,7 +1583,7 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
     buffer_strcat(wb, "\t}");
 }
 
-void health_alarm_log2json(RRDHOST *host, BUFFER *wb) {
+void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after) {
     pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
 
     buffer_strcat(wb, "[");
@@ -1592,8 +1592,10 @@ void health_alarm_log2json(RRDHOST *host, BUFFER *wb) {
     unsigned int count = 0;
     ALARM_ENTRY *ae;
     for(ae = host->health_log.alarms; ae && count < max ; count++, ae = ae->next) {
-        if(likely(count)) buffer_strcat(wb, ",");
-        health_alarm_entry2json_nolock(wb, ae);
+        if(ae->unique_id > after) {
+            if(likely(count)) buffer_strcat(wb, ",");
+            health_alarm_entry2json_nolock(wb, ae);
+        }
     }
 
     buffer_strcat(wb, "\n]\n");
index 567e1153974b233c4ff3e001c0fb3bf5bb63f68d..cd8a061e58e706bbb8e5b4f7867add5aaade84cd 100644 (file)
@@ -287,6 +287,6 @@ extern void health_reload(void);
 
 extern int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result);
 extern void health_alarms2json(RRDHOST *host, BUFFER *wb, int all);
-extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb);
+extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after);
 
 #endif //NETDATA_HEALTH_H
index ad605b69c048c4973b3028195830d422314378b5..7d1f77cbc8dbcf7da224164c23ce4b25bc5ec56a 100644 (file)
@@ -680,17 +680,28 @@ int web_client_api_request_v1_alarms(struct web_client *w, char *url)
 
 int web_client_api_request_v1_alarm_log(struct web_client *w, char *url)
 {
-    (void)url;
+    uint32_t after = 0;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&[]");
+        if (!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        if(!strcmp(name, "after")) after = strtoul(value, NULL, 0);
+    }
 
     buffer_flush(w->response.data);
     w->response.data->contenttype = CT_APPLICATION_JSON;
-    health_alarm_log2json(&localhost, w->response.data);
+    health_alarm_log2json(&localhost, w->response.data, after);
     return 200;
 }
 
 int web_client_api_request_v1_charts(struct web_client *w, char *url)
 {
-    if(url) { ; }
+    (void)url;
 
     buffer_flush(w->response.data);
     w->response.data->contenttype = CT_APPLICATION_JSON;
@@ -1336,7 +1347,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
 
             if(unlikely(cookie && person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID)))
                 person_guid[0] = '\0';
-            
+
             return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, time(NULL));
 
         case 'D':
index 0432f8a589365c22039c5357715f8bea1540c5bc..3c8d19d90128f37b5b201b89dab76bfc7941490e 100644 (file)
@@ -78,7 +78,13 @@ dist_webfonts_DATA = \
 
 webimagesdir=$(webdir)/images
 dist_webimages_DATA = \
+       images/alert-128-orange.png \
+       images/alert-128-red.png \
+       images/alert-multi-size-orange.ico \
+       images/alert-multi-size-red.ico \
        images/animated.gif \
+       images/check-mark-2-128-green.png \
+       images/check-mark-2-multi-size-green.ico \
        images/post.png \
        images/seo-performance-16.png \
        images/seo-performance-24.png \
index e5d01353f0b97e0831ac68f1edfffd12e04660ba..7fcc73688bc431bc9b567b5a837ec015cb7907e6 100644 (file)
@@ -655,4 +655,4 @@ So, to avoid flashing the charts, we destroy and re-create the charts on each up
     <!-- <script> netdataServer = "http://box:19999"; </script> -->
 
     <!-- load the dashboard manager - it will do the rest -->
-    <script type="text/javascript" src="dashboard.js?v42"></script>
+    <script type="text/javascript" src="dashboard.js?v44"></script>
index 1dc57a1ac58a66dbe7efd63bd52a56011a4f429d..a659ee74b0ae65c24317548731c64b9b94c4a5c2 100644 (file)
         netdataShowHelp = true;
 
     if(typeof netdataShowAlarms === 'undefined')
-        netdataShowAlarms = true;
+        netdataShowAlarms = false;
 
     NETDATA.colors = NETDATA.themes.current.colors;
 
         NETDATA.parseDom(NETDATA.chartRefresher);
 
         // Alarms initialization
-        if(netdataShowAlarms === true)
-            setTimeout(NETDATA.alarms.init, 1000);
+        setTimeout(NETDATA.alarms.init, 1000);
 
         // Registry initialization
         setTimeout(NETDATA.registry.init, 1500);
     // Registry of netdata hosts
 
     NETDATA.alarms = {
-        server: null,
-        current: null,
-        callback: null,
+        notifications: false,       // when true, the browser supports notifications (may not be granted though)
+        last_notification_id: 0,    // the id of the last alarm_log we have raised an alarm for
+        notifications_shown: new Array(),
+
+        server: null,               // the server to connect to for fetching alarms
+        current: null,              // the list of raised alarms
+        notified: { alarms: {} },   // the list of raised alarms for which we have shown notifications
+
+        callback: null,             // a callback function to call every time the list of raised alarms is refreshed
+
+        notify: function(entry) {
+            // console.log('alarm ' + entry.unique_id);
+
+            if(entry.updated === true) {
+                // console.log('alarm ' + entry.unique_id + ' has been updated by another alarm');
+                return;
+            }
+
+            var name = entry.name.replace(/_/g, ' ');
+            var title = name + ' = ' + ((entry.value === null)?'NaN':Math.floor(entry.value)).toString() + ' ' + entry.units;
+            var body = entry.info + ' of ' + entry.chart + ' (' + entry.family + ')';
+            var tag = entry.alarm_id;
+            var icon = 'images/seo-performance-128.png';
+            var interaction = false;
+            var data = entry;
+
+            switch(entry.status) {
+                case 'UNINITIALIZED':
+                    // console.log('alarm ' + entry.unique_id + ' is UNINITIALIZED');
+                    return;
+
+                case 'UNDEFINED':
+                    // console.log('alarm ' + entry.unique_id + ' is UNDEFINED');
+                    return;
+
+                case 'CLEAR':
+                    if(NETDATA.alarms.last_notification_id === 0) {
+                        // console.log('alarm ' + entry.unique_id + ' is not current');
+                        return;
+                    }
+                    if(entry.old_status === 'UNINITIALIZED' || entry.old_status === 'UNDEFINED') {
+                        // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status);
+                        return;
+                    }
+                    title = entry.name + ' back to normal';
+                    icon = 'images/check-mark-2-128-green.png'
+                    interaction = false;
+                    break;
+
+                case 'WARNING':
+                    icon = 'images/alert-128-orange.png';
+                    interaction = false;
+                    break;
+
+                case 'CRITICAL':
+                    icon = 'images/alert-128-red.png'
+                    interaction = true;
+                    break;
+
+                default:
+                    console.log('invalid alarm status ' + entry.status);
+                    return;
+            }
+
+            // cleanup old notifications with the same alarm_id as this one
+            var len = NETDATA.alarms.notifications_shown.length;
+            while(len--) {
+                var n = NETDATA.alarms.notifications_shown[len];
+                if(n.data.alarm_id === entry.alarm_id) {
+                    // console.log('removing old alarm ' + n.data.unique_id);
+
+                    // close the notification
+                    n.close.bind(n);
+
+                    // remove it from the array
+                    NETDATA.alarms.notifications_shown.splice(len, 1);
+                    len = NETDATA.alarms.notifications_shown.length;
+                }
+            }
+
+            // show this notification
+            // console.log('new notification: ' + title);
+            var n = new Notification(title, {
+                body: body,
+                tag: tag,
+                requireInteraction: interaction,
+                icon: NETDATA.serverDefault + icon,
+                data: data
+            });
+
+            // console.log(n);
+            NETDATA.alarms.notifications_shown.push(n);
+            // console.log(entry);
+        },
+
+        notifyAll: function() {
+            NETDATA.alarms.notified = NETDATA.alarms.current;
+
+            // console.log('FETCHING ALARM LOG');
+            NETDATA.alarms.get_log(NETDATA.alarms.last_notification_id, function(data) {
+                // console.log('ALARM LOG FETCHED');
+
+                if(data === null || typeof data !== 'object') {
+                    console.log('invalid alarms log response');
+                    return;
+                }
+
+                if(data.length === 0) {
+                    console.log('received empty alarm log');
+                    return;
+                }
+
+                data.sort(function(a, b) {
+                    if(a.unique_id > b.unique_id) return -1;
+                    if(a.unique_id < b.unique_id) return 1;
+                    return 0;
+                });
+
+                var len = data.length;
+                while(len--) {
+                    if(data[len].unique_id > NETDATA.alarms.last_notification_id) {
+                        NETDATA.alarms.notify(data[len]);
+                    }
+                }
+
+                NETDATA.alarms.last_notification_id = data[0].unique_id;
+            })
+        },
+
+        check_notifications: function() {
+            // returns true if we should fire 1+ notifications
+
+            if(NETDATA.alarms.notifications !== true) {
+                // console.log('notifications not available');
+                return false;
+            }
+
+            if(Notification.permission !== 'granted') {
+                // console.log('notifications not granted');
+                return false;
+            }
+
+            if(typeof NETDATA.alarms.current !== 'undefined' && typeof NETDATA.alarms.current.alarms === 'object') {
+                // console.log('can do alarms');
+
+                var current = NETDATA.alarms.current.alarms;
+                var old = NETDATA.alarms.notified.alarms;
+                var x;
+
+                if(Object.keys(current).length !== Object.keys(old).length) {
+                    // console.log('alarm count differs');
+                    return true;
+                }
+                else {
+                    for(x in current) {
+                        if(!(x in old)) {
+                            // console.log('new alarm detected: ' + x);
+                            return true;
+                        }
+
+                        if(current[x].status !== old[x].status) {
+                            // console.log('alarm changed state: ' + x);
+                            return true;
+                        }
+                    }
+                }
+
+                //console.log('alarms did not change');
+            }
+            // else console.log('cannot process alarms');
+
+            return false;
+        },
 
         get: function(what, callback) {
             $.ajax({
         update_forever: function() {
             NETDATA.alarms.get('active', function(data) {
                 if(data !== null) {
-                    if('Notification' in window && NETDATA.alarms.current != null) {
-                        if(Object.keys(NETDATA.alarms.current.alarms).length < Object.keys(data.alarms).length) {
-                            if (Notification.permission === 'granted') {
-                                new Notification('Netdata Alarm!', {body: 'Your Server needs attention!',
-                                    icon: 'images/seo-performance-128.png'});
-                            }
-                        }
-                    }
 
                     NETDATA.alarms.current = data;
 
+                    if(NETDATA.alarms.check_notifications() === true) {
+                        NETDATA.alarms.notifyAll();
+                    }
+
                     if (typeof NETDATA.alarms.callback === 'function') {
                         NETDATA.alarms.callback(data);
                     }
             });
         },
 
-        get_log: function(callback) {
+        get_log: function(last_id, callback) {
             $.ajax({
-                url: NETDATA.alarms.server + '/api/v1/alarm_log',
+                url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(),
                 async: true,
                 cache: false,
                 xhrFields: { withCredentials: true } // required for the cookie
                 host = host.substring(0, host.length - 1);
             NETDATA.alarms.server = host;
             
-            NETDATA.alarms.update_forever();
-            if ('Notification' in window && Notification.permission === 'default') {
-                Notification.requestPermission();
+            if(netdataShowAlarms === true) {
+                NETDATA.alarms.update_forever();
+            
+                if('Notification' in window) {
+                    // console.log('notifications available');
+                    NETDATA.alarms.notifications = true;
+
+                    if(Notification.permission === 'default')
+                        Notification.requestPermission();
+                }
             }
         }
     };
index 89bcb68c703d92b5f120a1c336b47d580db15e03..8ed63f096ae93f2d35ff63ad054396caf4f1a399 100644 (file)
@@ -20,7 +20,7 @@
     <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
     <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
     
-    <script type="text/javascript" src="dashboard.js?v42"></script>
+    <script type="text/javascript" src="dashboard.js?v44"></script>
 </head>
 <body>
 
index 5f8136ce505a63e2a64496c9d3d10fc820878c4c..7e0f3058f4a6be551f2194a6db5e2cbfe91ff994 100644 (file)
@@ -21,7 +21,7 @@
     <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
 
     <script>var netdataTheme = 'slate';</script>
-    <script type="text/javascript" src="http://my-netdata.io/dashboard.js?v42"></script>
+    <script type="text/javascript" src="http://my-netdata.io/dashboard.js?v44"></script>
 </head>
 <body>
 
index d57bb4e71adae52e7bd9edaffad40f1b2ea78c98..9ad5b5326bd766ab5c277179929b187fa3a86cca 100644 (file)
@@ -51,7 +51,7 @@
         and that you have chown it to be owned by netdata:netdata
     -->
     <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
-    <script type="text/javascript" src="dashboard.js?v42"></script>
+    <script type="text/javascript" src="dashboard.js?v44"></script>
 
     <script>
     // --- OPTIONS FOR THE CHARTS --
diff --git a/web/images/alert-128-orange.png b/web/images/alert-128-orange.png
new file mode 100644 (file)
index 0000000..35ece6a
Binary files /dev/null and b/web/images/alert-128-orange.png differ
diff --git a/web/images/alert-128-red.png b/web/images/alert-128-red.png
new file mode 100644 (file)
index 0000000..9afa851
Binary files /dev/null and b/web/images/alert-128-red.png differ
diff --git a/web/images/alert-multi-size-orange.ico b/web/images/alert-multi-size-orange.ico
new file mode 100644 (file)
index 0000000..edca438
Binary files /dev/null and b/web/images/alert-multi-size-orange.ico differ
diff --git a/web/images/alert-multi-size-red.ico b/web/images/alert-multi-size-red.ico
new file mode 100644 (file)
index 0000000..8f7cbd0
Binary files /dev/null and b/web/images/alert-multi-size-red.ico differ
diff --git a/web/images/check-mark-2-128-green.png b/web/images/check-mark-2-128-green.png
new file mode 100644 (file)
index 0000000..71d1760
Binary files /dev/null and b/web/images/check-mark-2-128-green.png differ
diff --git a/web/images/check-mark-2-multi-size-green.ico b/web/images/check-mark-2-multi-size-green.ico
new file mode 100644 (file)
index 0000000..2fc4141
Binary files /dev/null and b/web/images/check-mark-2-multi-size-green.ico differ
index fd04abb268a54e943b1b9bd3222486c76b5a9cf2..120f2c073213f417ccc2cd0957d03813fa5c0f28 100644 (file)
 
     <!-- check which theme to use -->
     <script type="text/javascript">
+        var netdataShowNotifications = true;
+        var netdataShowAlarms = true;
+        
         // --------------------------------------------------------------------
         // urlOptions
 
     </script>
 
     <!-- load the dashboard manager - it will do the rest -->
-    <script type="text/javascript" src="dashboard.js?v43"></script>
+    <script type="text/javascript" src="dashboard.js?v44"></script>
 </head>
 <body data-spy="scroll" data-target="#sidebar">
     <div id="loadOverlay" class="loadOverlay" style="background-color: #888; color: #888;">
@@ -2718,7 +2721,7 @@ function alarmsUpdateModal() {
             $('#alarm_all_' + id.toString()).html('');
         });
 
-        NETDATA.alarms.get_log(function(data) {
+        NETDATA.alarms.get_log(0, function(data) {
             if(data === null) {
                 document.getElementById('alarms_log').innerHTML =
                         'failed to load alarm data!';
index 74c676228aff63a04d6f0b1b38db2e4f8ef9830c..8b5dc3870236f4054b4cc74bbea02838c63e50a4 100644 (file)
         and that you have chown it to be owned by netdata:netdata
     -->
     <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
-    <script type="text/javascript" src="dashboard.js?v42"></script>
+    <script type="text/javascript" src="dashboard.js?v44"></script>
 
     <script>
     // Set options for TV operation
index 53c47a45383ee90e5b9aa712ff1f081e0bdad519..f648a41eaff5e3b1d5672af8b32990ad711dc858 100644 (file)
@@ -49,7 +49,7 @@
         and that you have chown it to be owned by netdata:netdata
     -->
     <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
-    <script type="text/javascript" src="dashboard.js?v42"></script>
+    <script type="text/javascript" src="dashboard.js?v44"></script>
 
     <script>
     // Set options for TV operation