/** * Written by Gil Tene of Azul Systems, and released to the public domain, * as explained at http://creativecommons.org/publicdomain/zero/1.0/ * * @author Gil Tene */ package org.HdrHistogram_voltpatches; import javax.xml.bind.DatatypeConverter; import java.io.File; import java.io.FileNotFoundException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.Locale; import java.util.Scanner; import java.util.zip.DataFormatException; class AbstractHistogramLogReader { protected final Scanner scanner; private double startTimeSec = 0.0; /** * Constructs a new HistogramLogReader that produces intervals read from the specified file name. * @param inputFileName The name of the file to read from * @throws java.io.FileNotFoundException when unable to find inputFileName */ public AbstractHistogramLogReader(final String inputFileName) throws FileNotFoundException { scanner = new Scanner(new File(inputFileName)); initScanner(); } /** * Constructs a new HistogramLogReader that produces intervals read from the specified InputStream. * @param inputStream The InputStream to read from */ public AbstractHistogramLogReader(final InputStream inputStream) { scanner = new Scanner(inputStream); initScanner(); } /** * Constructs a new HistogramLogReader that produces intervals read from the specified file. * @param inputFile The File to read from * @throws java.io.FileNotFoundException when unable to find inputFile */ public AbstractHistogramLogReader(final File inputFile) throws FileNotFoundException { scanner = new Scanner(inputFile); initScanner(); } private void initScanner() { scanner.useLocale(Locale.US); scanner.useDelimiter("[ ,\\r\\n]"); } /** * get the latest start time found in the file so far (or 0.0), * per the log file format explained above. Assuming the "#[StartTime:" comment * line precedes the actual intervals recorded in the file, getStartTimeSec() can * be safely used after each interval is read to determine's the offset of that * interval's timestamp from the epoch. * @return latest Start Time found in the file (or 0.0 if non found) */ public double getStartTimeSec() { return startTimeSec; } protected void setStartTimeSec(double startTimeSec) { this.startTimeSec = startTimeSec; } /** * Read the next interval histogram from the log, if interval falls within a time range. * <p> * Returns a histogram object if an interval line was found with an * associated start timestamp value that falls between startTimeSec and * endTimeSec, or null if no such interval line is found. Note that * the range is assumed to be in seconds relative to the actual * timestamp value found in each interval line in the log, and not * in absolute time. * <p> * Timestamps are assumed to appear in order in the log file, and as such * this method will return a null upon encountering a timestamp larger than * rangeEndTimeSec. * <p> * The histogram returned will have it's timestamp set to the absolute * timestamp calculated from adding the interval's indicated timestamp * value to the latest [optional] start time found in the log. * <p> * Upon encountering any unexpected format errors in reading the next * interval from the file, this method will return a null. * @param startTimeSec The (non-absolute time) start of the expected * time range, in seconds. * @param endTimeSec The (non-absolute time) end of the expected time * range, in seconds. * @return a histogram, or a null if no appropriate interval found */ public EncodableHistogram nextIntervalHistogram(final Double startTimeSec, final Double endTimeSec) { return nextIntervalHistogram(startTimeSec, endTimeSec, false); } /** * Read the next interval histogram from the log, if interval falls within an absolute time range * <p> * Returns a histogram object if an interval line was found with an * associated absolute start timestamp value that falls between * absoluteStartTimeSec and absoluteEndTimeSec, or null if no such * interval line is found. * <p> * Timestamps are assumed to appear in order in the log file, and as such * this method will return a null upon encountering a timestamp larger than * rangeEndTimeSec. * <p> * The histogram returned will have it's timestamp set to the absolute * timestamp calculated from adding the interval's indicated timestamp * value to the latest [optional] start time found in the log. * <p> * Absolute timestamps are calculated by adding the timestamp found * with the recorded interval to the [latest, optional] start time * found in the log. The start time is indicated in the log with * a "#[StartTime: " followed by the start time in seconds. * <p> * Upon encountering any unexpected format errors in reading the next * interval from the file, this method will return a null. * @param absoluteStartTimeSec The (absolute time) start of the expected * time range, in seconds. * @param absoluteEndTimeSec The (absolute time) end of the expected * time range, in seconds. * @return A histogram, or a null if no appropriate interval found */ public EncodableHistogram nextAbsoluteIntervalHistogram(final Double absoluteStartTimeSec, final Double absoluteEndTimeSec) { return nextIntervalHistogram(absoluteStartTimeSec, absoluteEndTimeSec, true); } /** * Read the next interval histogram from the log. Returns a Histogram object if * an interval line was found, or null if not. * <p>Upon encountering any unexpected format errors in reading the next interval * from the file, this method will return a null. * @return a DecodedInterval, or a null if no appropriate interval found */ public EncodableHistogram nextIntervalHistogram() { return nextIntervalHistogram(0.0, Long.MAX_VALUE * 1.0, true); } private EncodableHistogram nextIntervalHistogram(final Double rangeStartTimeSec, final Double rangeEndTimeSec, boolean absolute) { while (scanner.hasNextLine()) { try { if (scanner.hasNext("\\#.*")) { // comment line if (scanner.hasNext("#\\[StartTime:")) { scanner.next("#\\[StartTime:"); if (scanner.hasNextDouble()) { setStartTimeSec(scanner.nextDouble()); // start time represented as seconds since epoch } } scanner.nextLine(); continue; } if (scanner.hasNext("\"StartTimestamp\".*")) { // Legend line scanner.nextLine(); continue; } // Decode: startTimestamp, intervalLength, maxTime, histogramPayload final double offsetStartTimeStampSec = scanner.nextDouble(); // Timestamp start is expect to be in seconds final double absoluteStartTimeStampSec = getStartTimeSec() + offsetStartTimeStampSec; final double intervalLengthSec = scanner.nextDouble(); // Timestamp length is expect to be in seconds final double offsetEndTimeStampSec = offsetStartTimeStampSec + intervalLengthSec; final double absoluteEndTimeStampSec = getStartTimeSec() + offsetEndTimeStampSec; final double startTimeStampToCheckRangeOn = absolute ? absoluteStartTimeStampSec : offsetStartTimeStampSec; if (startTimeStampToCheckRangeOn < rangeStartTimeSec) { scanner.nextLine(); continue; } if (startTimeStampToCheckRangeOn > rangeEndTimeSec) { return null; } scanner.nextDouble(); // Skip maxTime field, as max time can be deduced from the histogram. final String compressedPayloadString = scanner.next(); final ByteBuffer buffer = ByteBuffer.wrap( DatatypeConverter.parseBase64Binary(compressedPayloadString)); EncodableHistogram histogram = Histogram.decodeFromCompressedByteBuffer(buffer, 0); histogram.setStartTimeStamp((long) (absoluteStartTimeStampSec * 1000.0)); histogram.setEndTimeStamp((long) (absoluteEndTimeStampSec * 1000.0)); return histogram; } catch (java.util.NoSuchElementException ex) { return null; } catch (DataFormatException ex) { return null; } } return null; } }