/* Copyright (c) 2011 Danish Maritime Authority.
*
* 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 net.maritimecloud.mms.server.rest;
import com.codahale.metrics.Clock;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Snapshot;
import com.codahale.metrics.Timer;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
/**
* MessageBodyWriter implementation for the MetricRegistry class
*/
@Produces("text/plain")
@Provider
public class JSONMetricRegistryBodyWriter implements MessageBodyWriter<MetricRegistry> {
private static final int CONSOLE_WIDTH = 80;
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type == MetricRegistry.class;
}
@Override
public long getSize(MetricRegistry myBean, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return -1; // deprecated by JAX-RS 2.0 and ignored by Jersey runtime
}
@Override
public void writeTo(MetricRegistry registry, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException {
PrintWriter output = new PrintWriter(entityStream);
report(output, registry.getGauges(), registry.getCounters(), registry.getHistograms(), registry.getMeters(), registry.getTimers());
}
/**
* This method is more or less copied from the ConsoleReporter class.
*/
@SuppressWarnings("rawtypes")
public void report(PrintWriter output,
SortedMap<String, Gauge> gauges,
SortedMap<String, Counter> counters,
SortedMap<String, Histogram> histograms,
SortedMap<String, Meter> meters,
SortedMap<String, Timer> timers) {
Locale locale = Locale.ENGLISH;
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.MEDIUM,
locale);
dateFormat.setTimeZone(TimeZone.getDefault());
final String dateTime = dateFormat.format(new Date(Clock.defaultClock().getTime()));
printWithBanner(output, dateTime, '=');
output.println();
if (!gauges.isEmpty()) {
printWithBanner(output, "-- Gauges", '-');
for (Map.Entry<String, Gauge> entry : gauges.entrySet()) {
output.println(entry.getKey());
printGauge(output, locale, entry);
}
output.println();
}
if (!counters.isEmpty()) {
printWithBanner(output, "-- Counters", '-');
for (Map.Entry<String, Counter> entry : counters.entrySet()) {
output.println(entry.getKey());
printCounter(output, locale, entry);
}
output.println();
}
if (!histograms.isEmpty()) {
printWithBanner(output, "-- Histograms", '-');
for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {
output.println(entry.getKey());
printHistogram(output, locale, entry.getValue());
}
output.println();
}
if (!meters.isEmpty()) {
printWithBanner(output, "-- Meters", '-');
for (Map.Entry<String, Meter> entry : meters.entrySet()) {
output.println(entry.getKey());
printMeter(output, locale, entry.getValue());
}
output.println();
}
if (!timers.isEmpty()) {
printWithBanner(output, "-- Timers", '-');
for (Map.Entry<String, Timer> entry : timers.entrySet()) {
output.println(entry.getKey());
printTimer(output, locale, entry.getValue());
}
output.println();
}
output.println();
output.flush();
}
private void printMeter(PrintWriter output, Locale locale, Meter meter) {
output.printf(locale, " count = %d%n", meter.getCount());
output.printf(locale, " mean rate = %2.2f events/%s%n", convertRate(meter.getMeanRate()), getRateUnit());
output.printf(locale, " 1-minute rate = %2.2f events/%s%n", convertRate(meter.getOneMinuteRate()), getRateUnit());
output.printf(locale, " 5-minute rate = %2.2f events/%s%n", convertRate(meter.getFiveMinuteRate()), getRateUnit());
output.printf(locale, " 15-minute rate = %2.2f events/%s%n", convertRate(meter.getFifteenMinuteRate()), getRateUnit());
}
private void printCounter(PrintWriter output, Locale locale, Map.Entry<String, Counter> entry) {
output.printf(locale, " count = %d%n", entry.getValue().getCount());
}
private void printGauge(PrintWriter output, Locale locale, @SuppressWarnings("rawtypes") Map.Entry<String, Gauge> entry) {
output.printf(locale, " value = %s%n", entry.getValue().getValue());
}
private void printHistogram(PrintWriter output, Locale locale, Histogram histogram) {
output.printf(locale, " count = %d%n", histogram.getCount());
Snapshot snapshot = histogram.getSnapshot();
output.printf(locale, " min = %d%n", snapshot.getMin());
output.printf(locale, " max = %d%n", snapshot.getMax());
output.printf(locale, " mean = %2.2f%n", snapshot.getMean());
output.printf(locale, " stddev = %2.2f%n", snapshot.getStdDev());
output.printf(locale, " median = %2.2f%n", snapshot.getMedian());
output.printf(locale, " 75%% <= %2.2f%n", snapshot.get75thPercentile());
output.printf(locale, " 95%% <= %2.2f%n", snapshot.get95thPercentile());
output.printf(locale, " 98%% <= %2.2f%n", snapshot.get98thPercentile());
output.printf(locale, " 99%% <= %2.2f%n", snapshot.get99thPercentile());
output.printf(locale, " 99.9%% <= %2.2f%n", snapshot.get999thPercentile());
}
private void printTimer(PrintWriter output, Locale locale, Timer timer) {
final Snapshot snapshot = timer.getSnapshot();
output.printf(locale, " count = %d%n", timer.getCount());
output.printf(locale, " mean rate = %2.2f calls/%s%n", convertRate(timer.getMeanRate()), getRateUnit());
output.printf(locale, " 1-minute rate = %2.2f calls/%s%n", convertRate(timer.getOneMinuteRate()), getRateUnit());
output.printf(locale, " 5-minute rate = %2.2f calls/%s%n", convertRate(timer.getFiveMinuteRate()), getRateUnit());
output.printf(locale, " 15-minute rate = %2.2f calls/%s%n", convertRate(timer.getFifteenMinuteRate()), getRateUnit());
output.printf(locale, " min = %2.2f %s%n", convertDuration(snapshot.getMin()), getDurationUnit());
output.printf(locale, " max = %2.2f %s%n", convertDuration(snapshot.getMax()), getDurationUnit());
output.printf(locale, " mean = %2.2f %s%n", convertDuration(snapshot.getMean()), getDurationUnit());
output.printf(locale, " stddev = %2.2f %s%n", convertDuration(snapshot.getStdDev()), getDurationUnit());
output.printf(locale, " median = %2.2f %s%n", convertDuration(snapshot.getMedian()), getDurationUnit());
output.printf(locale, " 75%% <= %2.2f %s%n", convertDuration(snapshot.get75thPercentile()), getDurationUnit());
output.printf(locale, " 95%% <= %2.2f %s%n", convertDuration(snapshot.get95thPercentile()), getDurationUnit());
output.printf(locale, " 98%% <= %2.2f %s%n", convertDuration(snapshot.get98thPercentile()), getDurationUnit());
output.printf(locale, " 99%% <= %2.2f %s%n", convertDuration(snapshot.get99thPercentile()), getDurationUnit());
output.printf(locale, " 99.9%% <= %2.2f %s%n", convertDuration(snapshot.get999thPercentile()), getDurationUnit());
}
private void printWithBanner(PrintWriter output, String s, char c) {
output.print(s);
output.print(' ');
for (int i = 0; i < (CONSOLE_WIDTH - s.length() - 1); i++) {
output.print(c);
}
output.println();
}
protected String getRateUnit() {
return calculateRateUnit(TimeUnit.SECONDS);
}
protected String getDurationUnit() {
return TimeUnit.MILLISECONDS.toString().toLowerCase(Locale.US);
}
protected double convertDuration(double duration) {
return duration * 1.0 / TimeUnit.MILLISECONDS.toNanos(1);
}
protected double convertRate(double rate) {
return rate * TimeUnit.SECONDS.toSeconds(1);
}
private String calculateRateUnit(TimeUnit unit) {
final String s = unit.toString().toLowerCase(Locale.US);
return s.substring(0, s.length() - 1);
}
}