All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.metrics.simple.jdisc.SnapshotConverter Maven / Gradle / Ivy

// Copyright 2017 Yahoo Holdings. 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 java.util.logging.Logger;

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 {

    private static Logger log = Logger.getLogger(SnapshotConverter.class.getName());

    final Bucket snapshot;
    final Map> 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' };

    public SnapshotConverter(Bucket snapshot) {
        this.snapshot = snapshot;
    }

    static MetricDimensions convert(Point p) {
        if (p == null) {
            return StateMetricContext.newInstance(null);
        }
        List dimensions = p.dimensions();
        List location = p.location();
        Map 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 value.longValue();
            case DOUBLE:
                return 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> buildPercentileList(DoubleHistogram histogram) {
        List> 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 entry : snapshot.entrySet()) {
            Identifier ident = entry.getKey();
            getMap(ident.getLocation()).put(ident.getName(), convert(entry.getValue()));
        }
        Map data = new HashMap<>();
        for (Map.Entry> entry : perPointData.entrySet()) {
            MetricDimensions key = convert(entry.getKey());
            MetricSet newval = new MetricSet(entry.getValue());
            MetricSet old = data.get(key);
            if (old != null) {
                // should not happen, this is bad
                // TODO: consider merging the two MetricSet instances
                log.warning("losing MetricSet when converting for: "+entry.getKey());
            } else {
                data.put(key, newval);
            }
        }
        return new MetricSnapshot(snapshot.getFromMillis(),
                                  snapshot.getToMillis(),
                                  TimeUnit.MILLISECONDS,
                                  data);
    }

    private Map getMap(Point point) {
        if (point == null) {
            point = Point.emptyPoint();
        }
        if (! perPointData.containsKey(point)) {
            perPointData.put(point, new HashMap<>());
        }
        return perPointData.get(point);
    }

    void outputHistograms(PrintStream output) {
        boolean gotHistogram = false;
        for (Map.Entry 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 dimensions = location.dimensions().iterator();
            Iterator 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;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy