com.codahale.metrics.CsvReporter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of metrics-core Show documentation
Show all versions of metrics-core Show documentation
Metrics is a Java library which gives you unparalleled insight into what your code does in
production. Metrics provides a powerful toolkit of ways to measure the behavior of critical
components in your production environment.
package com.codahale.metrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* A reporter which creates a comma-separated values file of the measurements for each metric.
*/
public class CsvReporter extends ScheduledReporter {
private static final String DEFAULT_SEPARATOR = ",";
/**
* Returns a new {@link Builder} for {@link CsvReporter}.
*
* @param registry the registry to report
* @return a {@link Builder} instance for a {@link CsvReporter}
*/
public static Builder forRegistry(MetricRegistry registry) {
return new Builder(registry);
}
/**
* A builder for {@link CsvReporter} instances. Defaults to using the default locale, converting
* rates to events/second, converting durations to milliseconds, and not filtering metrics.
*/
public static class Builder {
private final MetricRegistry registry;
private Locale locale;
private String separator;
private TimeUnit rateUnit;
private TimeUnit durationUnit;
private Clock clock;
private MetricFilter filter;
private ScheduledExecutorService executor;
private boolean shutdownExecutorOnStop;
private CsvFileProvider csvFileProvider;
private Builder(MetricRegistry registry) {
this.registry = registry;
this.locale = Locale.getDefault();
this.separator = DEFAULT_SEPARATOR;
this.rateUnit = TimeUnit.SECONDS;
this.durationUnit = TimeUnit.MILLISECONDS;
this.clock = Clock.defaultClock();
this.filter = MetricFilter.ALL;
this.executor = null;
this.shutdownExecutorOnStop = true;
this.csvFileProvider = new FixedNameCsvFileProvider();
}
/**
* Specifies whether or not, the executor (used for reporting) will be stopped with same time with reporter.
* Default value is true.
* Setting this parameter to false, has the sense in combining with providing external managed executor via {@link #scheduleOn(ScheduledExecutorService)}.
*
* @param shutdownExecutorOnStop if true, then executor will be stopped in same time with this reporter
* @return {@code this}
*/
public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop) {
this.shutdownExecutorOnStop = shutdownExecutorOnStop;
return this;
}
/**
* Specifies the executor to use while scheduling reporting of metrics.
* Default value is null.
* Null value leads to executor will be auto created on start.
*
* @param executor the executor to use while scheduling reporting of metrics.
* @return {@code this}
*/
public Builder scheduleOn(ScheduledExecutorService executor) {
this.executor = executor;
return this;
}
/**
* Format numbers for the given {@link Locale}.
*
* @param locale a {@link Locale}
* @return {@code this}
*/
public Builder formatFor(Locale locale) {
this.locale = locale;
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;
}
/**
* Use the given string to use as the separator for values.
*
* @param separator the string to use for the separator.
* @return {@code this}
*/
public Builder withSeparator(String separator) {
this.separator = separator;
return this;
}
/**
* Use the given {@link Clock} instance for the time.
*
* @param clock a {@link Clock} instance
* @return {@code this}
*/
public Builder withClock(Clock clock) {
this.clock = clock;
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;
}
public Builder withCsvFileProvider(CsvFileProvider csvFileProvider) {
this.csvFileProvider = csvFileProvider;
return this;
}
/**
* Builds a {@link CsvReporter} with the given properties, writing {@code .csv} files to the
* given directory.
*
* @param directory the directory in which the {@code .csv} files will be created
* @return a {@link CsvReporter}
*/
public CsvReporter build(File directory) {
return new CsvReporter(registry,
directory,
locale,
separator,
rateUnit,
durationUnit,
clock,
filter,
executor,
shutdownExecutorOnStop,
csvFileProvider);
}
}
private static final Logger LOGGER = LoggerFactory.getLogger(CsvReporter.class);
private final File directory;
private final Locale locale;
private final String separator;
private final Clock clock;
private final CsvFileProvider csvFileProvider;
private final String histogramFormat;
private final String meterFormat;
private final String timerFormat;
private final String timerHeader;
private final String meterHeader;
private final String histogramHeader;
private CsvReporter(MetricRegistry registry,
File directory,
Locale locale,
String separator,
TimeUnit rateUnit,
TimeUnit durationUnit,
Clock clock,
MetricFilter filter,
ScheduledExecutorService executor,
boolean shutdownExecutorOnStop,
CsvFileProvider csvFileProvider) {
super(registry, "csv-reporter", filter, rateUnit, durationUnit, executor, shutdownExecutorOnStop);
this.directory = directory;
this.locale = locale;
this.separator = separator;
this.clock = clock;
this.csvFileProvider = csvFileProvider;
this.histogramFormat = String.join(separator, "%d", "%d", "%f", "%d", "%f", "%f", "%f", "%f", "%f", "%f", "%f");
this.meterFormat = String.join(separator, "%d", "%f", "%f", "%f", "%f", "events/%s");
this.timerFormat = String.join(separator, "%d", "%f", "%f", "%f", "%f", "%f", "%f", "%f", "%f", "%f", "%f", "%f", "%f", "%f", "%f", "calls/%s", "%s");
this.timerHeader = String.join(separator, "count", "max", "mean", "min", "stddev", "p50", "p75", "p95", "p98", "p99", "p999", "mean_rate", "m1_rate", "m5_rate", "m15_rate", "rate_unit", "duration_unit");
this.meterHeader = String.join(separator, "count", "mean_rate", "m1_rate", "m5_rate", "m15_rate", "rate_unit");
this.histogramHeader = String.join(separator, "count", "max", "mean", "min", "stddev", "p50", "p75", "p95", "p98", "p99", "p999");
}
@Override
@SuppressWarnings("rawtypes")
public void report(SortedMap gauges,
SortedMap counters,
SortedMap histograms,
SortedMap meters,
SortedMap timers) {
final long timestamp = TimeUnit.MILLISECONDS.toSeconds(clock.getTime());
for (Map.Entry entry : gauges.entrySet()) {
reportGauge(timestamp, entry.getKey(), entry.getValue());
}
for (Map.Entry entry : counters.entrySet()) {
reportCounter(timestamp, entry.getKey(), entry.getValue());
}
for (Map.Entry entry : histograms.entrySet()) {
reportHistogram(timestamp, entry.getKey(), entry.getValue());
}
for (Map.Entry entry : meters.entrySet()) {
reportMeter(timestamp, entry.getKey(), entry.getValue());
}
for (Map.Entry entry : timers.entrySet()) {
reportTimer(timestamp, entry.getKey(), entry.getValue());
}
}
private void reportTimer(long timestamp, String name, Timer timer) {
final Snapshot snapshot = timer.getSnapshot();
report(timestamp,
name,
timerHeader,
timerFormat,
timer.getCount(),
convertDuration(snapshot.getMax()),
convertDuration(snapshot.getMean()),
convertDuration(snapshot.getMin()),
convertDuration(snapshot.getStdDev()),
convertDuration(snapshot.getMedian()),
convertDuration(snapshot.get75thPercentile()),
convertDuration(snapshot.get95thPercentile()),
convertDuration(snapshot.get98thPercentile()),
convertDuration(snapshot.get99thPercentile()),
convertDuration(snapshot.get999thPercentile()),
convertRate(timer.getMeanRate()),
convertRate(timer.getOneMinuteRate()),
convertRate(timer.getFiveMinuteRate()),
convertRate(timer.getFifteenMinuteRate()),
getRateUnit(),
getDurationUnit());
}
private void reportMeter(long timestamp, String name, Meter meter) {
report(timestamp,
name,
meterHeader,
meterFormat,
meter.getCount(),
convertRate(meter.getMeanRate()),
convertRate(meter.getOneMinuteRate()),
convertRate(meter.getFiveMinuteRate()),
convertRate(meter.getFifteenMinuteRate()),
getRateUnit());
}
private void reportHistogram(long timestamp, String name, Histogram histogram) {
final Snapshot snapshot = histogram.getSnapshot();
report(timestamp,
name,
histogramHeader,
histogramFormat,
histogram.getCount(),
snapshot.getMax(),
snapshot.getMean(),
snapshot.getMin(),
snapshot.getStdDev(),
snapshot.getMedian(),
snapshot.get75thPercentile(),
snapshot.get95thPercentile(),
snapshot.get98thPercentile(),
snapshot.get99thPercentile(),
snapshot.get999thPercentile());
}
private void reportCounter(long timestamp, String name, Counter counter) {
report(timestamp, name, "count", "%d", counter.getCount());
}
private void reportGauge(long timestamp, String name, Gauge> gauge) {
report(timestamp, name, "value", "%s", gauge.getValue());
}
private void report(long timestamp, String name, String header, String line, Object... values) {
try {
final File file = csvFileProvider.getFile(directory, name);
final boolean fileAlreadyExists = file.exists();
if (fileAlreadyExists || file.createNewFile()) {
try (PrintWriter out = new PrintWriter(new OutputStreamWriter(
new FileOutputStream(file, true), UTF_8))) {
if (!fileAlreadyExists) {
out.println("t" + separator + header);
}
out.printf(locale, String.format(locale, "%d" + separator + "%s%n", timestamp, line), values);
}
}
} catch (IOException e) {
LOGGER.warn("Error writing to {}", name, e);
}
}
protected String sanitize(String name) {
return name;
}
}