inceptum
Version:
hipages take on the foundational library for enterprise-grade apps written in NodeJS
159 lines • 5.68 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Decorators_1 = require("../ioc/Decorators");
exports.HEALTH_CHECK_GROUP = 'inceptum:health';
function RegisterAsHealthCheck(target) {
Decorators_1.RegisterInGroup(exports.HEALTH_CHECK_GROUP)(target);
Decorators_1.Lazy(false)(target);
}
exports.RegisterAsHealthCheck = RegisterAsHealthCheck;
var HealthCheckStatus;
(function (HealthCheckStatus) {
HealthCheckStatus["OK"] = "OK";
HealthCheckStatus["NOT_READY"] = "NOT_READY";
HealthCheckStatus["WARNING"] = "WARNING";
HealthCheckStatus["CRITICAL"] = "CRITICAL";
})(HealthCheckStatus = exports.HealthCheckStatus || (exports.HealthCheckStatus = {}));
function getStatusIndex(status) {
return [HealthCheckStatus.OK, HealthCheckStatus.NOT_READY, HealthCheckStatus.WARNING, HealthCheckStatus.CRITICAL].indexOf(status);
}
class HealthCheckResult {
constructor(status, message, timestamp, data) {
this.status = status;
this.message = message;
this.data = data;
this.timestamp = timestamp || new Date().getTime();
}
static getInitialResult() {
return new HealthCheckResult(HealthCheckStatus.NOT_READY, 'First check not run yet');
}
static staleResult(healthCheck, lastResult) {
return new HealthCheckResult(HealthCheckStatus.CRITICAL, `Stale check result for check ${healthCheck.checkName}. Last result on ${lastResult.timestamp}`);
}
}
exports.HealthCheckResult = HealthCheckResult;
class HealthCheck {
constructor(checkName, sleepMillis = 60000, staleNumFails = 2, runImmediately = false) {
this.checkName = checkName;
this.sleepMillis = sleepMillis;
this.staleNumFails = staleNumFails;
this.runImmediately = runImmediately;
this.started = false;
this.lastResult = HealthCheckResult.getInitialResult();
this.checkRunning = false;
}
getCheckName() {
return this.checkName;
}
getSleepMillis() {
return this.sleepMillis;
}
start() {
if (this.started) {
return;
}
this.started = true;
this.doStart();
if (this.runImmediately) {
this.runCheck();
}
}
async runCheck() {
if (!this.checkRunning) {
this.checkRunning = true;
try {
this.lastResult = await this.doCheck();
}
catch (e) {
this.lastResult = new HealthCheckResult(HealthCheckStatus.CRITICAL, `There was an error running check ${this.getCheckName()}: ${e.message}`);
}
this.checkRunning = false;
}
}
doStart() {
this.timer = setInterval(async () => { await this.runCheck(); }, this.getSleepMillis());
}
stop() {
if (this.started) {
this.doStop();
}
}
doStop() {
if (this.timer) {
clearInterval(this.timer);
}
}
isStarted() {
return this.started;
}
getLastResult() {
const threshold = (new Date().getTime()) - this.getSleepMillis() * this.staleNumFails;
if (this.lastResult.timestamp < threshold) {
return HealthCheckResult.staleResult(this, this.lastResult);
}
return this.lastResult;
}
}
exports.HealthCheck = HealthCheck;
class HealthCheckGroup extends HealthCheck {
constructor(groupName) {
super(`Group: ${groupName}`, 0, 0);
this.healthChecks = new Map();
this.groupKey = groupName;
}
addCheck(healthCheck, asName) {
this.addCheckAs(healthCheck, asName || healthCheck.getCheckName());
}
addCheckAs(healthCheck, asName) {
if (asName.indexOf('.') >= 0) {
const parts = asName.split('.', 2);
const firstPart = parts[0];
const remainderPart = asName.substr(firstPart.length + 1);
const group = this.healthChecks.get(firstPart);
if (group && (group instanceof HealthCheckGroup)) {
group.addCheckAs(healthCheck, remainderPart);
}
else if (group) {
throw new Error(`A health check with name ${firstPart} is already defined in this group`);
}
else {
const newGroup = new HealthCheckGroup(firstPart);
newGroup.addCheckAs(healthCheck, remainderPart);
this.addCheck(newGroup, firstPart);
}
}
else {
this.healthChecks.set(asName, healthCheck);
if (this.isStarted()) {
healthCheck.start();
}
}
}
doStart() {
this.healthChecks.forEach((healthCheck) => {
healthCheck.start();
});
}
doStop() {
this.healthChecks.forEach((healthCheck) => {
healthCheck.stop();
});
}
async doCheck() {
throw new Error('This shouldn\'t be called. We override getLastResult to get up-to-date info');
}
getLastResult() {
const resp = new HealthCheckResult(HealthCheckStatus.OK, 'OK', new Date().getTime(), {});
this.healthChecks.forEach((healthCheck, key) => {
const lastResult = healthCheck.getLastResult();
if (getStatusIndex(lastResult.status) > getStatusIndex(resp.status)) {
resp.status = lastResult.status;
resp.message = `Check ${healthCheck.getCheckName()} returned ${resp.status}`;
}
resp.data[key] = lastResult;
});
return resp;
}
}
exports.HealthCheckGroup = HealthCheckGroup;
//# sourceMappingURL=HealthCheck.js.map