com.wavefront.dropwizard.metrics.DropwizardMetricsReporter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wavefront-dropwizard-metrics-sdk-java Show documentation
Show all versions of wavefront-dropwizard-metrics-sdk-java Show documentation
Implements Dropwizard Metrics for collecting and sending metrics and histograms to Wavefront from Java applications.
package com.wavefront.dropwizard.metrics;
import com.codahale.metrics.Clock;
import com.codahale.metrics.Counter;
import com.codahale.metrics.DeltaCounter;
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.MetricAttribute;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.ScheduledReporter;
import com.codahale.metrics.Snapshot;
import com.codahale.metrics.Timer;
import com.codahale.metrics.WavefrontHistogram;
import com.codahale.metrics.jvm.BufferPoolMetricSet;
import com.codahale.metrics.jvm.ClassLoadingGaugeSet;
import com.codahale.metrics.jvm.FileDescriptorRatioGauge;
import com.codahale.metrics.jvm.GarbageCollectorMetricSet;
import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
import com.wavefront.sdk.common.WavefrontSender;
import com.wavefront.sdk.common.application.ApplicationTags;
import com.wavefront.sdk.common.metrics.WavefrontSdkCounter;
import com.wavefront.sdk.common.metrics.WavefrontSdkMetricsRegistry;
import com.wavefront.sdk.entities.histograms.HistogramGranularity;
import com.wavefront.sdk.entities.histograms.WavefrontHistogramImpl;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import static com.wavefront.sdk.common.Constants.APPLICATION_TAG_KEY;
import static com.wavefront.sdk.common.Constants.CLUSTER_TAG_KEY;
import static com.wavefront.sdk.common.Constants.DELTA_PREFIX;
import static com.wavefront.sdk.common.Constants.NULL_TAG_VAL;
import static com.wavefront.sdk.common.Constants.SDK_METRIC_PREFIX;
import static com.wavefront.sdk.common.Constants.SERVICE_TAG_KEY;
import static com.wavefront.sdk.common.Constants.SHARD_TAG_KEY;
/**
* A reporter which publishes metric values to a Wavefront cluster via proxy or direct ingestion
* from a Dropwizard {@link MetricRegistry}.
*
* @author Sushant Dewan ([email protected]).
*/
public class DropwizardMetricsReporter extends ScheduledReporter {
private static final Logger logger =
Logger.getLogger(DropwizardMetricsReporter.class.getCanonicalName());
/**
* Returns a new {@link Builder} for {@link DropwizardMetricsReporter}.
*
* @param registry the registry to report
* @return a {@link Builder} instance for a {@link DropwizardMetricsReporter}
*/
public static Builder forRegistry(MetricRegistry registry) {
return new Builder(registry);
}
/**
* A builder for {@link DropwizardMetricsReporter} instances. Defaults to not using a
* prefix, using the default clock, converting rates to events/second, converting durations to
* milliseconds, a host named "dropwizard-metrics", no point Tags, and not filtering any metrics.
*/
public static class Builder {
private final MetricRegistry registry;
private Clock clock;
private String prefix;
private MetricFilter filter;
private String source;
private final Map reporterPointTags;
private boolean includeJvmMetrics;
private Set disabledMetricAttributes;
private final Set histogramGranularities;
private Builder(MetricRegistry registry) {
this.registry = registry;
this.clock = Clock.defaultClock();
this.prefix = null;
this.filter = MetricFilter.ALL;
this.source = "dropwizard-metrics";
this.reporterPointTags = new HashMap<>();
this.includeJvmMetrics = false;
this.disabledMetricAttributes = Collections.emptySet();
this.histogramGranularities = new HashSet<>();
}
/**
* Use the given {@link Clock} instance for the time. Defaults to Clock.defaultClock()
*
* @param clock a {@link Clock} instance
* @return {@code this}
*/
public Builder withClock(Clock clock) {
this.clock = clock;
return this;
}
/**
* Prefix all metric names with the given string. Defaults to null.
*
* @param prefix the prefix for all metric names
* @return {@code this}
*/
public Builder prefixedWith(String prefix) {
this.prefix = prefix;
return this;
}
/**
* Set the source for this reporter. This is equivalent to withHost.
*
* @param source the host for all metrics
* @return {@code this}
*/
public Builder withSource(String source) {
this.source = source;
return this;
}
/**
* Add application tags to reporterPointTags.
*
* @param applicationTags application metadata.
* @return {@code this}
*/
public Builder withApplicationTags(ApplicationTags applicationTags) {
this.reporterPointTags.put(APPLICATION_TAG_KEY, applicationTags.getApplication());
this.reporterPointTags.put(SERVICE_TAG_KEY, applicationTags.getService());
this.reporterPointTags.put(CLUSTER_TAG_KEY,
applicationTags.getCluster() == null ? NULL_TAG_VAL : applicationTags.getCluster());
this.reporterPointTags.put(SHARD_TAG_KEY,
applicationTags.getShard() == null ? NULL_TAG_VAL : applicationTags.getShard());
if (applicationTags.getCustomTags() != null) {
this.reporterPointTags.putAll(applicationTags.getCustomTags());
}
return this;
}
/**
* Set the Point Tags for this reporter.
*
* @param reporterPointTags the pointTags Map for all metrics
* @return {@code this}
*/
public Builder withReporterPointTags(Map reporterPointTags) {
this.reporterPointTags.putAll(reporterPointTags);
return this;
}
/**
* Set a point tag for this reporter.
*
* @param tagKey the key of the Point Tag
* @param tagVal the value of the Point Tag
* @return {@code this}
*/
public Builder withReporterPointTag(String tagKey, String tagVal) {
this.reporterPointTags.put(tagKey, tagVal);
return this;
}
/**
* Only report metrics which match the given filter. Defaults to MetricFilter.ALL
*
* @param filter a {@link MetricFilter}
* @return {@code this}
*/
public Builder filter(MetricFilter filter) {
this.filter = filter;
return this;
}
/**
* Don't report the passed metric attributes for all metrics (e.g. "p999", "stddev" or "m15").
* See {@link MetricAttribute}.
*
* @param disabledMetricAttributes a set of {@link MetricAttribute}
* @return {@code this}
*/
public Builder disabledMetricAttributes(Set disabledMetricAttributes) {
this.disabledMetricAttributes = disabledMetricAttributes;
return this;
}
/**
* Include JVM Metrics from this Reporter.
*
* @return {@code this}
*/
public Builder withJvmMetrics() {
this.includeJvmMetrics = true;
return this;
}
/**
* Report histogram distributions aggregated into minute intervals
*
* @return {@code this}
*/
public Builder reportMinuteDistribution() {
this.histogramGranularities.add(HistogramGranularity.MINUTE);
return this;
}
/**
* Report histogram distributions aggregated into hour intervals
*
* @return {@code this}
*/
public Builder reportHourDistribution() {
this.histogramGranularities.add(HistogramGranularity.HOUR);
return this;
}
/**
* Report histogram distributions aggregated into day intervals
*
* @return {@code this}
*/
public Builder reportDayDistribution() {
this.histogramGranularities.add(HistogramGranularity.DAY);
return this;
}
/**
* Builds a {@link DropwizardMetricsReporter} with the given properties, sending metrics and
* histograms directly to a given Wavefront server using either proxy or direct ingestion APIs.
*
* @param wavefrontSender Wavefront Sender to send various Wavefront atoms.
* @return a {@link DropwizardMetricsReporter}
*/
public DropwizardMetricsReporter build(WavefrontSender wavefrontSender) {
return new DropwizardMetricsReporter(registry, wavefrontSender, clock, prefix,
source, reporterPointTags, filter, includeJvmMetrics,
disabledMetricAttributes, histogramGranularities);
}
}
private final WavefrontSender wavefrontSender;
private final Clock clock;
private final String prefix;
private final String source;
private final Map reporterPointTags;
private final Set histogramGranularities;
private final WavefrontSdkMetricsRegistry sdkMetricsRegistry;
private final WavefrontSdkCounter gaugesReported;
private final WavefrontSdkCounter deltaCountersReported;
private final WavefrontSdkCounter countersReported;
private final WavefrontSdkCounter wfHistogramsReported;
private final WavefrontSdkCounter histogramsReported;
private final WavefrontSdkCounter metersReported;
private final WavefrontSdkCounter timersReported;
private final WavefrontSdkCounter reportErrors;
private DropwizardMetricsReporter(MetricRegistry registry,
WavefrontSender wavefrontSender,
final Clock clock,
String prefix,
String source,
Map reporterPointTags,
MetricFilter filter,
boolean includeJvmMetrics,
Set disabledMetricAttributes,
Set histogramGranularities) {
super(registry, "wavefront-reporter", filter, TimeUnit.SECONDS, TimeUnit.MILLISECONDS,
Executors.newSingleThreadScheduledExecutor(), true,
disabledMetricAttributes == null ? Collections.emptySet() : disabledMetricAttributes);
this.wavefrontSender = wavefrontSender;
this.clock = clock;
this.prefix = prefix;
this.source = source;
this.reporterPointTags = reporterPointTags;
this.histogramGranularities = histogramGranularities;
if (includeJvmMetrics) {
tryRegister(registry, "jvm.uptime",
(Gauge) () -> ManagementFactory.getRuntimeMXBean().getUptime());
tryRegister(registry, "jvm.current_time", (Gauge) clock::getTime);
tryRegister(registry, "jvm.classes", new ClassLoadingGaugeSet());
tryRegister(registry, "jvm.fd_usage", new FileDescriptorRatioGauge());
tryRegister(registry, "jvm.buffers",
new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer()));
tryRegister(registry, "jvm.gc", new GarbageCollectorMetricSet());
tryRegister(registry, "jvm.memory", new MemoryUsageGaugeSet());
tryRegister(registry, "jvm.thread-states", new ThreadStatesGaugeSet());
}
sdkMetricsRegistry = new WavefrontSdkMetricsRegistry.Builder(this.wavefrontSender).
prefix(SDK_METRIC_PREFIX + ".dropwizard_metrics.reporter").
source(this.source).
tags(this.reporterPointTags).
build();
gaugesReported = sdkMetricsRegistry.newCounter("gauges.reported");
deltaCountersReported = sdkMetricsRegistry.newCounter("delta_counters.reported");
countersReported = sdkMetricsRegistry.newCounter("counters.reported");
wfHistogramsReported = sdkMetricsRegistry.newCounter("wavefront_histograms.reported");
histogramsReported = sdkMetricsRegistry.newCounter("histograms.reported");
metersReported = sdkMetricsRegistry.newCounter("meters.reported");
timersReported = sdkMetricsRegistry.newCounter("timers.reported");
reportErrors = sdkMetricsRegistry.newCounter("errors");
}
private void tryRegister(MetricRegistry registry, String name, T metric) {
// Dropwizard services automatically include JVM metrics, so adding them again would throw an exception
try {
registry.register(name, metric);
} catch (IllegalArgumentException e) {
logger.log(Level.INFO, e.getMessage());
}
}
@Override
public void report(SortedMap gauges,
SortedMap counters,
SortedMap histograms,
SortedMap meters,
SortedMap timers) {
try {
for (Map.Entry entry : gauges.entrySet()) {
if (entry.getValue().getValue() instanceof Number) {
reportGauge(entry.getKey(), entry.getValue());
gaugesReported.inc();
}
}
for (Map.Entry entry : counters.entrySet()) {
reportCounter(entry.getKey(), entry.getValue());
if (entry.getValue() instanceof DeltaCounter) {
deltaCountersReported.inc();
} else {
countersReported.inc();
}
}
for (Map.Entry entry : histograms.entrySet()) {
reportHistogram(entry.getKey(), entry.getValue());
if (entry.getValue() instanceof WavefrontHistogram) {
wfHistogramsReported.inc();
} else {
histogramsReported.inc();
}
}
for (Map.Entry entry : meters.entrySet()) {
reportMetered(entry.getKey(), entry.getValue());
metersReported.inc();
}
for (Map.Entry entry : timers.entrySet()) {
reportTimer(entry.getKey(), entry.getValue());
timersReported.inc();
}
} catch (IOException e) {
reportErrors.inc();
logger.log(Level.WARNING,"Unable to report to Wavefront", e);
}
}
/**
* Get total failure count reported by this reporter
*
* @return total failure count
*/
public int getFailureCount() {
return wavefrontSender.getFailureCount();
}
private void reportTimer(String name, Timer timer) throws IOException {
final Snapshot snapshot = timer.getSnapshot();
final long time = clock.getTime() / 1000;
sendIfEnabled(MetricAttribute.MAX, name, convertDuration(snapshot.getMax()), time);
sendIfEnabled(MetricAttribute.MEAN, name, convertDuration(snapshot.getMean()), time);
sendIfEnabled(MetricAttribute.MIN, name, convertDuration(snapshot.getMin()), time);
sendIfEnabled(MetricAttribute.STDDEV, name, convertDuration(snapshot.getStdDev()), time);
sendIfEnabled(MetricAttribute.P50, name, convertDuration(snapshot.getMedian()), time);
sendIfEnabled(MetricAttribute.P75, name, convertDuration(snapshot.get75thPercentile()), time);
sendIfEnabled(MetricAttribute.P95, name, convertDuration(snapshot.get95thPercentile()), time);
sendIfEnabled(MetricAttribute.P98, name, convertDuration(snapshot.get98thPercentile()), time);
sendIfEnabled(MetricAttribute.P99, name, convertDuration(snapshot.get99thPercentile()), time);
sendIfEnabled(MetricAttribute.P999, name, convertDuration(snapshot.get999thPercentile()), time);
reportMetered(name, timer);
}
private void reportMetered(String name, Metered meter) throws IOException {
final long time = clock.getTime() / 1000;
sendIfEnabled(MetricAttribute.COUNT, name, meter.getCount(), time);
sendIfEnabled(MetricAttribute.M1_RATE, name, convertRate(meter.getOneMinuteRate()), time);
sendIfEnabled(MetricAttribute.M5_RATE, name, convertRate(meter.getFiveMinuteRate()), time);
sendIfEnabled(MetricAttribute.M15_RATE, name, convertRate(meter.getFifteenMinuteRate()), time);
sendIfEnabled(MetricAttribute.MEAN_RATE, name, convertRate(meter.getMeanRate()), time);
}
private void reportHistogram(String name, Histogram histogram) throws IOException {
if (histogram instanceof WavefrontHistogram) {
String histogramName = prefixAndSanitize(name);
for (WavefrontHistogramImpl.Distribution distribution :
((WavefrontHistogram) histogram).flushDistributions()) {
wavefrontSender.sendDistribution(histogramName, distribution.centroids,
histogramGranularities, distribution.timestamp, source, reporterPointTags);
}
} else {
final Snapshot snapshot = histogram.getSnapshot();
final long time = clock.getTime() / 1000;
sendIfEnabled(MetricAttribute.COUNT, name, histogram.getCount(), time);
sendIfEnabled(MetricAttribute.MAX, name, snapshot.getMax(), time);
sendIfEnabled(MetricAttribute.MEAN, name, snapshot.getMean(), time);
sendIfEnabled(MetricAttribute.MIN, name, snapshot.getMin(), time);
sendIfEnabled(MetricAttribute.STDDEV, name, snapshot.getStdDev(), time);
sendIfEnabled(MetricAttribute.P50, name, snapshot.getMedian(), time);
sendIfEnabled(MetricAttribute.P75, name, snapshot.get75thPercentile(), time);
sendIfEnabled(MetricAttribute.P95, name, snapshot.get95thPercentile(), time);
sendIfEnabled(MetricAttribute.P98, name, snapshot.get98thPercentile(), time);
sendIfEnabled(MetricAttribute.P99, name, snapshot.get99thPercentile(), time);
sendIfEnabled(MetricAttribute.P999, name, snapshot.get999thPercentile(), time);
}
}
private void reportCounter(String name, Counter counter) throws IOException {
if (counter instanceof DeltaCounter) {
long count = counter.getCount();
if (count > 0) {
name = DELTA_PREFIX + prefixAndSanitize(name.substring(1), "count");
wavefrontSender.sendDeltaCounter(name, count, source, reporterPointTags);
counter.dec(count);
}
} else {
wavefrontSender.sendMetric(prefixAndSanitize(name, "count"), counter.getCount(),
clock.getTime() / 1000, source, reporterPointTags);
}
}
private void reportGauge(String name, Gauge gauge) throws IOException {
wavefrontSender.sendMetric(prefixAndSanitize(name), gauge.getValue().doubleValue(),
clock.getTime() / 1000, source, reporterPointTags);
}
private void sendIfEnabled(MetricAttribute type, String name, double value, long timestamp)
throws IOException {
if (!getDisabledMetricAttributes().contains(type)) {
wavefrontSender.sendMetric(prefixAndSanitize(name, type.getCode()), value, timestamp,
source, reporterPointTags);
}
}
private String prefixAndSanitize(String... components) {
return sanitize(MetricRegistry.name(prefix, components));
}
private static String sanitize(String name) {
return SIMPLE_NAMES.matcher(name).replaceAll("_");
}
private static final Pattern SIMPLE_NAMES = Pattern.compile("[^a-zA-Z0-9_.\\-~]");
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy