| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.sling.commons.metrics.internal; |
| |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.io.PrintWriter; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.SortedMap; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.felix.utils.json.JSONWriter; |
| |
| import com.codahale.metrics.ConsoleReporter; |
| import com.codahale.metrics.Counter; |
| import com.codahale.metrics.Gauge; |
| import com.codahale.metrics.Histogram; |
| import com.codahale.metrics.Meter; |
| import com.codahale.metrics.MetricFilter; |
| import com.codahale.metrics.MetricRegistry; |
| import com.codahale.metrics.Reporter; |
| import com.codahale.metrics.Snapshot; |
| import com.codahale.metrics.Timer; |
| |
| class JSONReporter implements Reporter, Closeable { |
| |
| public static JSONReporter.Builder forRegistry(MetricRegistry registry) { |
| return new JSONReporter.Builder(registry); |
| } |
| |
| public static class Builder { |
| private final MetricRegistry registry; |
| private PrintStream output; |
| private MetricFilter filter; |
| private TimeUnit rateUnit; |
| private TimeUnit durationUnit; |
| |
| private Builder(MetricRegistry registry) { |
| this.registry = registry; |
| this.output = System.out; |
| this.filter = MetricFilter.ALL; |
| this.rateUnit = TimeUnit.SECONDS; |
| this.durationUnit = TimeUnit.MILLISECONDS; |
| } |
| |
| /** |
| * Write to the given {@link PrintStream}. |
| * |
| * @param output a {@link PrintStream} instance. |
| * @return {@code this} |
| */ |
| public Builder outputTo(PrintStream output) { |
| this.output = output; |
| return this; |
| } |
| |
| /** |
| * Convert rates to the given time unit. |
| * |
| * @param rateUnit a unit of time |
| * @return {@code this} |
| */ |
| public Builder convertRatesTo(TimeUnit rateUnit) { |
| this.rateUnit = rateUnit; |
| return this; |
| } |
| |
| /** |
| * Convert durations to the given time unit. |
| * |
| * @param durationUnit a unit of time |
| * @return {@code this} |
| */ |
| public Builder convertDurationsTo(TimeUnit durationUnit) { |
| this.durationUnit = durationUnit; |
| return this; |
| } |
| |
| /** |
| * Only report metrics which match the given filter. |
| * |
| * @param filter a {@link MetricFilter} |
| * @return {@code this} |
| */ |
| public Builder filter(MetricFilter filter) { |
| this.filter = filter; |
| return this; |
| } |
| |
| /** |
| * Builds a {@link ConsoleReporter} with the given properties. |
| * |
| * @return a {@link ConsoleReporter} |
| */ |
| public JSONReporter build() { |
| return new JSONReporter(registry, |
| output, |
| rateUnit, |
| durationUnit, |
| filter); |
| } |
| } |
| |
| private final MetricRegistry registry; |
| private final MetricFilter filter; |
| private final double durationFactor; |
| private final String durationUnit; |
| private final double rateFactor; |
| private final String rateUnit; |
| private final JSONWriter json; |
| private final PrintWriter pw; |
| |
| private JSONReporter(MetricRegistry registry, |
| PrintStream output, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter){ |
| this.registry = registry; |
| this.filter = filter; |
| this.pw = new PrintWriter(output); |
| this.json = new JSONWriter(pw); |
| this.rateFactor = rateUnit.toSeconds(1); |
| this.rateUnit = calculateRateUnit(rateUnit); |
| this.durationFactor = 1.0 / durationUnit.toNanos(1); |
| this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); |
| } |
| |
| public void report() { |
| try { |
| report(registry.getGauges(filter), |
| registry.getCounters(filter), |
| registry.getHistograms(filter), |
| registry.getMeters(filter), |
| registry.getTimers(filter)); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Override |
| public void close(){ |
| pw.flush(); |
| } |
| |
| private void report(SortedMap<String, Gauge> gauges, SortedMap<String, Counter> counters, |
| SortedMap<String, Histogram> histograms, SortedMap<String, Meter> meters, |
| SortedMap<String, Timer> timers) throws IOException { |
| json.object(); |
| if (!gauges.isEmpty()) { |
| json.key("gauges").object(); |
| for (Map.Entry<String, Gauge> entry : gauges.entrySet()) { |
| printGauge(entry); |
| } |
| json.endObject(); |
| } |
| |
| if (!counters.isEmpty()) { |
| json.key("counters").object(); |
| for (Map.Entry<String, Counter> entry : counters.entrySet()) { |
| printCounter(entry); |
| } |
| json.endObject(); |
| } |
| |
| if (!histograms.isEmpty()) { |
| json.key("histograms").object(); |
| for (Map.Entry<String, Histogram> entry : histograms.entrySet()) { |
| printHistogram(entry); |
| } |
| json.endObject(); |
| } |
| |
| if (!meters.isEmpty()) { |
| json.key("meters").object(); |
| for (Map.Entry<String, Meter> entry : meters.entrySet()) { |
| printMeter(entry); |
| } |
| json.endObject(); |
| } |
| |
| if (!timers.isEmpty()) { |
| json.key("timers").object(); |
| for (Map.Entry<String, Timer> entry : timers.entrySet()) { |
| printTimer(entry); |
| } |
| json.endObject(); |
| } |
| |
| json.endObject(); |
| |
| } |
| |
| private void printTimer(Map.Entry<String, Timer> e) throws IOException { |
| json.key(e.getKey()).object(); |
| Timer timer = e.getValue(); |
| Snapshot snapshot = timer.getSnapshot(); |
| |
| json.key("count").value(timer.getCount()); |
| json.key("max").value(snapshot.getMax() * durationFactor); |
| json.key("mean").value(snapshot.getMean() * durationFactor); |
| json.key("min").value(snapshot.getMin() * durationFactor); |
| |
| json.key("p50").value(snapshot.getMedian() * durationFactor); |
| json.key("p75").value(snapshot.get75thPercentile() * durationFactor); |
| json.key("p95").value(snapshot.get95thPercentile() * durationFactor); |
| json.key("p98").value(snapshot.get98thPercentile() * durationFactor); |
| json.key("p99").value(snapshot.get99thPercentile() * durationFactor); |
| json.key("p999").value(snapshot.get999thPercentile() * durationFactor); |
| |
| json.key("stddev").value(snapshot.getStdDev() * durationFactor); |
| json.key("m1_rate").value(timer.getOneMinuteRate() * rateFactor); |
| json.key("m5_rate").value(timer.getFiveMinuteRate() * rateFactor); |
| json.key("m15_rate").value(timer.getFifteenMinuteRate() * rateFactor); |
| json.key("mean_rate").value(timer.getMeanRate() * rateFactor); |
| json.key("duration_units").value(durationUnit); |
| json.key("rate_units").value(rateUnit); |
| |
| json.endObject(); |
| } |
| |
| private void printMeter(Map.Entry<String, Meter> e) throws IOException { |
| json.key(e.getKey()).object(); |
| Meter meter = e.getValue(); |
| json.key("count").value(e.getValue().getCount()); |
| json.key("m1_rate").value(meter.getOneMinuteRate() * rateFactor); |
| json.key("m5_rate").value(meter.getFiveMinuteRate() * rateFactor); |
| json.key("m15_rate").value(meter.getFifteenMinuteRate() * rateFactor); |
| json.key("mean_rate").value(meter.getMeanRate() * rateFactor); |
| json.key("units").value(rateUnit); |
| json.endObject(); |
| } |
| |
| private void printHistogram(Map.Entry<String, Histogram> e) throws IOException { |
| json.key(e.getKey()).object(); |
| json.key("count").value(e.getValue().getCount()); |
| |
| Snapshot snapshot = e.getValue().getSnapshot(); |
| json.key("max").value(snapshot.getMax()); |
| json.key("mean").value(snapshot.getMean()); |
| json.key("min").value(snapshot.getMin()); |
| json.key("p50").value(snapshot.getMedian()); |
| json.key("p75").value(snapshot.get75thPercentile()); |
| json.key("p95").value(snapshot.get95thPercentile()); |
| json.key("p98").value(snapshot.get98thPercentile()); |
| json.key("p99").value(snapshot.get99thPercentile()); |
| json.key("p999").value(snapshot.get999thPercentile()); |
| json.key("stddev").value(snapshot.getStdDev()); |
| |
| json.endObject(); |
| } |
| |
| private void printCounter(Map.Entry<String, Counter> e) throws IOException { |
| json.key(e.getKey()).object(); |
| json.key("count").value(e.getValue().getCount()); |
| json.endObject(); |
| } |
| |
| private void printGauge(Map.Entry<String, Gauge> e) throws IOException { |
| json.key(e.getKey()).object(); |
| Object v = e.getValue().getValue(); |
| json.key("value").value(jsonSafeValue(v)); |
| json.endObject(); |
| } |
| |
| private static Object jsonSafeValue(Object v){ |
| //Json does not allow NaN or infinite doubles. So take care of that |
| if (v instanceof Number){ |
| if (v instanceof Double){ |
| Double d = (Double) v; |
| if (d.isInfinite() || d.isNaN()){ |
| return d.toString(); |
| } |
| } |
| } |
| return v; |
| } |
| |
| private static String calculateRateUnit(TimeUnit unit) { |
| final String s = unit.toString().toLowerCase(Locale.US); |
| return s.substring(0, s.length() - 1); |
| } |
| |
| } |