package org.stagemonitor.core.metrics.metrics2;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metered;
import com.codahale.metrics.Metric;
import com.codahale.metrics.Snapshot;
import com.codahale.metrics.Timer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleSerializers;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class Metric2RegistryModule extends Module {
private final double rateFactor;
private final double durationFactor;
private final Metric2Filter filter;
public Metric2RegistryModule(TimeUnit rateUnit, TimeUnit durationUnit) {
this(rateUnit, durationUnit, Metric2Filter.ALL);
}
public Metric2RegistryModule(TimeUnit rateUnit, TimeUnit durationUnit, Metric2Filter filter) {
this.rateFactor = rateUnit.toSeconds(1);
this.durationFactor = 1.0 / durationUnit.toNanos(1);
this.filter = filter;
}
@Override
public String getModuleName() {
return "stagemonitor";
}
@Override
public Version version() {
return new Version(1, 0, 0, "", "org.stagemonitor", "stagemonitor-core");
}
@Override
public void setupModule(SetupContext context) {
context.addSerializers(new SimpleSerializers(Collections.<JsonSerializer<?>>singletonList(new Metric2RegistrySerializer())));
}
private class Metric2RegistrySerializer extends StdSerializer<Metric2Registry> {
public Metric2RegistrySerializer() {
super(Metric2Registry.class);
}
@Override
public void serialize(Metric2Registry value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartArray();
new MetricSerializer<Gauge>(Gauge.class, new GaugeValueWriter()).serialize(value.getGauges(filter), gen, provider);
new MetricSerializer<Counter>(Counter.class, new CounterValueWriter()).serialize(value.getCounters(filter), gen, provider);
new MetricSerializer<Histogram>(Histogram.class, new HistogramValueWriter()).serialize(value.getHistograms(filter), gen, provider);
new MetricSerializer<Meter>(Meter.class, new MeterValueWriter()).serialize(value.getMeters(filter), gen, provider);
new MetricSerializer<Timer>(Timer.class, new TimerValueWriter()).serialize(value.getTimers(filter), gen, provider);
gen.writeEndArray();
}
}
private class MetricSerializer<T extends Metric> extends StdSerializer<Map<MetricName, T>> {
private final ValueWriter<T> valueWriter;
public MetricSerializer(Class<T> metricType, ValueWriter<T> valueWriter) {
super(TypeFactory.defaultInstance().constructMapType(Map.class, MetricName.class, metricType));
this.valueWriter = valueWriter;
}
@Override
public void serialize(Map<MetricName, T> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
for (Map.Entry<MetricName, T> entry : value.entrySet()) {
gen.writeStartObject();
MetricName metricName = entry.getKey();
gen.writeStringField("name", metricName.getName());
gen.writeObjectFieldStart("tags");
for (Map.Entry<String, String> tagEntry : metricName.getTags().entrySet()) {
gen.writeObjectField(tagEntry.getKey(), tagEntry.getValue());
}
gen.writeEndObject();
gen.writeObjectFieldStart("values");
valueWriter.writeValues(entry.getValue(), gen);
gen.writeEndObject();
gen.writeEndObject();
}
}
}
public <T extends Metric> ValueWriter<T> getValueWriter(Class<T> metricClass) {
if (Gauge.class == metricClass) {
return (ValueWriter<T>) new GaugeValueWriter();
} else if (Counter.class == metricClass) {
return (ValueWriter<T>) new CounterValueWriter();
} else if (Histogram.class == metricClass) {
return (ValueWriter<T>) new HistogramValueWriter();
} else if (Meter.class == metricClass) {
return (ValueWriter<T>) new MeterValueWriter();
} else if (Timer.class == metricClass) {
return (ValueWriter<T>) new TimerValueWriter();
} else {
throw new IllegalArgumentException("Unknown metric class: " + metricClass);
}
}
public interface ValueWriter<T extends Metric> {
void writeValues(T value, JsonGenerator jg) throws IOException;
}
private class GaugeValueWriter implements ValueWriter<Gauge> {
public void writeValues(Gauge gauge, JsonGenerator jg) throws IOException {
final Object value = gauge.getValue();
if (value == null) {
return;
}
if (value instanceof Number) {
writeDoubleUnlessNaN(jg, "value", ((Number) value).doubleValue());
} else if (value instanceof Boolean) {
jg.writeBooleanField("value_boolean", (Boolean) value);
} else {
jg.writeStringField("value_string", value.toString());
}
}
}
private class CounterValueWriter implements ValueWriter<Counter> {
public void writeValues(Counter counter, JsonGenerator jg) throws IOException {
jg.writeObjectField("count", counter.getCount());
}
}
private class HistogramValueWriter implements ValueWriter<Histogram> {
public void writeValues(Histogram histogram, JsonGenerator jg) throws IOException {
final Snapshot snapshot = histogram.getSnapshot();
jg.writeNumberField("count", histogram.getCount());
writeHistogramSnapshot(snapshot, jg);
}
}
private class MeterValueWriter implements ValueWriter<Meter> {
public void writeValues(Meter meter, JsonGenerator jg) throws IOException {
writeMetered(meter, jg);
}
}
private class TimerValueWriter implements ValueWriter<Timer> {
public void writeValues(Timer timer, JsonGenerator jg) throws IOException {
writeMetered(timer, jg);
writeTimerSnapshot(timer.getSnapshot(), jg);
}
}
private void writeTimerSnapshot(Snapshot snapshot, JsonGenerator jg) throws IOException {
writeDoubleUnlessNaN(jg, "min", convertDuration(snapshot.getMin()));
writeDoubleUnlessNaN(jg, "max", convertDuration(snapshot.getMax()));
writeDoubleUnlessNaN(jg, "mean", convertDuration(snapshot.getMean()));
writeDoubleUnlessNaN(jg, "p50", convertDuration(snapshot.getMedian()));
writeDoubleUnlessNaN(jg, "std", convertDuration(snapshot.getStdDev()));
writeDoubleUnlessNaN(jg, "p25", convertDuration(snapshot.getValue(0.25)));
writeDoubleUnlessNaN(jg, "p75", convertDuration(snapshot.get75thPercentile()));
writeDoubleUnlessNaN(jg, "p95", convertDuration(snapshot.get95thPercentile()));
writeDoubleUnlessNaN(jg, "p98", convertDuration(snapshot.get98thPercentile()));
writeDoubleUnlessNaN(jg, "p99", convertDuration(snapshot.get99thPercentile()));
writeDoubleUnlessNaN(jg, "p999", convertDuration(snapshot.get999thPercentile()));
}
private void writeHistogramSnapshot(Snapshot snapshot, JsonGenerator jg) throws IOException {
writeDoubleUnlessNaN(jg, "min", snapshot.getMin());
writeDoubleUnlessNaN(jg, "max", snapshot.getMax());
writeDoubleUnlessNaN(jg, "mean", snapshot.getMean());
writeDoubleUnlessNaN(jg, "p50", snapshot.getMedian());
writeDoubleUnlessNaN(jg, "std", snapshot.getStdDev());
writeDoubleUnlessNaN(jg, "p25", snapshot.getValue(0.25));
writeDoubleUnlessNaN(jg, "p75", snapshot.get75thPercentile());
writeDoubleUnlessNaN(jg, "p95", snapshot.get95thPercentile());
writeDoubleUnlessNaN(jg, "p98", snapshot.get98thPercentile());
writeDoubleUnlessNaN(jg, "p99", snapshot.get99thPercentile());
writeDoubleUnlessNaN(jg, "p999", snapshot.get999thPercentile());
}
private void writeMetered(Metered metered, JsonGenerator jg) throws IOException {
jg.writeNumberField("count", metered.getCount());
writeDoubleUnlessNaN(jg, "m1_rate", convertRate(metered.getOneMinuteRate()));
writeDoubleUnlessNaN(jg, "m5_rate", convertRate(metered.getFiveMinuteRate()));
writeDoubleUnlessNaN(jg, "m15_rate", convertRate(metered.getFifteenMinuteRate()));
writeDoubleUnlessNaN(jg, "mean_rate", convertRate(metered.getMeanRate()));
}
private void writeDoubleUnlessNaN(JsonGenerator jg, String key, double value) throws IOException {
if (!Double.isNaN(value)) {
jg.writeNumberField(key, value);
}
}
private double convertDuration(double duration) {
return duration * durationFactor;
}
private double convertRate(double rate) {
return rate * rateFactor;
}
}