blob: 1b24b3e2a75cba5871edd0eb62bfefb29e364f3b [file] [log] [blame]
/*
* 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);
}
}