All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.circonus.metrics.CirconusReporter Maven / Gradle / Ivy

package com.circonus.metrics;

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.Metered;
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.circonus.metrics.model.CirconusCounter;
import com.circonus.metrics.model.CirconusGauge;
import com.circonus.metrics.model.CirconusHistogram;
import com.circonus.metrics.transport.Transport;
import com.circonus.metrics.HistImpl;
import com.circonus.metrics.HistImplContainer;
import com.circonus.metrics.CirconusMetricRegistryAlaCoda;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;

public class CirconusReporter extends ScheduledReporter {

  private static final Logger LOG = LoggerFactory.getLogger(CirconusReporter.class);

  private static final Expansion[] STATS_EXPANSIONS = { Expansion.MAX, Expansion.MEAN,
      Expansion.MIN, Expansion.STD_DEV, Expansion.MEDIAN, Expansion.P75, Expansion.P95,
      Expansion.P98, Expansion.P99, Expansion.P999 };
  private static final Expansion[] RATE_EXPANSIONS = { Expansion.RATE_1_MINUTE,
      Expansion.RATE_5_MINUTE, Expansion.RATE_15_MINUTE, Expansion.RATE_MEAN };

  private final Transport transport;
  private final Clock clock;
  private final String host;
  private final EnumSet expansions;
  private final MetricNameFormatter metricNameFormatter;
  private final List tags;
  private final String prefix;
  private final DynamicTagsCallback tagsCallback;
  private final Boolean suppress_bad_analytics;
  private Transport.Request request;

  private CirconusReporter(MetricRegistry metricRegistry,
                          Transport transport,
                          MetricFilter filter,
                          Clock clock,
                          String host,
                          EnumSet expansions,
                          TimeUnit rateUnit,
                          TimeUnit durationUnit,
                          MetricNameFormatter metricNameFormatter,
                          List tags,
                          String prefix,
                          DynamicTagsCallback tagsCallback,
                          Boolean suppress) {
    super(metricRegistry, "circonus-reporter", filter, rateUnit, durationUnit);
    if(!(metricRegistry instanceof CirconusMetricRegistryAlaCoda)) {
      LOG.warn("CirconusReporter started without a  CirconusMetricRegistry(AlaCoda) so no real histograms.");
    }
    this.clock = clock;
    this.host = host;
    this.expansions = expansions;
    this.metricNameFormatter = metricNameFormatter;
    this.tags = (tags == null) ? new ArrayList() : tags;
    this.transport = transport;
    this.prefix = prefix;
    this.tagsCallback = tagsCallback;
    this.suppress_bad_analytics = suppress;
  }

  @Override
  public void report(SortedMap gauges,
                     SortedMap counters,
                     SortedMap histograms,
                     SortedMap meters,
                     SortedMap timers) {
    final long timestamp = clock.getTime() / 1000;

    List newTags = tags;
    if (tagsCallback != null) {
      List dynamicTags = tagsCallback.getTags();
      if (dynamicTags != null && ! dynamicTags.isEmpty()) {
        newTags = TagUtils.mergeTags(tags, dynamicTags);
      }
    }

    try {
      request = transport.prepare();

      for (Map.Entry entry : gauges.entrySet()) {
        reportGauge(prefix(entry.getKey()), entry.getValue(), timestamp, newTags);
      }

      for (Map.Entry entry : counters.entrySet()) {
        reportCounter(prefix(entry.getKey()), entry.getValue(), timestamp, newTags);
      }

      for (Map.Entry entry : histograms.entrySet()) {
        reportHistogram(prefix(entry.getKey()), entry.getValue(), timestamp, newTags);
      }

      for (Map.Entry entry : meters.entrySet()) {
        reportMetered(prefix(entry.getKey()), entry.getValue(), timestamp, newTags);
      }

      for (Map.Entry entry : timers.entrySet()) {
        reportTimer(prefix(entry.getKey()), entry.getValue(), timestamp, newTags);
      }

      request.send();
    } catch (Throwable e) {
      LOG.error("Error reporting metrics to Circonus: " + e);
    }
  }

  private void reportTimer(String name, Timer timer, long timestamp, List tags)
      throws IOException {
    final Snapshot snapshot = timer.getSnapshot();

    LOG.debug("Timer[" +name + "] " + timer);
    if(timer instanceof HistImplContainer) {
      HistImpl take = ((HistImplContainer)timer).getHistImpl().copyAndReset();
      LOG.debug("Adding Circonus Histogram to report: " + name);
      request.addHistogram(new CirconusHistogram(
            name, take, timestamp, host, tags));
      if(suppress_bad_analytics) return;
    }

    double[] values = { snapshot.getMax(), snapshot.getMean(), snapshot.getMin(), snapshot.getStdDev(),
        snapshot.getMedian(), snapshot.get75thPercentile(), snapshot.get95thPercentile(), snapshot.get98thPercentile(),
        snapshot.get99thPercentile(), snapshot.get999thPercentile() };

    for (int i = 0; i < STATS_EXPANSIONS.length; i++) {
      if (expansions.contains(STATS_EXPANSIONS[i])) {
        request.addGauge(new CirconusGauge(
            appendExpansionSuffix(name, STATS_EXPANSIONS[i]),
            toNumber(convertDuration(values[i])),
            timestamp,
            host,
            tags));
      }
    }

    reportMetered(name, timer, timestamp, tags);
  }

  private void reportMetered(String name, Metered meter, long timestamp, List tags)
      throws IOException {
    if (expansions.contains(Expansion.COUNT)) {
      request.addGauge(new CirconusGauge(
          appendExpansionSuffix(name, Expansion.COUNT),
          meter.getCount(),
          timestamp,
          host,
          tags));
    }

    double[] values = { meter.getOneMinuteRate(), meter.getFiveMinuteRate(),
        meter.getFifteenMinuteRate(), meter.getMeanRate() };

    for (int i = 0; i < RATE_EXPANSIONS.length; i++) {
      if (expansions.contains(RATE_EXPANSIONS[i])) {
        request.addGauge(new CirconusGauge(
            appendExpansionSuffix(name, RATE_EXPANSIONS[i]),
            toNumber(convertRate(values[i])),
            timestamp,
            host,
            tags));
      }
    }
  }

  private void reportHistogram(String name, Histogram histogram, long timestamp, List tags)
      throws IOException {
    final Snapshot snapshot = histogram.getSnapshot();

    if(histogram instanceof HistImplContainer) {
      HistImpl take = ((HistImplContainer)histogram).getHistImpl().copyAndReset();
      LOG.debug("Adding Circonus Histogram to report: " + name);
      request.addHistogram(new CirconusHistogram(
            name, take, timestamp, host, tags));
      if(suppress_bad_analytics) return;
    }

    if (expansions.contains(Expansion.COUNT)) {
      request.addGauge(new CirconusGauge(
          appendExpansionSuffix(name, Expansion.COUNT),
          histogram.getCount(),
          timestamp,
          host,
          tags));
    }

    Number[] values = { snapshot.getMax(), snapshot.getMean(), snapshot.getMin(), snapshot.getStdDev(),
        snapshot.getMedian(), snapshot.get75thPercentile(), snapshot.get95thPercentile(), snapshot.get98thPercentile(),
        snapshot.get99thPercentile(), snapshot.get999thPercentile() };

    for (int i = 0; i < STATS_EXPANSIONS.length; i++) {
      if (expansions.contains(STATS_EXPANSIONS[i])) {
        request.addGauge(new CirconusGauge(
            appendExpansionSuffix(name, STATS_EXPANSIONS[i]),
            toNumber(values[i]),
            timestamp,
            host,
            tags));
      }
    }
  }

  private void reportCounter(String name, Counter counter, long timestamp, List tags)
      throws IOException {
    // A Metrics counter is actually a Circonus Gauge.  Circonus Counters are for rates which is
    // similar to the Metrics Meter type.  Metrics counters have increment and decrement
    // functionality, which implies they are instantaneously measurable, which implies they are
    // actually a gauge. The Metrics documentation agrees, stating:
    // "A counter is just a gauge for an AtomicLong instance. You can increment or decrement its
    // value. For example, we may want a more efficient way of measuring the pending job in a queue"
    request.addGauge(new CirconusGauge(metricNameFormatter.format(name), counter.getCount(),
        timestamp, host, tags));
  }

  private void reportGauge(String name, Gauge gauge, long timestamp, List tags)
      throws IOException {
    final Number value = toNumber(gauge.getValue());
    if (value != null) {
      request.addGauge(new CirconusGauge(metricNameFormatter.format(name), value, timestamp, host,
          tags));
    }
  }

  private Number toNumber(Object o) {
    if (o instanceof Number) {
      return (Number) o;
    }
    return null;
  }

  private String appendExpansionSuffix(String name, Expansion expansion) {
    return metricNameFormatter.format(name, expansion.toString());
  }

  private String prefix(String name) {
    if (prefix == null) {
      return name;
    } else {
      return String.format("%s.%s", prefix, name);
    }
  }

  public static enum Expansion {
    COUNT("count"),
    RATE_MEAN("meanRate"),
    RATE_1_MINUTE("1MinuteRate"),
    RATE_5_MINUTE("5MinuteRate"),
    RATE_15_MINUTE("15MinuteRate"),
    MIN("min"),
    MEAN("mean"),
    MAX("max"),
    STD_DEV("stddev"),
    MEDIAN("median"),
    P75("p75"),
    P95("p95"),
    P98("p98"),
    P99("p99"),
    P999("p999");

    public static EnumSet ALL = EnumSet.allOf(Expansion.class);

    private final String displayName;

    private Expansion(String displayName) {
      this.displayName = displayName;
    }

    @Override
    public String toString() {
      return displayName;
    }
  }

  public static Builder forRegistry(MetricRegistry registry) {
    return new Builder(registry);
  }

  public static class Builder {
    private final MetricRegistry registry;
    private String host;
    private EnumSet expansions;
    private Clock clock;
    private TimeUnit rateUnit;
    private TimeUnit durationUnit;
    private MetricFilter filter;
    private MetricNameFormatter metricNameFormatter;
    private List tags;
    private Transport transport;
    private String prefix;
    private DynamicTagsCallback tagsCallback;
    private Boolean suppress_bad_analytics;

    public Builder(MetricRegistry registry) {
      this.registry = registry;
      this.expansions = Expansion.ALL;
      this.clock = Clock.defaultClock();
      this.rateUnit = TimeUnit.SECONDS;
      this.durationUnit = TimeUnit.MILLISECONDS;
      this.filter = MetricFilter.ALL;
      this.metricNameFormatter = new DefaultMetricNameFormatter();
      this.tags = new ArrayList();
      this.suppress_bad_analytics = true;
    }

    public Builder onlyCirconusAnalytics(Boolean suppress) {
      this.suppress_bad_analytics = suppress;
      return this;
    }

    public Builder withHost(String host) {
      this.host = host;
      return this;
    }

    public Builder withEC2Host() throws IOException {
      this.host = AwsHelper.getEc2InstanceId();
      return this;
    }

    public Builder withExpansions(EnumSet expansions) {
      this.expansions = expansions;
      return this;
    }

    public Builder withDynamicTagCallback(DynamicTagsCallback tagsCallback) {
      this.tagsCallback = tagsCallback;
      return this;
    }

    public Builder convertRatesTo(TimeUnit rateUnit) {
      this.rateUnit = rateUnit;
      return this;
    }

    /**
     * Tags that would be sent to Circonus with each and every metrics. This could be used to set
     * global metrics like version of the app, environment etc.
     * @param tags List of tags eg: [env:prod, version:1.0.1, name:kafka_client] etc
     */
    public Builder withTags(List tags) {
      this.tags = tags;
      return this;
    }

    /**
     * Prefix all metric names with the given string.
     *
     * @param prefix The prefix for all metric names.
     */
    public Builder withPrefix(String prefix) {
      this.prefix = prefix;
      return this;
    }

    public Builder withClock(Clock clock) {
      this.clock = clock;
      return this;
    }

    public Builder filter(MetricFilter filter) {
      this.filter = filter;
      return this;
    }

    public Builder withMetricNameFormatter(MetricNameFormatter formatter) {
      this.metricNameFormatter = formatter;
      return this;
    }

    public Builder convertDurationsTo(TimeUnit durationUnit) {
      this.durationUnit = durationUnit;
      return this;
    }

    /**
     * The transport mechanism to push metrics to Circonus. Supports HTTPTrap.
     *
     * @see com.circonus.metrics.transport.HttpTransport
     */
    public Builder withTransport(Transport transport) {
      this.transport = transport;
      return this;
    }

    public CirconusReporter build() {
      if (transport == null) {
        LOG.error("Transport for Circonus reporter is null. No recording!");
      }
      return new CirconusReporter(
          this.registry,
          this.transport,
          this.filter,
          this.clock,
          this.host,
          this.expansions,
          this.rateUnit,
          this.durationUnit,
          this.metricNameFormatter,
          this.tags,
          this.prefix,
          this.tagsCallback,
          this.suppress_bad_analytics);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy