Name

sn_app_insights.MetricConditionUtils

Description

No description available

Script

var MetricConditionUtils = Class.create();
MetricConditionUtils.prototype = {
  initialize: function() {
  	this.JOB_ID = '8eccc01723612010fb0c949e27bf6594';
  },

  evaluateInnerCondition: function (innerCondition) {
  	var results = {};

  	//Using the example above, an inner condition would be [[a], [b, c]] or [[d], [e]]
  	//All items in the inner condition must pass for the innerCondition to pass since it is an AND
  	for (var i = 0; i < innerCondition.length; ++i) {
  		var childCondition = innerCondition[i];

  		//Again using the example, one childCondition might be [b, c]
  		//Only one item in the child condition needs to pass, because it is an OR
  		for (var j = 0; j < childCondition.length; ++j) {
  			var trigger = new GlideRecord('sn_app_insights_metric_trigger');
  			if (trigger.get(childCondition[j])) {
  				var result = this._evaluateTrigger(trigger);
  				if (result.evaluation) {
  					result.metric_id = trigger.getValue('metric');
  					delete result.evaluation;

  					//Assign to dictionary to prevent duplicate logs
  					results[result.metric_id] = result;
  					break;
  				}
  			}
  		}
  		if (Object.keys(results).length === 0)
  			return { evaluation: false };
  	}

  	//Put all log objects into an array for easy retrieval
  	var finalResult = [];
  	for (var key in results)
  		finalResult.push(results[key]);

  	return { result: true, records: finalResult };
  },

  _evaluateTrigger: function (triggerGr) {
  	var job = new GlideRecord('sysauto_script');
  	job.get(this.JOB_ID);

  	var end = new GlideDateTime();
  	var start = new GlideDateTime();
  	var sustained = new GlideDateTime(triggerGr.getValue('sustained_for'));
  	var period = new GlideDateTime(job.getValue('run_period'));

  	var tableGr = new GlideRecord(triggerGr.metric.name);
  	tableGr.query();

  	var transformer = new sn_clotho.Transformer(tableGr);
  	var resultArray;

  	var operation = triggerGr.getValue('operation');

  	if (sustained.getNumericValue() == 0) {
  		//If sustained_for is 0, no need for resampling. Get every point of data since the last run period
  		transformer.metric(triggerGr.metric.element);
  		start.subtract(period.getNumericValue());
  		resultArray = transformer.execute(start, end).toArray();
  	} else {
  		/*
  			Determine the range of sampling. 
  			Take the larger of the script's run period and the metric's "sustained for" value.
  		*/
  		if (period.after(sustained)) {
  			var periodMillis = period.getNumericValue();
  			var sustainedMillis = sustained.getNumericValue();

  			/*
  				If sustained_for does not fit neatly into the run period of the job 
  				(e.g. 7 minute sustained for, 10 minute run period)
  				Lengthen the range of data to pull to ensure an even split (instead of 10 minutes, make it 14)
  			*/
  			if (periodMillis % sustainedMillis != 0) {
  				var calculatedStart = (periodMillis / sustainedMillis + 1) * sustainedMillis;
  				start.subtract(calculatedStart);
  			} else
  				start.subtract(periodMillis);
  		} else
  			start.subtract(sustained.getNumericValue());

  		var operand = '';
  		if (operation.startsWith('<'))
  			operand = 'MAX';
  		else
  			operand = 'MIN';

  		transformer
  			.metric(triggerGr.metric.element)
  			.resample(
  				operand, 
  			new GlideDateTime(triggerGr.getValue('sustained_for')).getNumericValue()
  		);

  		resultArray = transformer.execute(start, end).toArray();
  	}
  	var threshold = parseFloat(triggerGr.getValue('threshold'));

  	for (var i = 0; i < resultArray.length; ++i) {
  		var vals = resultArray[i].getValues();
  		for (var j = 0; j < vals.length; ++j) {

  			var evaluation = false;
  			var value = parseFloat(vals[j]);

  			switch (operation) {
  				case 'lt':
  					evaluation = value < threshold;
  					break;
  				case 'lte':
  					evaluation = value <= threshold;
  					break;
  				case 'gt':
  					evaluation = value > threshold;
  					break;
  				case 'gte':
  					evaluation = value >= threshold;
  					break;
  				default:
  					return false;
  			}

  			if (evaluation) {
  				return {
  					evaluation: true,
  					record_table: resultArray[i].getTableName(),
  					record_id: resultArray[i].getSubject(),
  					value: value
  				};
  			}
  		}
  	}
  	return {evaluation: false};
  },

  parseCondition: function (thresholdGr) {
  	var condition = thresholdGr.getValue('condition');
  	var ids = condition.split(/\^/);
  	var parsedCondition = [];
  	var currentCondition = [];

  	/*
  		parsedCondition is a computer readable version of the trigger's condition string
  		in the form of an array

  		Example:
  		The condition string is 'a^b^ORc^NQd^e' where a, b, c, d, e are triggers.
  		This condition can be re-written as (a && (b || c)) || (d && e).
  		parsedCondition will be [[[a], [b,c]], [[d], [e]]]
  	*/

  	for (var i = 0; i < ids.length; ++i) {
  		var val = ids[i];
  		if (val.startsWith('OR')) {
  			currentCondition[currentCondition.length - 1].push(val.replace('OR', ''));
  			continue;
  		}
  		if (val.startsWith('NQ')) {
  			parsedCondition.push(currentCondition);
  			currentCondition = [];
  		}
  		currentCondition.push([]);
  		currentCondition[currentCondition.length - 1].push(val.replace('NQ', ''));
  	}

  	parsedCondition.push(currentCondition);
  	return parsedCondition;
  },

  /*
  	Returns an object of triggers that is readable by the Thresholds UI.
  	Instead of an array that is easy to parse quickly, this object is easily displayed to the user

  	Example:
  	The condition string is 'a^b^ORc^NQd^e' where a, b, c, d, e are triggers.
  	Each of the triggers would become its own object with a condition (>, <, <=, etc..), id, and other attributes
  	Each of these objects represents a line in the UI (e.g. <metric> greater than <threshold> <sustained_for>)
  */
  parseConditionExternal: function (thresholdGr) {
  	var condition = thresholdGr.getValue('condition');
  	var ids = condition.split(/\^/);
  	var triggers = []; 

  	for (var i = 0; i < ids.length; ++i) {
  		var trigger = {};
  		var id = ids[i];

  		if (id.startsWith('OR'))
  			trigger.condition = '^OR';
  		else if (id.startsWith('NQ'))
  			trigger.condition = '^NQ';
  		else if (i != 0)
  			trigger.condition = '^';

  		id = id.replace(/^[A-Z]*/, '');
  		trigger.id = id;

  		triggers.push(trigger);
  	}

  	return triggers;
  },
  type: 'MetricConditionUtils'
};

Sys ID

8088e390ff432010fb0cffb0653bf1ac

Offical Documentation

Official Docs: