/** * 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; }