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.
/** * 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 Builder for CsvReporter.
Params:
  • registry – the registry to report
Returns:a Builder instance for a CsvReporter
/** * 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 CsvReporter instances. Defaults to using the default locale, converting rates to events/second, converting durations to milliseconds, and not filtering metrics.
/** * 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 scheduleOn(ScheduledExecutorService).
Params:
  • shutdownExecutorOnStop – if true, then executor will be stopped in same time with this reporter
Returns:this
/** * 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.
Params:
  • executor – the executor to use while scheduling reporting of metrics.
Returns: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 Locale.
Params:
Returns: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.
Params:
  • rateUnit – a unit of time
Returns: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.
Params:
  • durationUnit – a unit of time
Returns: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.
Params:
  • separator – the string to use for the separator.
Returns: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 Clock instance for the time.
Params:
  • clock – a Clock instance
Returns: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.
Params:
Returns: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 CsvReporter with the given properties, writing .csv files to the given directory.
Params:
  • directory – the directory in which the .csv files will be created
Returns:a CsvReporter
/** * 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 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"); } @Override @SuppressWarnings("rawtypes") public void report(SortedMap<String, Gauge> gauges, SortedMap<String, Counter> counters, SortedMap<String, Histogram> histograms, SortedMap<String, Meter> meters, SortedMap<String, Timer> timers) { final long timestamp = TimeUnit.MILLISECONDS.toSeconds(clock.getTime()); for (Map.Entry<String, Gauge> entry : gauges.entrySet()) { reportGauge(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry<String, Counter> entry : counters.entrySet()) { reportCounter(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry<String, Histogram> entry : histograms.entrySet()) { reportHistogram(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry<String, Meter> entry : meters.entrySet()) { reportMeter(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry<String, Timer> 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, "count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit,duration_unit", 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, "count,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit", 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, "count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999", 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," + 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; } }