package org.stagemonitor.core.metrics; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import com.codahale.metrics.Counter; import com.codahale.metrics.Gauge; import com.codahale.metrics.Histogram; import com.codahale.metrics.Meter; import com.codahale.metrics.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.stagemonitor.core.metrics.metrics2.Metric2Registry; import org.stagemonitor.core.metrics.metrics2.MetricName; import org.stagemonitor.core.metrics.metrics2.ScheduledMetrics2Reporter; /** * The MetricsAggregationReporter computes aggregated values of all the metrics and hands them over to a list of other * reporters on server shutdown. */ public class MetricsAggregationReporter extends ScheduledMetrics2Reporter { private final Logger logger = LoggerFactory.getLogger(getClass()); private final List<ScheduledMetrics2Reporter> onShutdownReporters; private Map<MetricName, Gauge> aggregatedGauges = new HashMap<MetricName, Gauge>(); private Map<MetricName, Counter> counters = new HashMap<MetricName, Counter>(); private Map<MetricName, Histogram> aggregatedHistograms = new HashMap<MetricName, Histogram>(); private Map<MetricName, Meter> meters = new HashMap<MetricName, Meter>(); private Map<MetricName, Timer> aggregatedTimers = new HashMap<MetricName, Timer>(); public static Builder forRegistry(Metric2Registry registry) { return new Builder(registry); } public MetricsAggregationReporter(Builder builder) { super(builder); this.onShutdownReporters = builder.getOnShutdownReporters(); } @Override public void reportMetrics(Map<MetricName, Gauge> gauges, Map<MetricName, Counter> counters, Map<MetricName, Histogram> histograms, Map<MetricName, Meter> meters, Map<MetricName, Timer> timers) { for (Map.Entry<MetricName, Gauge> entry : gauges.entrySet()) { if (entry.getValue().getValue() instanceof Number) { final Gauge<Number> gauge = (Gauge<Number>) entry.getValue(); if (aggregatedGauges.containsKey(entry.getKey())) { ((AggregatedGauge) aggregatedGauges.get(entry.getKey())).add(gauge); } else { aggregatedGauges.put(entry.getKey(), new AggregatedGauge(gauge)); } } else { aggregatedGauges.put(entry.getKey(), entry.getValue()); } } this.counters = counters; for (Map.Entry<MetricName, Histogram> entry : histograms.entrySet()) { if (aggregatedHistograms.containsKey(entry.getKey())) { ((AggregatedHistogram) aggregatedHistograms.get(entry.getKey())).add(entry.getValue()); } else { aggregatedHistograms.put(entry.getKey(), new AggregatedHistogram(entry.getValue())); } } this.meters = meters; for (Map.Entry<MetricName, Timer> entry : timers.entrySet()) { if (aggregatedTimers.containsKey(entry.getKey())) { ((AggregatedTimer) aggregatedTimers.get(entry.getKey())).add(entry.getValue()); } else { aggregatedTimers.put(entry.getKey(), new AggregatedTimer(entry.getValue())); } } } /** * Should be called just before the server is shutting down. * The aggregated metrics are then reported by the {@link #onShutdownReporters} */ public void onShutDown() { for (ScheduledMetrics2Reporter onShutdownReporter : onShutdownReporters) { try { onShutdownReporter.reportMetrics(aggregatedGauges, counters, aggregatedHistograms, meters, aggregatedTimers); } catch (RuntimeException e) { logger.warn(e.getMessage() + " (this exception was ignored)", e); } } } /** * Computes the average without storing all measurements * * @param average the current average (initially 0) * @param count the number of measurements (initially 0) * @param newValue the value of the current measurement * @return the arithmetic mean */ public static double computeMovingAverage(double average, int count, double newValue) { return (average * count + newValue) / (count + 1); } public static class Builder extends ScheduledMetrics2Reporter.Builder<MetricsAggregationReporter, Builder> { private final List<ScheduledMetrics2Reporter> onShutdownReporters = new LinkedList<ScheduledMetrics2Reporter>(); /** * @param registry the registry to report */ public Builder(Metric2Registry registry) { super(registry, "stagemonitor-aggregation-reporter"); } public List<ScheduledMetrics2Reporter> getOnShutdownReporters() { return onShutdownReporters; } public Builder addOnShutdownReporter(ScheduledMetrics2Reporter reporter) { onShutdownReporters.add(reporter); return this; } @Override public MetricsAggregationReporter build() { return new MetricsAggregationReporter(this); } public Builder onShutdownReporters(List<ScheduledMetrics2Reporter> onShutdownReporters) { this.onShutdownReporters.addAll(onShutdownReporters); return this; } } }