| // Copyright (C) 2015 The Android Open Source Project | |
| // | |
| // 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. | |
| package com.google.gerrit.metrics.dropwizard; | |
| import com.codahale.metrics.Counter; | |
| import com.codahale.metrics.Gauge; | |
| import com.codahale.metrics.Histogram; | |
| import com.codahale.metrics.Meter; | |
| import com.codahale.metrics.Metric; | |
| import com.codahale.metrics.Snapshot; | |
| import com.codahale.metrics.Timer; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.gerrit.common.Nullable; | |
| import com.google.gerrit.metrics.Description; | |
| import com.google.gerrit.metrics.Field; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.TreeMap; | |
| import java.util.function.Function; | |
| class MetricJson { | |
| String description; | |
| String unit; | |
| Boolean constant; | |
| Boolean rate; | |
| Boolean gauge; | |
| Boolean cumulative; | |
| Long count; | |
| Object value; | |
| Double rate_1m; | |
| Double rate_5m; | |
| Double rate_15m; | |
| Double rate_mean; | |
| Double p50; | |
| Double p75; | |
| Double p95; | |
| Double p98; | |
| Double p99; | |
| Double p99_9; | |
| Double min; | |
| Double avg; | |
| Double max; | |
| Double sum; | |
| Double std_dev; | |
| List<FieldJson> fields; | |
| Map<String, Object> buckets; | |
| MetricJson(Metric metric, ImmutableMap<String, String> atts, boolean dataOnly) { | |
| if (!dataOnly) { | |
| description = atts.get(Description.DESCRIPTION); | |
| unit = atts.get(Description.UNIT); | |
| constant = toBool(atts, Description.CONSTANT); | |
| rate = toBool(atts, Description.RATE); | |
| gauge = toBool(atts, Description.GAUGE); | |
| cumulative = toBool(atts, Description.CUMULATIVE); | |
| } | |
| init(metric, atts); | |
| } | |
| private void init(Metric metric, ImmutableMap<String, String> atts) { | |
| if (metric instanceof BucketedMetric) { | |
| BucketedMetric m = (BucketedMetric) metric; | |
| if (m.getTotal() != null) { | |
| init(m.getTotal(), atts); | |
| } | |
| Field<?>[] fieldList = m.getFields(); | |
| fields = new ArrayList<>(fieldList.length); | |
| for (Field<?> f : fieldList) { | |
| fields.add(new FieldJson(f)); | |
| } | |
| buckets = makeBuckets(fieldList, m.getCells(), atts); | |
| } else if (metric instanceof Counter) { | |
| Counter c = (Counter) metric; | |
| count = c.getCount(); | |
| } else if (metric instanceof Gauge) { | |
| Gauge<?> g = (Gauge<?>) metric; | |
| value = g.getValue(); | |
| } else if (metric instanceof Meter) { | |
| Meter m = (Meter) metric; | |
| count = m.getCount(); | |
| rate_1m = m.getOneMinuteRate(); | |
| rate_5m = m.getFiveMinuteRate(); | |
| rate_15m = m.getFifteenMinuteRate(); | |
| } else if (metric instanceof Timer) { | |
| Timer m = (Timer) metric; | |
| Snapshot s = m.getSnapshot(); | |
| count = m.getCount(); | |
| rate_1m = m.getOneMinuteRate(); | |
| rate_5m = m.getFiveMinuteRate(); | |
| rate_15m = m.getFifteenMinuteRate(); | |
| double div = Description.getTimeUnit(atts.get(Description.UNIT)).toNanos(1); | |
| p50 = s.getMedian() / div; | |
| p75 = s.get75thPercentile() / div; | |
| p95 = s.get95thPercentile() / div; | |
| p98 = s.get98thPercentile() / div; | |
| p99 = s.get99thPercentile() / div; | |
| p99_9 = s.get999thPercentile() / div; | |
| min = s.getMin() / div; | |
| max = s.getMax() / div; | |
| std_dev = s.getStdDev() / div; | |
| } else if (metric instanceof Histogram) { | |
| Histogram m = (Histogram) metric; | |
| Snapshot s = m.getSnapshot(); | |
| count = m.getCount(); | |
| p50 = s.getMedian(); | |
| p75 = s.get75thPercentile(); | |
| p95 = s.get95thPercentile(); | |
| p98 = s.get98thPercentile(); | |
| p99 = s.get99thPercentile(); | |
| p99_9 = s.get999thPercentile(); | |
| min = (double) s.getMin(); | |
| avg = s.getMean(); | |
| max = (double) s.getMax(); | |
| sum = s.getMean() * m.getCount(); | |
| std_dev = s.getStdDev(); | |
| } | |
| } | |
| @Nullable | |
| private static Boolean toBool(ImmutableMap<String, String> atts, String key) { | |
| return Description.TRUE_VALUE.equals(atts.get(key)) ? true : null; | |
| } | |
| @SuppressWarnings("unchecked") | |
| private static Map<String, Object> makeBuckets( | |
| Field<?>[] fields, Map<?, Metric> metrics, ImmutableMap<String, String> atts) { | |
| if (fields.length == 1) { | |
| Function<Object, String> fmt = (Function<Object, String>) fields[0].formatter(); | |
| Map<String, Object> out = new TreeMap<>(); | |
| for (Map.Entry<?, Metric> e : metrics.entrySet()) { | |
| out.put(fmt.apply(e.getKey()), new MetricJson(e.getValue(), atts, true)); | |
| } | |
| return out; | |
| } | |
| Map<String, Object> out = new TreeMap<>(); | |
| for (Map.Entry<?, Metric> e : metrics.entrySet()) { | |
| ImmutableList<Object> keys = (ImmutableList<Object>) e.getKey(); | |
| Map<String, Object> dst = out; | |
| for (int i = 0; i < fields.length - 1; i++) { | |
| Function<Object, String> fmt = (Function<Object, String>) fields[i].formatter(); | |
| String key = fmt.apply(keys.get(i)); | |
| Map<String, Object> t = (Map<String, Object>) dst.get(key); | |
| if (t == null) { | |
| t = new TreeMap<>(); | |
| dst.put(key, t); | |
| } | |
| dst = t; | |
| } | |
| Function<Object, String> fmt = | |
| (Function<Object, String>) fields[fields.length - 1].formatter(); | |
| dst.put(fmt.apply(keys.get(fields.length - 1)), new MetricJson(e.getValue(), atts, true)); | |
| } | |
| return out; | |
| } | |
| static class FieldJson { | |
| String name; | |
| String type; | |
| String description; | |
| FieldJson(Field<?> field) { | |
| this.name = field.name(); | |
| this.description = field.description().orElse(null); | |
| this.type = | |
| Enum.class.isAssignableFrom(field.valueType()) ? field.valueType().getSimpleName() : null; | |
| } | |
| } | |
| } |