Chrome, JavaScript

Monitor Your Site With Apps Script

Network alertIn this short tutorial we will see how to built an application that can be used as a (simple) Nagios but, without the need to set up a server and maintain a lot of code. It’s not a replacement to a ‘full’ monitoring systems that give you rich set of abilities but more ‘an idea’ to show you the power of Google cloud with Apps script. The main three components of our system will be:

  1. Google Spreadsheet – We will use two sheets. One to allow us to set configuration data like: urls to monitor, email to get notifications etc’. The other will be our ‘log’. We will write to it all the alerts so we could analyze them later.
  2. Google Form – This will be a quick and easy way to acknowledge that we are aware for the situation and we wish to mute the emails.
  3. Apps Script – All the logic for the checks, alerts and everything in between will be done with a short script with one trigger.

For the first two components, we will use a regular google sheet that looks similar to this:

website-monitor

and a form that will enable us to mute the alert:

website-monitor-2

Let’s see some code snippets. First we will create the main function that will fetch our site and analyze the content in order to see that we present the user a valid page.

function blogMonitor() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[2];
var curUrl = sheet.getRange("B1").getValue();
var curStatus = sheet.getRange("B3").getValue();
// monitorObj contain the url we are checking and its status (200 or 503 or any other HTML return code).
var monitorObj = {url: curUrl, status: curStatus};
// We will start as if the blog is down and hope for the best.
var code = 503;
try {
// Fetch the url of our site
var response = UrlFetchApp.fetch(monitorObj.url);
// Get the content (=HTML) in order to check for anchors
// that will validate the site is up and running
var checkRes = response.getContentText();
// You can put here any string that will let you know the site is up.
if (checkRes.indexOf("ToDo - some string from the h1 or any other anchor on the site") < 1) {
// The site doesn't render our validation string
code = 503;
Logger.log("Our site on: " + monitorObj.url + " is not OK");
}
else {
// If it's something else let's get the HTML return code
code = response.getResponseCode();
Logger.log("Our site on: " + monitorObj.url + " is OK. Code: "+ code);
}
} catch(error) {
Logger.log("Err: during blogMonitor() we got: " + error);
}
// monitorObj contain two properties: site-url and its status.
// The status will be the return code. E.g. 200 - we are good.
monitorObj.status = checkAndNotify(monitorObj, code);
// store the obj in our meta data sheet or if you wish
// you can use ScriptDB
saveMonitorObj(monitorObj);
}

Now that we have the ‘fetcher’ we can add a logic to alert only if we have 2 (or more) failures in X minutes. We are using the triggers in order to run the checks every 5 minutes and later a time based trigger to do another 2nd check. This will minimize the false-positive cases of alerts. You can also think on a mechanism that will stop the alerts after Y emails. For the brave ones, you can send SMS (or voice!) with the integration of Apps script with Twilio!

//
// The main logic to check the site and do a quick 2nd check in case we
// did not get 200 as return code
//
function checkAndNotify(monitorObj, curCode) {
var status = monitorObj.status;
if (curCode === 200) {
if (status === 503) {
// Site was down previously but up on second check
logToSheet(monitorObj.url, "The site is Up");
}
// We are cool
return curCode;
}
// Site was up previously but is now down
if (curCode === 503 ) {
if (status === 200) {
// Run another check after 1 minutes to prevent false positives
quickCheck();
}
else if (status === 503 ) {
// Site was down previously and is down again
quickCheck();
logToSheet(monitorObj.url, "Process is Down");
}
return curCode;
}
Logger.log("WARN: We did not catch code: " + curCode + " - let's check why");
return curCode;
}
//
// Log our alerts to the main sheet and get info from
// The 'Form' sheet in order to mute the alerts.
//
function logToSheet(url, message) {
var formSheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
// We wish to see what is the last response from the 'mute alerts' form
var toStopEmailsRow = formSheet.getLastRow();
var toStopEmails = formSheet.getRange(toStopEmailsRow, 2).getValue();
// The logger sheet
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var row = sheet.getLastRow() + 1;
var time = new Date();
sheet.getRange(row,1).setValue(time);
sheet.getRange(row,2).setValue(message + " : " + url);
var alert = "At: " + time + " Our site on: " + url +
" is " + message.toLowerCase() +
" | To stop emails: http://bit.ly/please-stop-email-now&quot;;
if (toStopEmails === "Yes") {
Logger.log(alert + " But with no email");
}
else {
// Send an email with the alert
Logger.log(alert);
MailApp.sendEmail(sheet.getRange("B3").getValue(), "Site " + message, alert);
}
}
//
// Let's check the site again to confirm that it's down
//
function secondCheck() {
// first we clean all the triggers to keep our house clean
var checksTriggers = ScriptApp.getProjectTriggers();
for (var i=0; i< checksTriggers.length; i++) {
if (checksTriggers[i].getHandlerFunction() == "secondCheck") {
ScriptApp.deleteTrigger(checksTriggers[i]);
}
}
// Back to our main function
blogMonitor();
}
//
// Our const - you should put it at another file that contain all your
// configurations.
//
var HALFMinute = 30000;
//
// Wait 30sec and check again
//
function quickCheck() {
ScriptApp.newTrigger("secondCheck").timeBased().after(HALFMinute).create();
}

Other things to note in the script above are the usage of the spreadsheet a our simple database. You can do something better with ScriptDb and have more powerful with this object database. In case you do use ScriptDB, it will be great to checkout ScriptDB common patterns as it gives you the functions you will need. Happy hacking.

Advertisement
Standard

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s