Name

sn_app_insights.MultiMetricDataRetriever

Description

No description available

Script

var MultiMetricDataRetriever = Class.create();
MultiMetricDataRetriever.prototype = {
  initialize: function() {
      this.X_AXIS = "x";
      this.X_AXIS_KEY = "timeStamp";
      this.Y_AXIS = "y";
      this.TYPE = "type";
      this.LINE_TYPE = "line";
      this.LABEL = "label";
      this.dataFromMetricBase = [];

      this.SYS_CLUSTER_STATE = "sys_cluster_state";
  },

  getMultiMetricDataPayload: function(reqMetrics, secondsInTimeWindow) {
      var start = new GlideDateTime();
      start.addSeconds(-1 * secondsInTimeWindow);

      var graphPayload = {};
      var metricObjects = this._getMetricObjects(reqMetrics);
      this._readyDataFromMetricBase(metricObjects, secondsInTimeWindow);
      graphPayload.seriesConfig = this._seriesConfig(metricObjects, this.dataFromMetricBase);
      graphPayload.seriesData = this._seriesData(this.dataFromMetricBase);

      var thresholdUtils = new ThresholdUtils();
      graphPayload["thresholdConfig"] = thresholdUtils.buildThresholdConfig();
      graphPayload["thresholdData"] = this._addQueueDepthLimitThreshold(reqMetrics);

      var annotationsDataUtils = new AnnotationsDataUtils();
      graphPayload["annotationsConfig"] = annotationsDataUtils._getAnnotationsConfig();
      var annotationsData = annotationsDataUtils._getAnnotationsData(start);
      graphPayload["annotationsData"] = annotationsData.data;
      graphPayload["annotationsMetadata"] = annotationsData.counts;

      return graphPayload;
  },

  _readyDataFromMetricBase: function(reqMetrics, secondsInTimeWindow) {
      for (var i = 0; i < reqMetrics.length; ++i)
          this.dataFromMetricBase.push(this._getDataForMetric(reqMetrics[i], secondsInTimeWindow, reqMetrics[i].averageMetrics));
  },

  _getDataForMetric: function(metric, secondsInTimeWindow, averageMetrics) {
      var end = new GlideDateTime();
      var start = new GlideDateTime(end);
      start.addSeconds(-1 * secondsInTimeWindow);
      var metricSubjects = new GlideRecord(metric.tableName);

      if (metric.tableName == this.SYS_CLUSTER_STATE)
          metricSubjects.addQuery("status", "online");

      metricSubjects.query();

      var resamplePeriodDuration;
      if (metric.tableName == this.SYS_CLUSTER_STATE) {
          var scheduledJob = new GlideRecord("sysauto_script");
          scheduledJob.get("8c94b336c37310107f5633f6bb40dddc");
          resamplePeriodDuration = new GlideDuration(scheduledJob.run_period.dateNumericValue());
      } else
          resamplePeriodDuration = new GlideDuration(5 * 60 * 1000);

      var transformer = new sn_clotho.Transformer(metricSubjects);

      switch (metric.aggregate) {
          case 'average':
              transformer.metric(metric.metricName).avg().resample("AVG", resamplePeriodDuration);
              break;
          case 'maximum':
              transformer.metric(metric.metricName).max().resample("MAX", resamplePeriodDuration);
              break;
          case 'minimum':
              transformer.metric(metric.metricName).min().resample("MIN", resamplePeriodDuration);
              break;
          default:
              transformer.metric(metric.metricName).sum().resample("AVG", resamplePeriodDuration);
      }

      var resultArray = transformer.execute(start, end).toArray();

      return resultArray;
  },

  _seriesConfig: function(reqMetrics, resultArray) {
      var seriesConfig = {};
      for (var i = 0; i < resultArray.length; i++) {
          var singleLineConfig = {};
          singleLineConfig[this.X_AXIS] = this.X_AXIS_KEY;
          singleLineConfig[this.Y_AXIS] = this.Y_AXIS + i;
          singleLineConfig[this.TYPE] = this.LINE_TYPE;
          singleLineConfig[this.LABEL] = reqMetrics[i].label;

          seriesConfig[reqMetrics[i].metricName] = singleLineConfig;
      }

      return seriesConfig;
  },

  _seriesData: function(resultArray) {
      if (resultArray.length == 0 )
          return [];

      var index = 0;
      //find first non zero length second dimension
      for (index; index < resultArray.length; index++) {
          if(resultArray[index].length != 0)
              break;
      }

      if(index >= resultArray.length)
          return [];//no series had entries

      var seriesData = this._populateSeriesDataSkeletonWithTimeStamps(resultArray[index][0]);
      for (var lineNumber = 0; lineNumber < resultArray.length; lineNumber++) {
          if (!resultArray[lineNumber] || !resultArray[lineNumber][0])
              continue;

          var valuesOfSingleLine = resultArray[lineNumber][0].getValues();
          for (var dataPointIndex = 0; dataPointIndex < seriesData.length; dataPointIndex++) {
              var pointInTime = seriesData[dataPointIndex];

              // The NaN object gets turned to a string when it exists in the return, but null doesn't. So
              // make all NaN objects null objects so the front-end can process it correctly.
              var metricValue = valuesOfSingleLine[dataPointIndex];
              pointInTime[this.Y_AXIS + lineNumber] = isNaN(metricValue) ? null : valuesOfSingleLine[dataPointIndex];
          }
      }

      return seriesData;
  },

  _populateSeriesDataSkeletonWithTimeStamps: function(singleResultArray) {
      // Create new gdt so we don't modify the original in the result
      var indexDateTime = new GlideDateTime(singleResultArray.getStart());
      var seriesDataSkeleton = [];
      var valueArray = singleResultArray.getValues();
      for (var i = 0; i < valueArray.length; i++) {
          var localTimestamp = new GlideDateTime();
          localTimestamp.setValue(indexDateTime.getDisplayValueInternal());
          seriesDataSkeleton.push({
              "timeStamp": localTimestamp.getNumericValue()
          });

          // Each value in the valueArray is separated from the previous value by an amount
          // of time (ex: 300 seconds), which is the time period. So to get the time stamp for the
          // next value, we need to increment by the period to get the time stamp related to the value
          indexDateTime.addSeconds(singleResultArray.getPeriod());
      }

      return seriesDataSkeleton;
  },

  _getMetricObjects: function(reqMetrics) {
      var metricsObject = [];

      var graphSet = (reqMetrics).graphs;
      graphSet.forEach(function(graph) {
          var grMetric = new GlideRecord('sys_metric');
          grMetric.addQuery('element', graph.metricName);
          grMetric.addQuery('name', graph.tableName);
          grMetric.query();

          if (grMetric.next()) {
              var label = grMetric.getValue('label');
              label = label.replace(/^Metric - /, '');

              metricsObject.push({
                  tableName: graph.tableName,
                  metricName: graph.metricName,
                  label: label,
                  aggregate: graph.aggregate,
              });
          }
      });

      return metricsObject;
  },

  _addQueueDepthLimitThreshold: function(reqMetrics) {
      // NOTE: This method will only find the first metric with a defined depth limit, 
      // so it is not advised to mix multiple semaphore sets in a single reqMetrics object.
      var thresholdData = [];
      if (!reqMetrics)
          return thresholdData;

      var thresholdUtils = new ThresholdUtils();
      var metrics = reqMetrics.graphs;
      for (var i in metrics) {
          var metricName = metrics[i]['metricName'];
          thresholdUtils.addQueueDepthLimitThreshold(thresholdData, metricName);

          if (thresholdData.length != 0)
              return thresholdData;
      }

      return thresholdData;
  },

  type: 'MultiMetricDataRetriever'
};

Sys ID

b8486de8b7d1201080a89078ee11a962

Offical Documentation

Official Docs: