package com.codahale.metrics.collectd;

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.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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetAddress;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static com.codahale.metrics.MetricAttribute.COUNT;
import static com.codahale.metrics.MetricAttribute.M15_RATE;
import static com.codahale.metrics.MetricAttribute.M1_RATE;
import static com.codahale.metrics.MetricAttribute.M5_RATE;
import static com.codahale.metrics.MetricAttribute.MAX;
import static com.codahale.metrics.MetricAttribute.MEAN;
import static com.codahale.metrics.MetricAttribute.MEAN_RATE;
import static com.codahale.metrics.MetricAttribute.MIN;
import static com.codahale.metrics.MetricAttribute.P50;
import static com.codahale.metrics.MetricAttribute.P75;
import static com.codahale.metrics.MetricAttribute.P95;
import static com.codahale.metrics.MetricAttribute.P98;
import static com.codahale.metrics.MetricAttribute.P99;
import static com.codahale.metrics.MetricAttribute.P999;
import static com.codahale.metrics.MetricAttribute.STDDEV;

A reporter which publishes metric values to a Collectd server.
See Also:
/** * A reporter which publishes metric values to a Collectd server. * * @see <a href="https://collectd.org">collectd – The system statistics * collection daemon</a> */
public class CollectdReporter extends ScheduledReporter {
Returns a builder for the specified registry.

The default settings are:

  • hostName: InetAddress.getLocalHost().getHostName()
  • executor: default executor created by ScheduledReporter
  • shutdownExecutorOnStop: true
  • clock: Clock.defaultClock()
  • rateUnit: TimeUnit.SECONDS
  • durationUnit: TimeUnit.MILLISECONDS
  • filter: MetricFilter.ALL
  • securityLevel: NONE
  • username: ""
  • password: ""
/** * Returns a builder for the specified registry. * <p> * The default settings are: * <ul> * <li>hostName: InetAddress.getLocalHost().getHostName()</li> * <li>executor: default executor created by {@code ScheduledReporter}</li> * <li>shutdownExecutorOnStop: true</li> * <li>clock: Clock.defaultClock()</li> * <li>rateUnit: TimeUnit.SECONDS</li> * <li>durationUnit: TimeUnit.MILLISECONDS</li> * <li>filter: MetricFilter.ALL</li> * <li>securityLevel: NONE</li> * <li>username: ""</li> * <li>password: ""</li> * </ul> */
public static Builder forRegistry(MetricRegistry registry) { return new Builder(registry); } public static class Builder { private final MetricRegistry registry; private String hostName; private ScheduledExecutorService executor; private boolean shutdownExecutorOnStop = true; private Clock clock = Clock.defaultClock(); private TimeUnit rateUnit = TimeUnit.SECONDS; private TimeUnit durationUnit = TimeUnit.MILLISECONDS; private MetricFilter filter = MetricFilter.ALL; private SecurityLevel securityLevel = SecurityLevel.NONE; private String username = ""; private String password = ""; private Set<MetricAttribute> disabledMetricAttributes = Collections.emptySet(); private Builder(MetricRegistry registry) { this.registry = registry; } public Builder withHostName(String hostName) { this.hostName = hostName; return this; } public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop) { this.shutdownExecutorOnStop = shutdownExecutorOnStop; return this; } public Builder scheduleOn(ScheduledExecutorService executor) { this.executor = executor; return this; } public Builder withClock(Clock clock) { this.clock = clock; return this; } public Builder convertRatesTo(TimeUnit rateUnit) { this.rateUnit = rateUnit; return this; } public Builder convertDurationsTo(TimeUnit durationUnit) { this.durationUnit = durationUnit; return this; } public Builder filter(MetricFilter filter) { this.filter = filter; return this; } public Builder withUsername(String username) { this.username = username; return this; } public Builder withPassword(String password) { this.password = password; return this; } public Builder withSecurityLevel(SecurityLevel securityLevel) { this.securityLevel = securityLevel; return this; } public Builder disabledMetricAttributes(Set<MetricAttribute> attributes) { this.disabledMetricAttributes = attributes; return this; } public CollectdReporter build(Sender sender) { if (securityLevel != SecurityLevel.NONE) { if (username.isEmpty()) { throw new IllegalArgumentException("username is required for securityLevel: " + securityLevel); } if (password.isEmpty()) { throw new IllegalArgumentException("password is required for securityLevel: " + securityLevel); } } return new CollectdReporter(registry, hostName, sender, executor, shutdownExecutorOnStop, clock, rateUnit, durationUnit, filter, disabledMetricAttributes, username, password, securityLevel); } } private static final Logger LOG = LoggerFactory.getLogger(CollectdReporter.class); private static final String REPORTER_NAME = "collectd-reporter"; private static final String FALLBACK_HOST_NAME = "localhost"; private static final String COLLECTD_TYPE_GAUGE = "gauge"; private String hostName; private final Sender sender; private final Clock clock; private long period; private final PacketWriter writer; private CollectdReporter(MetricRegistry registry, String hostname, Sender sender, ScheduledExecutorService executor, boolean shutdownExecutorOnStop, Clock clock, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter, Set<MetricAttribute> disabledMetricAttributes, String username, String password, SecurityLevel securityLevel) { super(registry, REPORTER_NAME, filter, rateUnit, durationUnit, executor, shutdownExecutorOnStop, disabledMetricAttributes); this.hostName = (hostname != null) ? hostname : resolveHostName(); this.sender = sender; this.clock = clock; writer = new PacketWriter(sender, username, password, securityLevel); } private String resolveHostName() { try { return InetAddress.getLocalHost().getHostName(); } catch (Exception e) { LOG.error("Failed to lookup local host name: {}", e.getMessage(), e); return FALLBACK_HOST_NAME; } } @Override public void start(long period, TimeUnit unit) { this.period = period; super.start(period, unit); } @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) { MetaData.Builder metaData = new MetaData.Builder(hostName, clock.getTime() / 1000, period) .type(COLLECTD_TYPE_GAUGE); try { connect(sender); for (Map.Entry<String, Gauge> entry : gauges.entrySet()) { serializeGauge(metaData.plugin(entry.getKey()), entry.getValue()); } for (Map.Entry<String, Counter> entry : counters.entrySet()) { serializeCounter(metaData.plugin(entry.getKey()), entry.getValue()); } for (Map.Entry<String, Histogram> entry : histograms.entrySet()) { serializeHistogram(metaData.plugin(entry.getKey()), entry.getValue()); } for (Map.Entry<String, Meter> entry : meters.entrySet()) { serializeMeter(metaData.plugin(entry.getKey()), entry.getValue()); } for (Map.Entry<String, Timer> entry : timers.entrySet()) { serializeTimer(metaData.plugin(entry.getKey()), entry.getValue()); } } catch (IOException e) { LOG.warn("Unable to report to Collectd", e); } finally { disconnect(sender); } } private void connect(Sender sender) throws IOException { if (!sender.isConnected()) { sender.connect(); } } private void disconnect(Sender sender) { try { sender.disconnect(); } catch (Exception e) { LOG.warn("Error disconnecting from Collectd", e); } } private void writeValue(MetaData.Builder metaData, MetricAttribute attribute, Number value) { if (!getDisabledMetricAttributes().contains(attribute)) { write(metaData.typeInstance(attribute.getCode()).get(), value); } } private void writeRate(MetaData.Builder metaData, MetricAttribute attribute, double rate) { writeValue(metaData, attribute, convertRate(rate)); } private void writeDuration(MetaData.Builder metaData, MetricAttribute attribute, double duration) { writeValue(metaData, attribute, convertDuration(duration)); } private void write(MetaData metaData, Number value) { try { writer.write(metaData, value); } catch (RuntimeException e) { LOG.warn("Failed to process metric '" + metaData.getPlugin() + "': " + e.getMessage()); } catch (IOException e) { LOG.error("Failed to send metric to collectd", e); } } @SuppressWarnings("rawtypes") private void serializeGauge(MetaData.Builder metaData, Gauge metric) { if (metric.getValue() instanceof Number) { write(metaData.typeInstance("value").get(), (Number) metric.getValue()); } else if (metric.getValue() instanceof Boolean) { write(metaData.typeInstance("value").get(), ((Boolean) metric.getValue()) ? 1 : 0); } else { LOG.warn("Failed to process metric '{}'. Unsupported gauge of type: {} ", metaData.get().getPlugin(), metric.getValue().getClass().getName()); } } private void serializeMeter(MetaData.Builder metaData, Meter metric) { writeValue(metaData, COUNT, (double) metric.getCount()); writeRate(metaData, M1_RATE, metric.getOneMinuteRate()); writeRate(metaData, M5_RATE, metric.getFiveMinuteRate()); writeRate(metaData, M15_RATE, metric.getFifteenMinuteRate()); writeRate(metaData, MEAN_RATE, metric.getMeanRate()); } private void serializeCounter(MetaData.Builder metaData, Counter metric) { writeValue(metaData, COUNT, (double) metric.getCount()); } private void serializeHistogram(MetaData.Builder metaData, Histogram metric) { final Snapshot snapshot = metric.getSnapshot(); writeValue(metaData, COUNT, (double) metric.getCount()); writeValue(metaData, MAX, (double) snapshot.getMax()); writeValue(metaData, MEAN, snapshot.getMean()); writeValue(metaData, MIN, (double) snapshot.getMin()); writeValue(metaData, STDDEV, snapshot.getStdDev()); writeValue(metaData, P50, snapshot.getMedian()); writeValue(metaData, P75, snapshot.get75thPercentile()); writeValue(metaData, P95, snapshot.get95thPercentile()); writeValue(metaData, P98, snapshot.get98thPercentile()); writeValue(metaData, P99, snapshot.get99thPercentile()); writeValue(metaData, P999, snapshot.get999thPercentile()); } private void serializeTimer(MetaData.Builder metaData, Timer metric) { final Snapshot snapshot = metric.getSnapshot(); writeValue(metaData, COUNT, (double) metric.getCount()); writeDuration(metaData, MAX, (double) snapshot.getMax()); writeDuration(metaData, MEAN, snapshot.getMean()); writeDuration(metaData, MIN, (double) snapshot.getMin()); writeDuration(metaData, STDDEV, snapshot.getStdDev()); writeDuration(metaData, P50, snapshot.getMedian()); writeDuration(metaData, P75, snapshot.get75thPercentile()); writeDuration(metaData, P95, snapshot.get95thPercentile()); writeDuration(metaData, P98, snapshot.get98thPercentile()); writeDuration(metaData, P99, snapshot.get99thPercentile()); writeDuration(metaData, P999, snapshot.get999thPercentile()); writeRate(metaData, M1_RATE, metric.getOneMinuteRate()); writeRate(metaData, M5_RATE, metric.getFiveMinuteRate()); writeRate(metaData, M15_RATE, metric.getFifteenMinuteRate()); writeRate(metaData, MEAN_RATE, metric.getMeanRate()); } }