/**
 * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */

package com.yahoo.ycsb.measurements;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.text.DecimalFormat;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;

import org.HdrHistogram.Histogram;
import org.HdrHistogram.HistogramLogWriter;
import org.HdrHistogram.Recorder;

import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter;

/**
 * Take measurements and maintain a HdrHistogram of a given metric, such as
 * READ LATENCY.
 *
 * @author nitsanw
 *
 */
public class OneMeasurementHdrHistogram extends OneMeasurement {

  // we need one log per measurement histogram
  final PrintStream log;
  final HistogramLogWriter histogramLogWriter;

  final Recorder histogram = new Recorder(3);

  Histogram totalHistogram;

  public OneMeasurementHdrHistogram(String name, Properties props) {
    super(name);
    boolean shouldLog = Boolean.parseBoolean(props.getProperty("hdrhistogram.fileoutput", "false"));
    if (!shouldLog) {
      log = null;
      histogramLogWriter = null;
      return;
    }
    try {
      final String hdrOutputFilename = props.getProperty("hdrhistogram.output.path", "") +name+".hdr";
      log = new PrintStream(new FileOutputStream(hdrOutputFilename), false);
    } catch (FileNotFoundException e) {
      throw new RuntimeException("Failed to open hdr histogram output file", e);
    }
    histogramLogWriter = new HistogramLogWriter(log);
    histogramLogWriter.outputComment("[Logging for: " + name + "]");
    histogramLogWriter.outputLogFormatVersion();
    histogramLogWriter.outputStartTime(System.currentTimeMillis());
    histogramLogWriter.outputLegend();
  }

  /**
    * It appears latency is reported in micros.
    * Using {@link Recorder} to support concurrent updates to histogram.
    *
    * @see com.yahoo.ycsb.OneMeasurement#measure(int)
    */
  public void measure(int latencyInMicros) {
    histogram.recordValue(latencyInMicros);
  }

  /**
    * This is called from a main thread, on orderly termination.
    *
    * @see com.yahoo.ycsb.measurements.OneMeasurement#exportMeasurements(com.yahoo.ycsb.measurements.exporter.MeasurementsExporter)
    */
  @Override
  public void exportMeasurements(MeasurementsExporter exporter) throws IOException {
    // accumulate the last interval which was not caught by status thread
    Histogram intervalHistogram = getIntervalHistogramAndAccumulate();
    if(histogramLogWriter != null) {
      histogramLogWriter.outputIntervalHistogram(intervalHistogram);
      // we can close now
      log.close();
    }
    exporter.write(getName(), "Operations", totalHistogram.getTotalCount());
    exporter.write(getName(), "AverageLatency(us)", totalHistogram.getMean());
    exporter.write(getName(), "MinLatency(us)", totalHistogram.getMinValue());
    exporter.write(getName(), "MaxLatency(us)", totalHistogram.getMaxValue());
    exporter.write(getName(), "95thPercentileLatency(ms)", totalHistogram.getValueAtPercentile(90)/1000);
    exporter.write(getName(), "99thPercentileLatency(ms)", totalHistogram.getValueAtPercentile(99)/1000);

    for (Map.Entry<Integer, AtomicInteger> entry : returncodes.entrySet()) {
      exporter.write(getName(), "Return=" + entry.getKey(), entry.getValue().get());
    }
  }

  /**
    * This is called periodically from the StatusThread. There's a single StatusThread per Client process.
    * We optionally serialize the interval to log on this opportunity.
    * @see com.yahoo.ycsb.measurements.OneMeasurement#getSummary()
    */
  @Override
  public String getSummary() {
    Histogram intervalHistogram = getIntervalHistogramAndAccumulate();
    // we use the summary interval as the histogram file interval.
    if(histogramLogWriter != null) {
      histogramLogWriter.outputIntervalHistogram(intervalHistogram);
    }

    DecimalFormat d = new DecimalFormat("#.##");
    return "[" + getName() +
            ": Count=" + intervalHistogram.getTotalCount() +
            ", Max=" + intervalHistogram.getMaxValue() +
            ", Min=" + intervalHistogram.getMinValue() +
            ", Avg=" + d.format(intervalHistogram.getMean()) +
            ", 90=" + d.format(intervalHistogram.getValueAtPercentile(90)) +
            ", 99=" + d.format(intervalHistogram.getValueAtPercentile(99)) +
            ", 99.9=" + d.format(intervalHistogram.getValueAtPercentile(99.9)) +
            ", 99.99=" + d.format(intervalHistogram.getValueAtPercentile(99.99)) +"]";
  }

  private Histogram getIntervalHistogramAndAccumulate() {
      Histogram intervalHistogram = histogram.getIntervalHistogram();
      // add this to the total time histogram.
      if (totalHistogram == null) {
        totalHistogram = intervalHistogram;
      }
      else {
        totalHistogram.add(intervalHistogram);
      }
      return intervalHistogram;
  }

}

@@ -1488,28 +1488,9 @@
 gram
- = new Recorder(3);%0A
+;
 %0A  H
@@ -1783,24 +1783,19 @@
     
-  return;%0A    %7D%0A
+%7D else %7B%0A  
     
@@ -1800,16 +1800,18 @@
   try %7B%0A
+  
       fi
@@ -1896,13 +1896,16 @@
 %22) +
+ 
 name
-+
+ + 
 %22.hd
@@ -1910,24 +1910,26 @@
 hdr%22;%0A      
+  
 log = new Pr
@@ -1991,16 +1991,18 @@
 e);%0A    
+  
 %7D catch 
@@ -2025,24 +2025,26 @@
 eption e) %7B%0A
+  
       throw 
@@ -2112,26 +2112,30 @@
 e%22, e);%0A    
-%7D%0A
+  %7D%0A  
     histogra
@@ -2176,24 +2176,26 @@
 r(log);%0A    
+ 
+ 
 histogramLog
@@ -2247,24 +2247,26 @@
 + %22%5D%22);%0A    
+  
 histogramLog
@@ -2306,72 +2306,141 @@
     
-histogramLogWriter.outputStartTime(System.currentTimeMillis()
+  long now = System.currentTimeMillis();%0A      histogramLogWriter.outputStartTime(now);%0A      histogramLogWriter.setBaseTime(now
 );%0A
+  
     
@@ -2474,16 +2474,55 @@
 gend();%0A
+    %7D%0A    histogram = new Recorder(3);%0A
   %7D%0A%0A  /

Fix recorder/logger initialization timing issue.