Example #1
0
  /**
   * Retrieves the RRD stored data for the specified metric over the specified time range.
   *
   * @param rrdFilename the name of the RRD file containing the metric's data
   * @param startTime start time, in seconds since Unix epoch, to fetch metric's data
   * @param endTime end time, in seconds since Unix epoch, to fetch metric's data
   * @return domain object containing the metric's sampled data, which consists of the timestamps
   *     and their associated values, and the total count of the sampled data
   * @throws IOException
   * @throws MetricsGraphException
   */
  private MetricData getMetricData(String rrdFilename, long startTime, long endTime)
      throws IOException, MetricsGraphException {
    LOGGER.trace("ENTERING: getMetricData");

    // Create RRD DB in read-only mode for the specified RRD file
    RrdDb rrdDb = new RrdDb(rrdFilename, true);

    // Extract the data source (should always only be one data source per RRD file - otherwise we
    // have a problem)
    if (rrdDb.getDsCount() != 1) {
      throw new MetricsGraphException(
          "Only one data source per RRD file is supported - RRD file "
              + rrdFilename
              + " has "
              + rrdDb.getDsCount()
              + " data sources.");
    }

    // The step (sample) interval that determines how often RRD collects the metric's data
    long rrdStep = rrdDb.getRrdDef().getStep();

    // Retrieve the RRD file's data source type (COUNTER or GAUGE) to determine how (later)
    // to store the metric's data for presentation.
    DsType dataSourceType = rrdDb.getDatasource(0).getType();

    // Fetch the metric's data from the RRD file for the specified time range
    FetchRequest fetchRequest = rrdDb.createFetchRequest(ConsolFun.TOTAL, startTime, endTime);
    FetchData fetchData = fetchRequest.fetchData();
    long[] timestamps = fetchData.getTimestamps();
    double[] values = fetchData.getValues(0);

    // Done retrieving data from the RRD database - close it, otherwise no one else will
    // be able to access it later.
    rrdDb.close();

    // The lists of the metric's timestamps and their associated values that have non-"NaN" values
    List<Long> validTimestamps = new ArrayList<Long>();
    List<Double> validValues = new ArrayList<Double>();

    long totalCount = 0;
    MetricData metricData = new MetricData();

    if (dataSourceType == DsType.COUNTER) {
      // Counters are for constantly incrementing data, hence they can
      // have a summation of their totals
      metricData.setHasTotalCount(true);
      for (int i = 0; i < timestamps.length; i++) {
        // Filter out the RRD values that have not yet been sampled (they will
        // have been set to NaN as a placeholder when the RRD file was created)
        if (!Double.toString(values[i]).equals("NaN")) {
          // RRD averages the collected samples over the step interval.
          // To "undo" this averaging and get the actual count, need to
          // multiply the sampled data value by the RRD step interval.
          double nonAveragedValue = (double) (values[i] * rrdStep);
          validTimestamps.add(timestamps[i]);
          validValues.add(nonAveragedValue);
          totalCount += (long) nonAveragedValue;
        }
      }
    } else if (dataSourceType == DsType.GAUGE) {
      // Gauges are for data that waxes and wanes, hence no total count
      metricData.setHasTotalCount(false);
      for (int i = 0; i < timestamps.length; i++) {
        // Filter out the RRD values that have not yet been sampled (they will
        // have been set to NaN as a placeholder when the RRD file was created)
        if (!Double.toString(values[i]).equals("NaN")) {
          validTimestamps.add(timestamps[i]);
          validValues.add(values[i]);
        }
      }
    }

    metricData.setTimestamps(validTimestamps);
    metricData.setValues(validValues);
    metricData.setTotalCount(totalCount);

    LOGGER.trace("EXITING: getMetricData");

    return metricData;
  }