diff --git a/core/src/main/java/com/yahoo/ycsb/measurements/Measurements.java b/core/src/main/java/com/yahoo/ycsb/measurements/Measurements.java index 76ba40c1..faa81ba0 100644 --- a/core/src/main/java/com/yahoo/ycsb/measurements/Measurements.java +++ b/core/src/main/java/com/yahoo/ycsb/measurements/Measurements.java @@ -1,277 +1,283 @@ -/** - * 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. +/** + * 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.IOException; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; /** * Collects latency measurements, and reports them when requested. - * + * * @author cooperb * */ public class Measurements { - public static final String MEASUREMENT_TYPE_PROPERTY = "measurementtype"; - private static final String MEASUREMENT_TYPE_PROPERTY_DEFAULT = "histogram"; - public static final String MEASUREMENT_INTERVAL = "measurement.interval"; - private static final String MEASUREMENT_INTERVAL_DEFAULT = "op"; + public static final String MEASUREMENT_TYPE_PROPERTY = "measurementtype"; - static Measurements singleton=null; - - static Properties measurementproperties=null; - - public static void setProperties(Properties props) - { - measurementproperties=props; - } + private static final String MEASUREMENT_TYPE_PROPERTY_DEFAULT = "histogram"; + public static final String MEASUREMENT_INTERVAL = "measurement.interval"; + private static final String MEASUREMENT_INTERVAL_DEFAULT = "op"; - /** - * Return the singleton Measurements object. - */ - public synchronized static Measurements getMeasurements() - { - if (singleton==null) - { - singleton=new Measurements(measurementproperties); - } - return singleton; - } + static Measurements singleton=null; + static Properties measurementproperties=null; - final ConcurrentHashMap _opToMesurementMap; - final ConcurrentHashMap _opToIntendedMesurementMap; - final int _measurementType; - final int _measurementInterval; - private Properties _props; - - /** - * Create a new object with the specified properties. - */ - public Measurements(Properties props) - { - _opToMesurementMap=new ConcurrentHashMap(); - _opToIntendedMesurementMap=new ConcurrentHashMap(); - - _props=props; - - String mTypeString = _props.getProperty(MEASUREMENT_TYPE_PROPERTY, MEASUREMENT_TYPE_PROPERTY_DEFAULT); - if (mTypeString.equals("histogram")) - { - _measurementType = 0; - } - else if (mTypeString.equals("hdrhistogram")) - { - _measurementType = 1; - } - else if (mTypeString.equals("hdrhistogram+histogram")) - { - _measurementType = 2; - } - else if (mTypeString.equals("timeseries")) - { - _measurementType = 3; - } - else { - throw new IllegalArgumentException("unknown "+MEASUREMENT_TYPE_PROPERTY+"="+mTypeString); - } - - String mIntervalString = _props.getProperty(MEASUREMENT_INTERVAL, MEASUREMENT_INTERVAL_DEFAULT); - if (mIntervalString.equals("op")) - { - _measurementInterval = 0; - } - else if (mIntervalString.equals("intended")) - { - _measurementInterval = 1; - } - else if (mIntervalString.equals("both")) - { - _measurementInterval = 2; - } - else { - throw new IllegalArgumentException("unknown "+MEASUREMENT_INTERVAL+"="+mIntervalString); - } - - } - - OneMeasurement constructOneMeasurement(String name) + public static void setProperties(Properties props) + { + measurementproperties=props; + } + + /** + * Return the singleton Measurements object. + */ + public synchronized static Measurements getMeasurements() + { + if (singleton==null) { - switch (_measurementType) - { - case 0: - return new OneMeasurementHistogram(name, _props); - case 1: - return new OneMeasurementHdrHistogram(name, _props); - case 2: - return new TwoInOneMeasurement(name, - new OneMeasurementHdrHistogram("Hdr"+name, _props), - new OneMeasurementHistogram("Bucket"+name, _props)); - default: - return new OneMeasurementTimeSeries(name, _props); - } + singleton=new Measurements(measurementproperties); } + return singleton; + } + + final ConcurrentHashMap _opToMesurementMap; + final ConcurrentHashMap _opToIntendedMesurementMap; + final int _measurementType; + final int _measurementInterval; + private Properties _props; - static class StartTimeHolder{ - long time; - long startTime(){ - if(time == 0) { - return System.nanoTime(); - } - else { - return time; - } - } + /** + * Create a new object with the specified properties. + */ + public Measurements(Properties props) + { + _opToMesurementMap=new ConcurrentHashMap(); + _opToIntendedMesurementMap=new ConcurrentHashMap(); + + _props=props; + + String mTypeString = _props.getProperty(MEASUREMENT_TYPE_PROPERTY, MEASUREMENT_TYPE_PROPERTY_DEFAULT); + if (mTypeString.equals("histogram")) + { + _measurementType = 0; + } + else if (mTypeString.equals("hdrhistogram")) + { + _measurementType = 1; + } + else if (mTypeString.equals("hdrhistogram+histogram")) + { + _measurementType = 2; + } + else if (mTypeString.equals("timeseries")) + { + _measurementType = 3; } - ThreadLocal tlIntendedStartTime = new ThreadLocal(){ - protected StartTimeHolder initialValue() { - return new StartTimeHolder(); - }; + else { + throw new IllegalArgumentException("unknown "+MEASUREMENT_TYPE_PROPERTY+"="+mTypeString); + } + + String mIntervalString = _props.getProperty(MEASUREMENT_INTERVAL, MEASUREMENT_INTERVAL_DEFAULT); + if (mIntervalString.equals("op")) + { + _measurementInterval = 0; + } + else if (mIntervalString.equals("intended")) + { + _measurementInterval = 1; + } + else if (mIntervalString.equals("both")) + { + _measurementInterval = 2; + } + else { + throw new IllegalArgumentException("unknown "+MEASUREMENT_INTERVAL+"="+mIntervalString); + } + } + + OneMeasurement constructOneMeasurement(String name) + { + switch (_measurementType) + { + case 0: + return new OneMeasurementHistogram(name, _props); + case 1: + return new OneMeasurementHdrHistogram(name, _props); + case 2: + return new TwoInOneMeasurement(name, + new OneMeasurementHdrHistogram("Hdr"+name, _props), + new OneMeasurementHistogram("Bucket"+name, _props)); + default: + return new OneMeasurementTimeSeries(name, _props); + } + } + + static class StartTimeHolder { + long time; + + long startTime(){ + if(time == 0) { + return System.nanoTime(); + } + else { + return time; + } + } + } + + ThreadLocal tlIntendedStartTime = new ThreadLocal() { + protected StartTimeHolder initialValue() { + return new StartTimeHolder(); }; - public void setIntendedStartTimeNs(long time){ - if(_measurementInterval==0) - return; - tlIntendedStartTime.get().time=time; + }; + + public void setIntendedStartTimeNs(long time) { + if(_measurementInterval==0) + return; + tlIntendedStartTime.get().time=time; + } + + public long getIntendedtartTimeNs() { + if(_measurementInterval==0) + return 0L; + return tlIntendedStartTime.get().startTime(); + } + + /** + * Report a single value of a single metric. E.g. for read latency, operation="READ" and latency is the measured + * value. + */ + public void measure(String operation, int latency) + { + if(_measurementInterval==1) + return; + try + { + OneMeasurement m = getOpMeasurement(operation); + m.measure(latency); } - - public long getIntendedtartTimeNs(){ - if(_measurementInterval==0) - return 0L; - return tlIntendedStartTime.get().startTime(); + // This seems like a terribly hacky way to cover up for a bug in the measurement code + catch (java.lang.ArrayIndexOutOfBoundsException e) + { + System.out.println("ERROR: java.lang.ArrayIndexOutOfBoundsException - ignoring and continuing"); + e.printStackTrace(); + e.printStackTrace(System.out); } + } - /** - * Report a single value of a single metric. E.g. for read latency, operation="READ" and latency is the measured - * value. - */ - public void measure(String operation, int latency) - { - if(_measurementInterval==1) - return; - try - { - OneMeasurement m = getOpMeasurement(operation); - m.measure(latency); - } - // This seems like a terribly hacky way to cover up for a bug in the measurement code - catch (java.lang.ArrayIndexOutOfBoundsException e) - { - System.out.println("ERROR: java.lang.ArrayIndexOutOfBoundsException - ignoring and continuing"); - e.printStackTrace(); - e.printStackTrace(System.out); - } - } - /** - * Report a single value of a single metric. E.g. for read latency, operation="READ" and latency is the measured - * value. - */ - public void measureIntended(String operation, int latency) + /** + * Report a single value of a single metric. E.g. for read latency, operation="READ" and latency is the measured + * value. + */ + public void measureIntended(String operation, int latency) + { + if(_measurementInterval==0) + return; + try { - if(_measurementInterval==0) - return; - try - { - OneMeasurement m = getOpIntendedMeasurement(operation); - m.measure(latency); - } - // This seems like a terribly hacky way to cover up for a bug in the measurement code - catch (java.lang.ArrayIndexOutOfBoundsException e) - { - System.out.println("ERROR: java.lang.ArrayIndexOutOfBoundsException - ignoring and continuing"); - e.printStackTrace(); - e.printStackTrace(System.out); - } + OneMeasurement m = getOpIntendedMeasurement(operation); + m.measure(latency); } + // This seems like a terribly hacky way to cover up for a bug in the measurement code + catch (java.lang.ArrayIndexOutOfBoundsException e) + { + System.out.println("ERROR: java.lang.ArrayIndexOutOfBoundsException - ignoring and continuing"); + e.printStackTrace(); + e.printStackTrace(System.out); + } + } - private OneMeasurement getOpMeasurement(String operation) { - OneMeasurement m = _opToMesurementMap.get(operation); - if(m == null) - { - m = constructOneMeasurement(operation); - OneMeasurement oldM = _opToMesurementMap.putIfAbsent(operation, m); - if(oldM != null) - { - m = oldM; - } - } - return m; + private OneMeasurement getOpMeasurement(String operation) { + OneMeasurement m = _opToMesurementMap.get(operation); + if(m == null) + { + m = constructOneMeasurement(operation); + OneMeasurement oldM = _opToMesurementMap.putIfAbsent(operation, m); + if(oldM != null) + { + m = oldM; + } } - private OneMeasurement getOpIntendedMeasurement(String operation) { - OneMeasurement m = _opToIntendedMesurementMap.get(operation); - if(m == null) - { - final String name = _measurementInterval==1 ? operation : "Intended-" + operation; - m = constructOneMeasurement(name); - OneMeasurement oldM = _opToIntendedMesurementMap.putIfAbsent(operation, m); - if(oldM != null) - { - m = oldM; - } - } - return m; + return m; + } + + private OneMeasurement getOpIntendedMeasurement(String operation) { + OneMeasurement m = _opToIntendedMesurementMap.get(operation); + if(m == null) + { + final String name = _measurementInterval==1 ? operation : "Intended-" + operation; + m = constructOneMeasurement(name); + OneMeasurement oldM = _opToIntendedMesurementMap.putIfAbsent(operation, m); + if(oldM != null) + { + m = oldM; + } } - /** - * Report a return code for a single DB operation. - */ - public void reportReturnCode(String operation, int code) - { - OneMeasurement m = _measurementInterval==1 ? - getOpIntendedMeasurement(operation) : - getOpMeasurement(operation); - m.reportReturnCode(code); - } - + return m; + } + + /** + * Report a return code for a single DB operation. + */ + public void reportReturnCode(String operation, int code) + { + OneMeasurement m = _measurementInterval==1 ? + getOpIntendedMeasurement(operation) : + getOpMeasurement(operation); + m.reportReturnCode(code); + } + /** * Export the current measurements to a suitable format. - * + * * @param exporter Exporter representing the type of format to write to. * @throws IOException Thrown if the export failed. */ public void exportMeasurements(MeasurementsExporter exporter) throws IOException { - for (OneMeasurement measurement : _opToMesurementMap.values()) - { - measurement.exportMeasurements(exporter); - } - for (OneMeasurement measurement : _opToIntendedMesurementMap.values()) - { - measurement.exportMeasurements(exporter); - } + for (OneMeasurement measurement : _opToMesurementMap.values()) + { + measurement.exportMeasurements(exporter); + } + for (OneMeasurement measurement : _opToIntendedMesurementMap.values()) + { + measurement.exportMeasurements(exporter); + } + } + + /** + * Return a one line summary of the measurements. + */ + public synchronized String getSummary() + { + String ret=""; + for (OneMeasurement m : _opToMesurementMap.values()) + { + ret += m.getSummary()+" "; + } + for (OneMeasurement m : _opToIntendedMesurementMap.values()) + { + ret += m.getSummary()+" "; + } + return ret; } - - /** - * Return a one line summary of the measurements. - */ - public synchronized String getSummary() - { - String ret=""; - for (OneMeasurement m : _opToMesurementMap.values()) - { - ret+=m.getSummary()+" "; - } - for (OneMeasurement m : _opToIntendedMesurementMap.values()) - { - ret+=m.getSummary()+" "; - } - return ret; - } + } diff --git a/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurement.java b/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurement.java index 90720ef4..45c6a561 100644 --- a/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurement.java +++ b/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurement.java @@ -1,55 +1,76 @@ -/** - * 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.IOException; - -import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; - -/** - * A single measured metric (such as READ LATENCY) - */ -public abstract class OneMeasurement { - - String _name; - - public String getName() { - return _name; - } - - /** - * @param _name - */ - public OneMeasurement(String _name) { - this._name = _name; - } - - public abstract void reportReturnCode(int code); - - public abstract void measure(int latency); - - public abstract String getSummary(); - - /** - * Export the current measurements to a suitable format. - * - * @param exporter Exporter representing the type of format to write to. - * @throws IOException Thrown if the export failed. - */ - public abstract void exportMeasurements(MeasurementsExporter exporter) throws IOException; -} +/** + * 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.IOException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; + +/** + * A single measured metric (such as READ LATENCY) + */ +public abstract class OneMeasurement { + + String _name; + final ConcurrentHashMap returncodes; + + public String getName() { + return _name; + } + + /** + * @param _name + */ + public OneMeasurement(String _name) { + this._name = _name; + this.returncodes = new ConcurrentHashMap(); + } + + public abstract void measure(int latency); + + public abstract String getSummary(); + + /** + * No need for synchronization, using CHM to deal with that + */ + public void reportReturnCode(int code) { + Integer Icode = code; + AtomicInteger counter = returncodes.get(Icode); + + if (counter == null) { + AtomicInteger other = returncodes.putIfAbsent(Icode, counter = new AtomicInteger()); + if (other != null) { + counter = other; + } + } + + counter.incrementAndGet(); + } + + /** + * Export the current measurements to a suitable format. + * + * @param exporter Exporter representing the type of format to write to. + * @throws IOException Thrown if the export failed. + */ + public abstract void exportMeasurements(MeasurementsExporter exporter) throws IOException; + +} diff --git a/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementHdrHistogram.java b/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementHdrHistogram.java index fa0735f8..07316324 100644 --- a/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementHdrHistogram.java +++ b/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementHdrHistogram.java @@ -1,166 +1,147 @@ -/** - * 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.ConcurrentHashMap; -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); - final ConcurrentHashMap returncodes; - - Histogram totalHistogram; - - public OneMeasurementHdrHistogram(String name, Properties props) { - super(name); - returncodes = new ConcurrentHashMap(); - 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(); - } - - /** - * No need for synchronization, using CHM to deal with that - * - * @see com.yahoo.ycsb.OneMeasurement#reportReturnCode(int) - */ - public void reportReturnCode(int code) { - Integer Icode = code; - AtomicInteger counter = returncodes.get(Icode); - if (counter == null) { - AtomicInteger other = returncodes.putIfAbsent(Icode, counter = new AtomicInteger()); - if (other != null) { - counter = other; - } - } - - counter.incrementAndGet(); - } - - /** - * 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 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; - } - -} +/** + * 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 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; + } + +} diff --git a/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementHistogram.java b/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementHistogram.java index 2cba955a..d8305b30 100644 --- a/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementHistogram.java +++ b/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementHistogram.java @@ -1,165 +1,145 @@ -/** - * 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.IOException; -import java.text.DecimalFormat; -import java.util.HashMap; -import java.util.Properties; - -import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; - - -/** - * Take measurements and maintain a histogram of a given metric, such as READ LATENCY. - * - * @author cooperb - * - */ -public class OneMeasurementHistogram extends OneMeasurement -{ - public static final String BUCKETS="histogram.buckets"; - public static final String BUCKETS_DEFAULT="1000"; - - int _buckets; - int[] histogram; - int histogramoverflow; - int operations; - long totallatency; - - //keep a windowed version of these stats for printing status - int windowoperations; - long windowtotallatency; - - int min; - int max; - HashMap returncodes; - - public OneMeasurementHistogram(String name, Properties props) - { - super(name); - _buckets=Integer.parseInt(props.getProperty(BUCKETS, BUCKETS_DEFAULT)); - histogram=new int[_buckets]; - histogramoverflow=0; - operations=0; - totallatency=0; - windowoperations=0; - windowtotallatency=0; - min=-1; - max=-1; - returncodes=new HashMap(); - } - - /* (non-Javadoc) - * @see com.yahoo.ycsb.OneMeasurement#reportReturnCode(int) - */ - public synchronized void reportReturnCode(int code) - { - Integer Icode=code; - if (!returncodes.containsKey(Icode)) - { - int[] val=new int[1]; - val[0]=0; - returncodes.put(Icode,val); - } - returncodes.get(Icode)[0]++; - } - - - /* (non-Javadoc) - * @see com.yahoo.ycsb.OneMeasurement#measure(int) - */ - public synchronized void measure(int latency) - { - if (latency/1000>=_buckets) - { - histogramoverflow++; - } - else - { - histogram[latency/1000]++; - } - operations++; - totallatency+=latency; - windowoperations++; - windowtotallatency+=latency; - - if ( (min<0) || (latencymax) ) - { - max=latency; - } - } - - - @Override - public void exportMeasurements(MeasurementsExporter exporter) throws IOException - { - exporter.write(getName(), "Operations", operations); - exporter.write(getName(), "AverageLatency(us)", (((double)totallatency)/((double)operations))); - exporter.write(getName(), "MinLatency(us)", min); - exporter.write(getName(), "MaxLatency(us)", max); - - int opcounter=0; - boolean done95th=false; - for (int i=0; i<_buckets; i++) - { - opcounter+=histogram[i]; - if ( (!done95th) && (((double)opcounter)/((double)operations)>=0.95) ) - { - exporter.write(getName(), "95thPercentileLatency(ms)", i); - done95th=true; - } - if (((double)opcounter)/((double)operations)>=0.99) - { - exporter.write(getName(), "99thPercentileLatency(ms)", i); - break; - } - } - - for (Integer I : returncodes.keySet()) - { - int[] val=returncodes.get(I); - exporter.write(getName(), "Return="+I, val[0]); - } - - for (int i=0; i<_buckets; i++) - { - exporter.write(getName(), Integer.toString(i), histogram[i]); - } - exporter.write(getName(), ">"+_buckets, histogramoverflow); - } - - @Override - public String getSummary() { - if (windowoperations==0) - { - return ""; - } - DecimalFormat d = new DecimalFormat("#.##"); - double report=((double)windowtotallatency)/((double)windowoperations); - windowtotallatency=0; - windowoperations=0; - return "["+getName()+" AverageLatency(us)="+d.format(report)+"]"; - } - -} +/** + * 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.IOException; +import java.text.DecimalFormat; +import java.util.Properties; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; + + +/** + * Take measurements and maintain a histogram of a given metric, such as READ LATENCY. + * + * @author cooperb + * + */ +public class OneMeasurementHistogram extends OneMeasurement +{ + public static final String BUCKETS="histogram.buckets"; + public static final String BUCKETS_DEFAULT="1000"; + + int _buckets; + int[] histogram; + int histogramoverflow; + int operations; + long totallatency; + + //keep a windowed version of these stats for printing status + int windowoperations; + long windowtotallatency; + + int min; + int max; + + public OneMeasurementHistogram(String name, Properties props) + { + super(name); + _buckets=Integer.parseInt(props.getProperty(BUCKETS, BUCKETS_DEFAULT)); + histogram=new int[_buckets]; + histogramoverflow=0; + operations=0; + totallatency=0; + windowoperations=0; + windowtotallatency=0; + min=-1; + max=-1; + } + + /* (non-Javadoc) + * @see com.yahoo.ycsb.OneMeasurement#measure(int) + */ + public synchronized void measure(int latency) + { + if (latency/1000>=_buckets) + { + histogramoverflow++; + } + else + { + histogram[latency/1000]++; + } + operations++; + totallatency+=latency; + windowoperations++; + windowtotallatency+=latency; + + if ( (min<0) || (latencymax) ) + { + max=latency; + } + } + + @Override + public void exportMeasurements(MeasurementsExporter exporter) throws IOException + { + exporter.write(getName(), "Operations", operations); + exporter.write(getName(), "AverageLatency(us)", (((double)totallatency)/((double)operations))); + exporter.write(getName(), "MinLatency(us)", min); + exporter.write(getName(), "MaxLatency(us)", max); + + int opcounter=0; + boolean done95th=false; + for (int i=0; i<_buckets; i++) + { + opcounter+=histogram[i]; + if ( (!done95th) && (((double)opcounter)/((double)operations)>=0.95) ) + { + exporter.write(getName(), "95thPercentileLatency(ms)", i); + done95th=true; + } + if (((double)opcounter)/((double)operations)>=0.99) + { + exporter.write(getName(), "99thPercentileLatency(ms)", i); + break; + } + } + + for (Map.Entry entry : returncodes.entrySet()) { + exporter.write(getName(), "Return=" + entry.getKey(), entry.getValue().get()); + } + + for (int i=0; i<_buckets; i++) + { + exporter.write(getName(), Integer.toString(i), histogram[i]); + } + exporter.write(getName(), ">"+_buckets, histogramoverflow); + } + + @Override + public String getSummary() { + if (windowoperations==0) + { + return ""; + } + DecimalFormat d = new DecimalFormat("#.##"); + double report=((double)windowtotallatency)/((double)windowoperations); + windowtotallatency=0; + windowoperations=0; + return "["+getName()+" AverageLatency(us)="+d.format(report)+"]"; + } +} diff --git a/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementTimeSeries.java b/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementTimeSeries.java index 25c77b76..d4871660 100644 --- a/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementTimeSeries.java +++ b/core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementTimeSeries.java @@ -1,179 +1,163 @@ -/** - * 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.IOException; -import java.text.DecimalFormat; -import java.util.HashMap; -import java.util.Properties; -import java.util.Vector; - -import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; - -class SeriesUnit -{ - /** - * @param time - * @param average - */ - public SeriesUnit(long time, double average) { - this.time = time; - this.average = average; - } - public long time; - public double average; -} - -/** - * A time series measurement of a metric, such as READ LATENCY. - */ -public class OneMeasurementTimeSeries extends OneMeasurement -{ - /** - * Granularity for time series; measurements will be averaged in chunks of this granularity. Units are milliseconds. - */ - public static final String GRANULARITY="timeseries.granularity"; - - public static final String GRANULARITY_DEFAULT="1000"; - - int _granularity; - Vector _measurements; - - long start=-1; - long currentunit=-1; - int count=0; - int sum=0; - int operations=0; - long totallatency=0; - - //keep a windowed version of these stats for printing status - int windowoperations=0; - long windowtotallatency=0; - - int min=-1; - int max=-1; - - private HashMap returncodes; - - public OneMeasurementTimeSeries(String name, Properties props) - { - super(name); - _granularity=Integer.parseInt(props.getProperty(GRANULARITY,GRANULARITY_DEFAULT)); - _measurements=new Vector(); - returncodes=new HashMap(); - } - - void checkEndOfUnit(boolean forceend) - { - long now=System.currentTimeMillis(); - - if (start<0) - { - currentunit=0; - start=now; - } - - long unit=((now-start)/_granularity)*_granularity; - - if ( (unit>currentunit) || (forceend) ) - { - double avg=((double)sum)/((double)count); - _measurements.add(new SeriesUnit(currentunit,avg)); - - currentunit=unit; - - count=0; - sum=0; - } - } - - @Override - public void measure(int latency) - { - checkEndOfUnit(false); - - count++; - sum+=latency; - totallatency+=latency; - operations++; - windowoperations++; - windowtotallatency+=latency; - - if (latency>max) - { - max=latency; - } - - if ( (latency _measurements; + + long start=-1; + long currentunit=-1; + int count=0; + int sum=0; + int operations=0; + long totallatency=0; + + //keep a windowed version of these stats for printing status + int windowoperations=0; + long windowtotallatency=0; + + int min=-1; + int max=-1; + + public OneMeasurementTimeSeries(String name, Properties props) + { + super(name); + _granularity=Integer.parseInt(props.getProperty(GRANULARITY,GRANULARITY_DEFAULT)); + _measurements=new Vector(); + } + + void checkEndOfUnit(boolean forceend) + { + long now=System.currentTimeMillis(); + + if (start<0) + { + currentunit=0; + start=now; + } + + long unit=((now-start)/_granularity)*_granularity; + + if ( (unit>currentunit) || (forceend) ) + { + double avg=((double)sum)/((double)count); + _measurements.add(new SeriesUnit(currentunit,avg)); + + currentunit=unit; + + count=0; + sum=0; + } + } + + @Override + public void measure(int latency) + { + checkEndOfUnit(false); + + count++; + sum+=latency; + totallatency+=latency; + operations++; + windowoperations++; + windowtotallatency+=latency; + + if (latency>max) + { + max=latency; + } + + if ( (latency entry : returncodes.entrySet()) { + exporter.write(getName(), "Return=" + entry.getKey(), entry.getValue().get()); + } + + for (SeriesUnit unit : _measurements) + { + exporter.write(getName(), Long.toString(unit.time), unit.average); + } + } + + @Override + public String getSummary() { + if (windowoperations==0) + { + return ""; + } + DecimalFormat d = new DecimalFormat("#.##"); + double report=((double)windowtotallatency)/((double)windowoperations); + windowtotallatency=0; + windowoperations=0; + return "["+getName()+" AverageLatency(us)="+d.format(report)+"]"; + } + +} diff --git a/core/src/main/java/com/yahoo/ycsb/measurements/TwoInOneMeasurement.java b/core/src/main/java/com/yahoo/ycsb/measurements/TwoInOneMeasurement.java index c48a3f77..0a191e4e 100644 --- a/core/src/main/java/com/yahoo/ycsb/measurements/TwoInOneMeasurement.java +++ b/core/src/main/java/com/yahoo/ycsb/measurements/TwoInOneMeasurement.java @@ -1,79 +1,81 @@ -/** - * 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. +/** + * 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.IOException; import org.HdrHistogram.Recorder; import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; /** * delegates to 2 measurement instances. * @author nitsanw * */ public class TwoInOneMeasurement extends OneMeasurement { - final OneMeasurement thing1,thing2; - public TwoInOneMeasurement(String name, OneMeasurement thing1,OneMeasurement thing2) { - super(name); - this.thing1 = thing1; - this.thing2 = thing2; - } - /** - * No need for synchronization, using CHM to deal with that - * - * @see com.yahoo.ycsb.OneMeasurement#reportReturnCode(int) - */ - public void reportReturnCode(int code) { - thing1.reportReturnCode(code); - } + final OneMeasurement thing1,thing2; + public TwoInOneMeasurement(String name, OneMeasurement thing1,OneMeasurement thing2) { + super(name); + this.thing1 = thing1; + this.thing2 = thing2; + } + + /** + * No need for synchronization, using CHM to deal with that + * + * @see com.yahoo.ycsb.OneMeasurement#reportReturnCode(int) + */ + public void reportReturnCode(int code) { + thing1.reportReturnCode(code); + } + + /** + * 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) { + thing1.measure(latencyInMicros); + thing2.measure(latencyInMicros); + } - /** - * 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) { - thing1.measure(latencyInMicros); - thing2.measure(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 { + thing1.exportMeasurements(exporter); + thing2.exportMeasurements(exporter); + } - /** - * 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 { - thing1.exportMeasurements(exporter); - thing2.exportMeasurements(exporter); - } + /** + * 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() { + return thing1.getSummary() + "\n" + thing2.getSummary(); + } - /** - * 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() { - return thing1.getSummary() + "\n" + thing2.getSummary(); - } } diff --git a/core/src/main/java/com/yahoo/ycsb/measurements/exporter/JSONArrayMeasurementsExporter.java b/core/src/main/java/com/yahoo/ycsb/measurements/exporter/JSONArrayMeasurementsExporter.java index b8466cfc..555fb2d4 100644 --- a/core/src/main/java/com/yahoo/ycsb/measurements/exporter/JSONArrayMeasurementsExporter.java +++ b/core/src/main/java/com/yahoo/ycsb/measurements/exporter/JSONArrayMeasurementsExporter.java @@ -1,73 +1,72 @@ /** * Copyright (c) 2015 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.exporter; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.util.DefaultPrettyPrinter; /** * Export measurements into a machine readable JSON Array of measurement objects. */ public class JSONArrayMeasurementsExporter implements MeasurementsExporter { private JsonFactory factory = new JsonFactory(); private JsonGenerator g; public JSONArrayMeasurementsExporter(OutputStream os) throws IOException { - BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os)); g = factory.createJsonGenerator(bw); g.setPrettyPrinter(new DefaultPrettyPrinter()); g.writeStartArray(); } public void write(String metric, String measurement, int i) throws IOException { g.writeStartObject(); g.writeStringField("metric", metric); g.writeStringField("measurement", measurement); g.writeNumberField("value", i); g.writeEndObject(); } public void write(String metric, String measurement, double d) throws IOException { g.writeStartObject(); g.writeStringField("metric", metric); g.writeStringField("measurement", measurement); g.writeNumberField("value", d); g.writeEndObject(); } public void close() throws IOException { if (g != null) { g.writeEndArray(); g.close(); } } }