import { sendAnalyticWebhook, sendInfoWebhook, InfoAlert, AnalyticAlert, WebhookType } from './notification_service'; import * as _ from 'lodash'; import * as AnalyticUnit from '../models/analytic_units'; import { Segment } from '../models/segment_model'; import { availableReporter } from '../utils/reporter'; export class Alert { public enabled = true; constructor(protected analyticUnit: AnalyticUnit.AnalyticUnit) {}; public receive(segment: Segment) { if(this.enabled) { const alert = this.makeAlert(segment); sendAnalyticWebhook(alert); } }; protected makeAlert(segment): AnalyticAlert { const alert: AnalyticAlert = { type: WebhookType.DETECT, analyticUnitType: this.analyticUnit.type, analyticUnitName: this.analyticUnit.name, analyticUnitId: this.analyticUnit.id, grafanaUrl: this.analyticUnit.grafanaUrl, from: segment.from, to: segment.to }; return alert; } } class PatternAlert extends Alert { private lastSentSegment: Segment; public receive(segment: Segment) { if(this.lastSentSegment === undefined || !segment.equals(this.lastSentSegment) ) { this.lastSentSegment = segment; if(this.enabled) { sendAnalyticWebhook(this.makeAlert(segment)); } } } }; class ThresholdAlert extends Alert { EXPIRE_PERIOD_MS = 60000; lastOccurence = 0; public receive(segment: Segment) { if(this.lastOccurence === 0) { this.lastOccurence = segment.from; if(this.enabled) { sendAnalyticWebhook(this.makeAlert(segment)); } } else { if(segment.from - this.lastOccurence > this.EXPIRE_PERIOD_MS) { if(this.enabled) { console.log(`time between threshold occurences ${segment.from - this.lastOccurence}ms, send alert`); sendAnalyticWebhook(this.makeAlert(segment)); } } this.lastOccurence = segment.from; } } } export class AlertService { private _alerts: { [id: string]: Alert; }; private _alertingEnable: boolean; private _grafanaAvailableReporter: Function; constructor() { this._alerts = {} } public receiveAlert(analyticUnit: AnalyticUnit.AnalyticUnit, segment: Segment) { if(!this._alertingEnable) { return; } let id = analyticUnit.id; if(!_.has(this._alerts, id)) { this.addAnalyticUnit(analyticUnit); } this._alerts[id].receive(segment); }; public sendMsg(message: string, type: WebhookType, optionalInfo = {}) { const now = Date.now(); const infoAlert: InfoAlert = { message, params: optionalInfo, type, from: now, to: now } sendInfoWebhook(infoAlert); } public getGrafanaAvailableReporter() { if(!this._grafanaAvailableReporter) { this._grafanaAvailableReporter = availableReporter( ['Grafana available', WebhookType.RECOVERY], ['Grafana unavailable for pulling data', WebhookType.FAILURE], this.sendMsg, this.sendMsg ); } return this._grafanaAvailableReporter; } public getAvailableWebhook(recoveryMsg: string, failureMsg: string) { return availableReporter( [recoveryMsg, WebhookType.RECOVERY], [failureMsg, WebhookType.FAILURE], this.sendMsg, this.sendMsg ); } public addAnalyticUnit(analyticUnit: AnalyticUnit.AnalyticUnit) { const detector = analyticUnit.detectorType; let alertsType = {}; alertsType[AnalyticUnit.DetectorType.THRESHOLD] = ThresholdAlert; alertsType[AnalyticUnit.DetectorType.PATTERN] = PatternAlert; this._alerts[analyticUnit.id] = new alertsType[detector](analyticUnit); } public removeAnalyticUnit(analyticUnitId: AnalyticUnit.AnalyticUnitId) { delete this._alerts[analyticUnitId]; } public stopAlerting() { this._alertingEnable = false; this._alerts = {}; } public startAlerting() { this._alertingEnable = true; } }