private boolean validateStepSize(File rrdFile) throws IOException { RrdDb r = null; try { r = new RrdDb(rrdFile.getAbsolutePath(), true); return (r.getRrdDef().getStep() == STEP_SIZE); } finally { if (r != null) r.close(); } }
@Override protected Object doPost(String account, String id, SampleRequest request) throws Exception { File parent = new File(dataDir, account + "/" + id); File file = new File(parent, "database.rrd"); if (!file.exists()) { throw new DatabaseDoesNotExistException(); } RrdDb db = new RrdDb(file.getPath()); Sample sample = db.createSample(Util.normalize(request.getTime(), db.getRrdDef().getStep())); for (Map.Entry<String, Double> entry : request.getValues().entrySet()) { sample.setValue(entry.getKey(), entry.getValue()); } sample.update(); db.close(); return null; }
/** * 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; }
@Override public byte[] createGraph( String metricName, String rrdFilename, long startTime, long endTime, String verticalAxisLabel, String title) throws IOException, MetricsGraphException { // 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."); } // Define attributes of the graph to be created for this metric RrdGraphDef graphDef = new RrdGraphDef(); graphDef.setTimeSpan(startTime, endTime); graphDef.setImageFormat("PNG"); graphDef.setShowSignature(false); graphDef.setStep(60); graphDef.setVerticalLabel(verticalAxisLabel); graphDef.setHeight(500); graphDef.setWidth(1000); graphDef.setTitle(title); DsType dataSourceType = rrdDb.getDatasource(0).getType(); // Determine if the Data Source for this RRD file is a COUNTER or GAUGE // (Need to know this because COUNTER data is averaged across samples and the vertical axis of // the // generated graph by default will show data per rrdStep interval) if (dataSourceType == DsType.COUNTER) { // If we ever needed to adjust the metric's data collected by RRD by the archive step // (which is the rrdStep * archiveSampleCount) this is how to do it. // FetchRequest fetchRequest = rrdDb.createFetchRequest(ConsolFun.AVERAGE, // startTime, endTime); // Archive archive = rrdDb.findMatchingArchive(fetchRequest); // long archiveStep = archive.getArcStep(); // LOGGER.debug("archiveStep = " + archiveStep); long rrdStep = rrdDb.getRrdDef().getStep(); LOGGER.debug("rrdStep = " + rrdStep); // Still TBD if we want to graph the AVERAGE data on the same graph // graphDef.comment(metricName + " "); // graphDef.datasource("myAverage", rrdFilename, "data", ConsolFun.AVERAGE); // graphDef.datasource("realAverage", "myAverage," + rrdStep + ",*"); // graphDef.line("realAverage", Color.GREEN, "Average", 2); // Multiplied by the rrdStep to "undo" the automatic averaging that RRD does // when it collects TOTAL data - we want the actual totals for the step, not // the average of the totals. graphDef.datasource("myTotal", rrdFilename, "data", ConsolFun.TOTAL); graphDef.datasource("realTotal", "myTotal," + rrdStep + ",*"); graphDef.line("realTotal", Color.BLUE, convertCamelCase(metricName), 2); // Add some spacing between the graph and the summary stats shown beneath the graph graphDef.comment("\\s"); graphDef.comment("\\s"); graphDef.comment("\\c"); // Average, Min, and Max over all of the TOTAL data - displayed at bottom of the graph graphDef.gprint("realTotal", ConsolFun.AVERAGE, "Average = %.3f%s"); graphDef.gprint("realTotal", ConsolFun.MIN, "Min = %.3f%s"); graphDef.gprint("realTotal", ConsolFun.MAX, "Max = %.3f%s"); } else if (dataSourceType == DsType.GAUGE) { graphDef.datasource("myAverage", rrdFilename, "data", ConsolFun.AVERAGE); graphDef.line("myAverage", Color.RED, convertCamelCase(metricName), 2); // Add some spacing between the graph and the summary stats shown beneath the graph graphDef.comment("\\s"); graphDef.comment("\\s"); graphDef.comment("\\c"); // Average, Min, and Max over all of the AVERAGE data - displayed at bottom of the graph graphDef.gprint("myAverage", ConsolFun.AVERAGE, "Average = %.3f%s"); graphDef.gprint("myAverage", ConsolFun.MIN, "Min = %.3f%s"); graphDef.gprint("myAverage", ConsolFun.MAX, "Max = %.3f%s"); } else { rrdDb.close(); throw new MetricsGraphException( "Unsupported data source type " + dataSourceType.name() + " in RRD file " + rrdFilename + ", only COUNTER and GAUGE data source types supported."); } rrdDb.close(); // Use "-" as filename so that RRD creates the graph only in memory (no file is // created, hence no file locking problems due to race conditions between multiple clients) graphDef.setFilename("-"); RrdGraph graph = new RrdGraph(graphDef); return graph.getRrdGraphInfo().getBytes(); }