@hfsnetbr/ping-monitor
Version:
Website Uptime Event Emitter
361 lines (259 loc) • 7.71 kB
JavaScript
'use strict';
const util = require('util');
const EventEmitter = require('events').EventEmitter;
const httpMonitor = require('./http');
const tcpMonitor = require('./tcp');
const utils = require('./utils');
function Monitor (opts, state) {
EventEmitter.call(this);
this.id = null;
this.created_at = null;
this.title = '';
this.method = 'GET';
this.website = null;
this.address = null;
this.host = null;
this.interval = 5;
this.port = null;
this.handle = null;
this.active = true;
this.isUp = true;
this.paused = false;
this.totalRequests = 0;
this.totalDownTimes = 0;
this.lastDownTime = null;
this.lastRequest = null;
this.httpOptions = {};
this.expect = {
statusCode: 200,
contentSearch: null
};
// initialize the app
this.init(opts, state);
return this;
}
/*
Inherit from EventEmitter
*/
util.inherits(Monitor, EventEmitter);
Monitor.prototype.init = function (opts, state) {
this.setProperties(opts, state);
if (!this.active) {
/*eslint no-console: ["error", { allow: ["warn", "error", "log"] }] */
return console.log(`${this.title} monitor is off`);
}
if (!this.website && !this.address) {
return this.emit('error', new Error('You did not specify a website to monitor'));
}
else if (this.website && this.address) {
return this.emit('error', new Error('You can only specify either a website or a tcp address'));
}
// start monitoring
if (this.website) {
this.start('http');
}
else {
this.start('tcp');
}
};
Monitor.prototype.setProperties = function (opts, state) {
const defaultState = this.getState();
const currentState = Object.assign(defaultState, opts, state || {});
currentState.host = currentState.website || currentState.address;
if (!currentState.created_at) {
currentState.created_at = Date.now();
}
this.setState(currentState);
};
Monitor.prototype.setState = function (state) {
Object.keys(state).forEach((key) => {
this[key] = state[key];
});
};
Monitor.prototype.getState = function () {
return {
id: this.id,
title: this.title,
created_at: this.created_at,
isUp: this.isUp,
website: this.website,
address: this.address,
host: this.host,
port: this.port,
totalRequests: this.totalRequests,
totalDownTimes: this.totalDownTimes,
lastDownTime: this.lastDownTime,
lastRequest: this.lastRequest,
interval: this.interval,
active: this.active,
httpOptions: this.httpOptions,
expect: this.expect
};
};
Monitor.prototype.start = function (method) {
let host = this.website || this.address + ':' + this.port;
let startTime = utils.getFormatedDate();
const ONE_MINUTE = (60 * 1000);
const INTERVAL = this.interval * ONE_MINUTE;
/*eslint no-console: ["error", { allow: ["warn", "error", "log"] }] */
console.log(`\nMonitoring:${host}\nTime: ${startTime}\n`);
if (method === 'http') {
this.pingHTTP();
// create an interval for pings
this.handle = setInterval(() => {
this.pingHTTP();
}, INTERVAL);
}
else {
this.pingTCP();
// create an interval for pings
this.handle = setInterval(() => {
this.pingTCP();
}, INTERVAL);
}
};
Monitor.prototype.stop = function () {
let responseData = utils.responseData(200, this.website, 0, this.address, this.port);
this.active = false;
clearInterval(this.handle);
this.handle = null;
this.emit('stop', responseData, this.getState());
return this;
};
Monitor.prototype.pause = function () {
if (this.handle) {
clearInterval(this.handle);
this.handle = null;
this.paused = true;
/*eslint no-console: ["error", { allow: ["warn", "error", "log"] }] */
console.log('%s has paused', this.title || this.host);
}
};
Monitor.prototype.unpause = function () {
if (this.website && this.active) {
this.paused = false;
this.start('http');
/*eslint no-console: ["error", { allow: ["warn", "error", "log"] }] */
console.log('%s has unpaused', this.title || this.host);
}
else if (this.address && this.active) {
this.paused = false;
this.start('tcp');
/*eslint no-console: ["error", { allow: ["warn", "error", "log"] }] */
console.log('%s has paused', this.title || this.host);
}
};
Monitor.prototype.restart = function () {
this.active = true;
if (this.website) {
this.start('http');
}
else {
this.start('tcp');
}
return this;
};
Monitor.prototype.pingHTTP = function () {
this.totalRequests += 1;
this.lastRequest = Date.now();
const options = {
website: this.website,
method: this.method,
httpOptions: this.httpOptions
};
process.nextTick(() => {
httpMonitor(options, (error, data, res) => {
if(!error) {
let bodyChunks = [];
res.on('data', (chunk) => {
bodyChunks.push(chunk);
});
res.on('end', () => {
data.body = bodyChunks.join('');
if (this.expect) {
let isUp = true;
// Check if actual status code matches the expected code.
if (this.expect.statusCode) {
isUp = isUp && (parseInt(res.statusCode , 10) === parseInt(this.expect.statusCode, 10));
}
// Checks if we can find the content within the response body.
if (this.expect.contentSearch) {
isUp = isUp && data.body.includes(this.expect.contentSearch);
}
this.isUp = isUp;
if (!isUp) {
this.lastDownTime = Date.now();
this.totalDownTimes += 1;
}
}
else if (res.statusCode == 200) {
this.isUp = true;
}
else {
this.isUp = false;
this.lastDownTime = Date.now();
this.totalDownTimes += 1;
}
data.error = error;
data.httpResponse = res;
this.response(this.isUp, res.statusCode, data);
});
} else {
this.isUp = false;
this.lastDownTime = Date.now();
this.totalDownTimes += 1;
data.error = error;
data.httpResponse = res;
this.response(this.isUp, res.statusCode, data);
}
});
});
};
Monitor.prototype.pingTCP = function () {
this.totalRequests += 1;
this.lastRequest = Date.now();
process.nextTick(() => {
tcpMonitor({
address: this.address,
port: this.port
}, (error, data) => {
if (error) {
this.isUp = false;
this.lastDownTime = Date.now();
this.totalDownTimes += 1;
data.error = error;
this.response(this.isUp, 500, data);
}
else {
this.isUp = true;
this.response(this.isUp, 200, data);
}
});
});
};
Monitor.prototype.response = function (isUp, statusCode, data) {
let responseData = utils.responseData(statusCode, this.website, data.responseTime, this.address, this.port);
if (data.httpResponse) {
responseData.httpResponse = data.httpResponse;
}
if (isUp) {
this.emit('up', responseData, this.getState());
}
else {
if (data.timeout) {
this.emit('timeout', data.error, responseData, this.getState());
}
else if (data.error) {
this.emit('error', data.error, responseData, this.getState());
}
else {
this.emit('down', responseData, this.getState());
}
}
};
// process.on('uncaughtException', function (err) {
// /*eslint no-console: ["error", { allow: ["warn", "error", "log"] }] */
// console.log('UNCAUGHT EXCEPTION', err);
// process.exit(1);
// });
module.exports = Monitor;