diff options
Diffstat (limited to 'simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/SnapshotConverter.java')
-rw-r--r-- | simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/SnapshotConverter.java | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/SnapshotConverter.java b/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/SnapshotConverter.java new file mode 100644 index 00000000000..6d3b6ad6243 --- /dev/null +++ b/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/SnapshotConverter.java @@ -0,0 +1,212 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.metrics.simple.jdisc; + +import java.io.PrintStream; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import org.HdrHistogram.DoubleHistogram; + +import com.yahoo.collections.Tuple2; +import com.yahoo.container.jdisc.state.*; +import com.yahoo.metrics.simple.Bucket; +import com.yahoo.metrics.simple.Identifier; +import com.yahoo.metrics.simple.Point; +import com.yahoo.metrics.simple.UntypedMetric; +import com.yahoo.metrics.simple.Value; +import com.yahoo.text.JSON; + +/** + * Convert simple metrics snapshots into jdisc state snapshots. + * + * @author arnej27959 + */ +class SnapshotConverter { + final Bucket snapshot; + final Map<Point, Map<String, MetricValue>> perPointData = new HashMap<>(); + private static final char[] DIGITS = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + private Map<String, MetricValue> getMap(Point point) { + if (! perPointData.containsKey(point)) { + perPointData.put(point, new HashMap<String, MetricValue>()); + } + return perPointData.get(point); + } + + public SnapshotConverter(Bucket snapshot) { + this.snapshot = snapshot; + } + + static MetricDimensions convert(Point p) { + if (p == null) { + return StateMetricContext.newInstance(null); + } + List<String> dimensions = p.dimensions(); + List<Value> location = p.location(); + Map<String, Object> pointWrapper = new HashMap<>(dimensions.size()); + for (int i = 0; i < dimensions.size(); ++i) { + pointWrapper.put(dimensions.get(i), valueAsString(location.get(i))); + } + return StateMetricContext.newInstance(pointWrapper); + } + + // TODO: just a compatibility wrapper, should be removed ASAP + private static Object valueAsString(Value value) { + switch (value.getType()) { + case STRING: + return value.stringValue(); + case LONG: + return Long.valueOf(value.longValue()); + case DOUBLE: + return Double.valueOf(value.doubleValue()); + default: + throw new IllegalStateException("simplemetrics impl is out of sync with itself, please file a ticket."); + } + } + + + static MetricValue convert(UntypedMetric val) { + if (val.isCounter()) { + return CountMetric.newInstance(val.getCount()); + } else { + if (val.getHistogram() == null) { + return GaugeMetric.newInstance(val.getLast(), val.getMax(), val.getMin(), val.getSum(), val.getCount()); + } else { + return GaugeMetric.newInstance(val.getLast(), val.getMax(), val.getMin(), val.getSum(), val.getCount(), + Optional.of(buildPercentileList(val.getHistogram()))); + } + } + } + + private static List<Tuple2<String, Double>> buildPercentileList(DoubleHistogram histogram) { + final List<Tuple2<String, Double>> prefixAndValues = new ArrayList<>(2); + prefixAndValues.add(new Tuple2<>("95", histogram.getValueAtPercentile(95.0d))); + prefixAndValues.add(new Tuple2<>("99", histogram.getValueAtPercentile(99.0d))); + return prefixAndValues; + } + + MetricSnapshot convert() { + for (Map.Entry<Identifier, UntypedMetric> entry : snapshot.entrySet()) { + Identifier ident = entry.getKey(); + getMap(ident.getLocation()).put(ident.getName(), convert(entry.getValue())); + } + Map<MetricDimensions, MetricSet> data = new HashMap<>(); + for (Map.Entry<Point, Map<String, MetricValue>> entry : perPointData.entrySet()) { + data.put(convert(entry.getKey()), new MetricSet(entry.getValue())); + } + return new MetricSnapshot(snapshot.getFromMillis(), + snapshot.getToMillis(), + TimeUnit.MILLISECONDS, + data); + } + + void outputHistograms(PrintStream output) { + boolean gotHistogram = false; + for (Map.Entry<Identifier, UntypedMetric> entry : snapshot.entrySet()) { + if (entry.getValue().getHistogram() == null) { + continue; + } + gotHistogram = true; + DoubleHistogram histogram = entry.getValue().getHistogram(); + Identifier id = entry.getKey(); + String metricIdentifier = getIdentifierString(id); + output.println("# start of metric " + metricIdentifier); + histogram.outputPercentileDistribution(output, 4, 1.0d, true); + output.println("# end of metric " + metricIdentifier); + } + if (!gotHistogram) { + output.println("# No histograms currently available."); + } + } + + private String getIdentifierString(Identifier id) { + StringBuilder buffer = new StringBuilder(); + Point location = id.getLocation(); + buffer.append(id.getName()); + if (location != null) { + buffer.append(", dimensions: { "); + Iterator<String> dimensions = location.dimensions().iterator(); + Iterator<Value> values = location.location().iterator(); + boolean firstDimension = true; + while (dimensions.hasNext() && values.hasNext()) { + + if (firstDimension) { + firstDimension = false; + } else { + buffer.append(", "); + } + serializeSingleDimension(buffer, dimensions.next(), values.next()); + } + buffer.append(" }"); + } + return buffer.toString(); + + } + + private void serializeSingleDimension(StringBuilder buffer, final String dimensionName, Value dimensionValue) { + buffer.append('"'); + escape(dimensionName, buffer); + buffer.append("\": "); + switch (dimensionValue.getType()) { + case LONG: + buffer.append(Long.toString(dimensionValue.longValue())); + break; + case DOUBLE: + buffer.append(Double.toString(dimensionValue.doubleValue())); + break; + case STRING: + buffer.append('"'); + escape(dimensionValue.stringValue(), buffer); + buffer.append('"'); + break; + default: + buffer.append("\"Unknown type for this dimension, this is a bug.\""); + break; + } + } + + private void escape(final String in, final StringBuilder target) { + for (final char c : in.toCharArray()) { + switch (c) { + case ('"'): + target.append("\\\""); + break; + case ('\\'): + target.append("\\\\"); + break; + case ('\b'): + target.append("\\b"); + break; + case ('\f'): + target.append("\\f"); + break; + case ('\n'): + target.append("\\n"); + break; + case ('\r'): + target.append("\\r"); + break; + case ('\t'): + target.append("\\t"); + break; + default: + if (c < 32) { + target.append("\\u").append(fourDigitHexString(c)); + } else { + target.append(c); + } + break; + } + } + } + + private static char[] fourDigitHexString(final char c) { + final char[] hex = new char[4]; + int in = ((c) & 0xFFFF); + for (int i = 3; i >= 0; --i) { + hex[i] = DIGITS[in & 0xF]; + in >>>= 4; + } + return hex; + } +} |