diff options
Diffstat (limited to 'simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc')
4 files changed, 338 insertions, 0 deletions
diff --git a/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/JdiscMetricsFactory.java b/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/JdiscMetricsFactory.java new file mode 100644 index 00000000000..f5208b2226c --- /dev/null +++ b/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/JdiscMetricsFactory.java @@ -0,0 +1,61 @@ +// 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.logging.Logger; + +import com.yahoo.container.jdisc.MetricConsumerFactory; +import com.yahoo.container.jdisc.state.MetricSnapshot; +import com.yahoo.container.jdisc.state.SnapshotProvider; +import com.yahoo.jdisc.application.MetricConsumer; +import com.yahoo.metrics.simple.Bucket; +import com.yahoo.metrics.simple.MetricReceiver; + +/** + * A factory for all the JDisc API classes. + * + * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a> + */ +public class JdiscMetricsFactory implements MetricConsumerFactory, SnapshotProvider { + private static final Logger log = Logger.getLogger(JdiscMetricsFactory.class.getName()); + private final SimpleMetricConsumer metricInstance; + private final MetricReceiver metricReceiver; + + public JdiscMetricsFactory(MetricReceiver receiver) { + this.metricReceiver = receiver; + this.metricInstance = new SimpleMetricConsumer(receiver); + } + + @Override + public MetricConsumer newInstance() { + // the underlying implementation is thread safe anyway to allow for stand-alone use + return metricInstance; + } + + + @Override + public MetricSnapshot latestSnapshot() { + Bucket curr = metricReceiver.getSnapshot(); + if (curr == null) { + log.warning("no snapshot from instance of " + metricInstance.getClass()); + return null; + } else { + SnapshotConverter converter = new SnapshotConverter(curr); + return converter.convert(); + } + } + + @Override + public void histogram(PrintStream output) { + Bucket curr = metricReceiver.getSnapshot(); + if (curr == null) { + log.warning("no snapshot from instance of " + metricInstance.getClass()); + return; + } else { + SnapshotConverter converter = new SnapshotConverter(curr); + converter.outputHistograms(output); + return; + } + } + +} diff --git a/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/SimpleMetricConsumer.java b/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/SimpleMetricConsumer.java new file mode 100644 index 00000000000..7f5571418fe --- /dev/null +++ b/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/SimpleMetricConsumer.java @@ -0,0 +1,55 @@ +// 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.util.Map; + +import com.yahoo.jdisc.Metric.Context; +import com.yahoo.jdisc.application.MetricConsumer; +import com.yahoo.metrics.simple.Identifier; +import com.yahoo.metrics.simple.Measurement; +import com.yahoo.metrics.simple.Point; +import com.yahoo.metrics.simple.MetricReceiver; +import com.yahoo.metrics.simple.Sample; +import com.yahoo.metrics.simple.UntypedMetric.AssumedType; + +/** + * The single user facing part of the JDisc interfaces of simple metrics. + * + * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a> + */ +public class SimpleMetricConsumer implements MetricConsumer { + + private final MetricReceiver receiver; + + public SimpleMetricConsumer(MetricReceiver receiver) { + this.receiver = receiver; + } + + @Override + public void set(String key, Number val, Context ctx) { + receiver.update(new Sample(new Measurement(val), new Identifier(key, getSimpleCoordinate(ctx)), AssumedType.GAUGE)); + } + + @Override + public void add(String key, Number val, Context ctx) { + receiver.update(new Sample(new Measurement(val), new Identifier(key, getSimpleCoordinate(ctx)), AssumedType.COUNTER)); + } + + private Point getSimpleCoordinate(Context ctx) { + if (ctx instanceof Point) { + return (Point) ctx; + } else { + return null; + } + } + + @Override + public Context createContext(Map<String, ?> properties) { + if (properties == null) { + return null; + } else { + return new Point(properties); + } + } + +} 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; + } +} diff --git a/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/package-info.java b/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/package-info.java new file mode 100644 index 00000000000..4d34244bec0 --- /dev/null +++ b/simplemetrics/src/main/java/com/yahoo/metrics/simple/jdisc/package-info.java @@ -0,0 +1,10 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * JDisc metrics API for simple metrics implementation. + * + * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a> + */ +@ExportPackage +package com.yahoo.metrics.simple.jdisc; + +import com.yahoo.osgi.annotation.ExportPackage; |