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

io.vertx.ext.dropwizard.reporters.JmxReporter Maven / Gradle / Ivy

The newest version!
/**
 * A reporter which listens for new metrics and exposes them as namespaced MBeans.
 *
 * Copyright 2010-2013 Coda Hale and Yammer, Inc.
 *
 * NOTE: This class has been forked from the http://metrics.codahale.com/ project which is licensed under aslv2.
 *  -- Changes --
 *  2014-08-07 nscavell: remove the dependency on slf4j and use the Vert.x logger instead.
 */

package io.vertx.ext.dropwizard.reporters;

import com.codahale.metrics.*;
import io.vertx.core.internal.logging.Logger;
import io.vertx.core.internal.logging.LoggerFactory;
import io.vertx.ext.dropwizard.ThroughputMeter;
import io.vertx.ext.dropwizard.ThroughputTimer;

import javax.management.*;
import java.io.Closeable;
import java.lang.management.ManagementFactory;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;

public class JmxReporter implements Closeable {
  /**
   * Returns a new {@link Builder} for {@link JmxReporter}.
   *
   * @param registry the registry to report
   * @return a {@link Builder} instance for a {@link JmxReporter}
   */
  public static Builder forRegistry(MetricRegistry registry) {
    return new Builder(registry);
  }

  /**
   * A builder for {@link JmxReporter} instances. Defaults to using the default MBean server and
   * not filtering metrics.
   */
  public static class Builder {
    private final MetricRegistry registry;
    private MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
    private TimeUnit rateUnit;
    private TimeUnit durationUnit;
    private MetricFilter filter = MetricFilter.ALL;
    private String domain;
    private Map specificDurationUnits;
    private Map specificRateUnits;

    private Builder(MetricRegistry registry) {
      this.registry = registry;
      this.rateUnit = TimeUnit.SECONDS;
      this.durationUnit = TimeUnit.MILLISECONDS;
      this.domain = "metrics";
      this.specificDurationUnits = Collections.emptyMap();
      this.specificRateUnits = Collections.emptyMap();
    }

    /**
     * Register MBeans with the given {@link javax.management.MBeanServer}.
     *
     * @param mBeanServer     an {@link javax.management.MBeanServer}
     * @return {@code this}
     */
    public Builder registerWith(MBeanServer mBeanServer) {
      this.mBeanServer = mBeanServer;
      return 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.
     *
     * @param durationUnit a unit of time
     * @return {@code this}
     */
    public Builder convertDurationsTo(TimeUnit durationUnit) {
      this.durationUnit = durationUnit;
      return this;
    }

    /**
     * Only report metrics which match the given filter.
     *
     * @param filter a {@link com.codahale.metrics.MetricFilter}
     * @return {@code this}
     */
    public Builder filter(MetricFilter filter) {
      this.filter = filter;
      return this;
    }

    public Builder inDomain(String domain) {
      this.domain = domain;
      return this;
    }

    /**
     * Use specific {@link java.util.concurrent.TimeUnit}s for the duration of the metrics with these names.
     *
     * @param specificDurationUnits a map of metric names and specific {@link java.util.concurrent.TimeUnit}s
     * @return {@code this}
     */
    public Builder specificDurationUnits(Map specificDurationUnits) {
      this.specificDurationUnits = Collections.unmodifiableMap(specificDurationUnits);
      return this;
    }


    /**
     * Use specific {@link java.util.concurrent.TimeUnit}s for the rate of the metrics with these names.
     *
     * @param specificRateUnits a map of metric names and specific {@link java.util.concurrent.TimeUnit}s
     * @return {@code this}
     */
    public Builder specificRateUnits(Map specificRateUnits) {
      this.specificRateUnits = Collections.unmodifiableMap(specificRateUnits);
      return this;
    }

    /**
     * Builds a {@link JmxReporter} with the given properties.
     *
     * @return a {@link JmxReporter}
     */
    public JmxReporter build() {
      final MetricTimeUnits timeUnits = new MetricTimeUnits(rateUnit, durationUnit, specificRateUnits, specificDurationUnits);
      return new JmxReporter(mBeanServer, domain, registry, filter, timeUnits);
    }
  }

  private static final Logger LOGGER = LoggerFactory.getLogger(JmxReporter.class);

  // CHECKSTYLE:OFF
  @SuppressWarnings("UnusedDeclaration")
  public interface MetricMBean {
    ObjectName objectName();
  }
  // CHECKSTYLE:ON


  private abstract static class AbstractBean implements MetricMBean {
    private final ObjectName objectName;

    AbstractBean(ObjectName objectName) {
      this.objectName = objectName;
    }

    @Override
    public ObjectName objectName() {
      return objectName;
    }
  }

  // CHECKSTYLE:OFF
  @SuppressWarnings("UnusedDeclaration")
  public interface JmxGaugeMBean extends MetricMBean {
    Object getValue();
  }
  // CHECKSTYLE:ON

  private static class JmxGauge extends AbstractBean implements JmxGaugeMBean {
    private final Gauge metric;

    private JmxGauge(Gauge metric, ObjectName objectName) {
      super(objectName);
      this.metric = metric;
    }

    @Override
    public Object getValue() {
      return metric.getValue();
    }
  }

  // CHECKSTYLE:OFF
  @SuppressWarnings("UnusedDeclaration")
  public interface JmxCounterMBean extends MetricMBean {
    long getCount();
  }
  // CHECKSTYLE:ON

  private static class JmxCounter extends AbstractBean implements JmxCounterMBean {
    private final Counter metric;

    private JmxCounter(Counter metric, ObjectName objectName) {
      super(objectName);
      this.metric = metric;
    }

    @Override
    public long getCount() {
      return metric.getCount();
    }
  }

  // CHECKSTYLE:OFF
  @SuppressWarnings("UnusedDeclaration")
  public interface JmxHistogramMBean extends MetricMBean {
    long getCount();

    long getMin();

    long getMax();

    double getMean();

    double getStdDev();

    double get50thPercentile();

    double get75thPercentile();

    double get95thPercentile();

    double get98thPercentile();

    double get99thPercentile();

    double get999thPercentile();

    long[] values();
  }
  // CHECKSTYLE:ON

  private static class JmxHistogram implements JmxHistogramMBean {
    private final ObjectName objectName;
    private final Histogram metric;

    private JmxHistogram(Histogram metric, ObjectName objectName) {
      this.metric = metric;
      this.objectName = objectName;
    }

    @Override
    public ObjectName objectName() {
      return objectName;
    }

    @Override
    public double get50thPercentile() {
      return metric.getSnapshot().getMedian();
    }

    @Override
    public long getCount() {
      return metric.getCount();
    }

    @Override
    public long getMin() {
      return metric.getSnapshot().getMin();
    }

    @Override
    public long getMax() {
      return metric.getSnapshot().getMax();
    }

    @Override
    public double getMean() {
      return metric.getSnapshot().getMean();
    }

    @Override
    public double getStdDev() {
      return metric.getSnapshot().getStdDev();
    }

    @Override
    public double get75thPercentile() {
      return metric.getSnapshot().get75thPercentile();
    }

    @Override
    public double get95thPercentile() {
      return metric.getSnapshot().get95thPercentile();
    }

    @Override
    public double get98thPercentile() {
      return metric.getSnapshot().get98thPercentile();
    }

    @Override
    public double get99thPercentile() {
      return metric.getSnapshot().get99thPercentile();
    }

    @Override
    public double get999thPercentile() {
      return metric.getSnapshot().get999thPercentile();
    }

    @Override
    public long[] values() {
      return metric.getSnapshot().getValues();
    }
  }

  //CHECKSTYLE:OFF
  @SuppressWarnings("UnusedDeclaration")
  public interface JmxMeterMBean extends MetricMBean {
    long getCount();

    double getMeanRate();

    double getOneMinuteRate();

    double getFiveMinuteRate();

    double getFifteenMinuteRate();

    String getRateUnit();
  }
  //CHECKSTYLE:ON

  //CHECKSTYLE:OFF
  @SuppressWarnings("UnusedDeclaration")
  public interface JmxThroughputMeterMBean extends JmxMeterMBean {
    double getInstantThroughput();
  }
  //CHECKSTYLE:ON


  private static class JmxMeter extends AbstractBean implements JmxMeterMBean {
    private final Metered metric;
    private final double rateFactor;
    private final String rateUnit;

    private JmxMeter(Metered metric, ObjectName objectName, TimeUnit rateUnit) {
      super(objectName);
      this.metric = metric;
      this.rateFactor = rateUnit.toSeconds(1);
      this.rateUnit = "events/" + calculateRateUnit(rateUnit);
    }

    @Override
    public long getCount() {
      return metric.getCount();
    }

    @Override
    public double getMeanRate() {
      return metric.getMeanRate() * rateFactor;
    }

    @Override
    public double getOneMinuteRate() {
      return metric.getOneMinuteRate() * rateFactor;
    }

    @Override
    public double getFiveMinuteRate() {
      return metric.getFiveMinuteRate() * rateFactor;
    }

    @Override
    public double getFifteenMinuteRate() {
      return metric.getFifteenMinuteRate() * rateFactor;
    }

    @Override
    public String getRateUnit() {
      return rateUnit;
    }

    private String calculateRateUnit(TimeUnit unit) {
      final String s = unit.toString().toLowerCase(Locale.US);
      return s.substring(0, s.length() - 1);
    }
  }

  private static class JmxThroughputMeter extends JmxMeter implements JmxThroughputMeterMBean {

    private ThroughputMeter metric;

    private JmxThroughputMeter(Metered metric, ObjectName objectName, TimeUnit rateUnit) {
      super(metric, objectName, rateUnit);
      this.metric = (ThroughputMeter) metric;
    }

    @Override
    public double getInstantThroughput() {
      return metric.getValue();
    }
  }

  // CHECKSTYLE:OFF
  @SuppressWarnings("UnusedDeclaration")
  public interface JmxTimerMBean extends JmxMeterMBean {
    double getMin();

    double getMax();

    double getMean();

    double getStdDev();

    double get50thPercentile();

    double get75thPercentile();

    double get95thPercentile();

    double get98thPercentile();

    double get99thPercentile();

    double get999thPercentile();

    long[] values();

    String getDurationUnit();
  }
  // CHECKSTYLE:ON

  // CHECKSTYLE:OFF
  @SuppressWarnings("UnusedDeclaration")
  public interface JmxThroughputTimerMBean extends JmxTimerMBean {
    double getInstantThroughput();
  }
  // CHECKSTYLE:ON


  static class JmxTimer extends JmxMeter implements JmxTimerMBean {
    private final Timer metric;
    private final double durationFactor;
    private final String durationUnit;

    private JmxTimer(Timer metric,
                     ObjectName objectName,
                     TimeUnit rateUnit,
                     TimeUnit durationUnit) {
      super(metric, objectName, rateUnit);
      this.metric = metric;
      this.durationFactor = 1.0 / durationUnit.toNanos(1);
      this.durationUnit = durationUnit.toString().toLowerCase(Locale.US);
    }

    @Override
    public double get50thPercentile() {
      return metric.getSnapshot().getMedian() * durationFactor;
    }

    @Override
    public double getMin() {
      return metric.getSnapshot().getMin() * durationFactor;
    }

    @Override
    public double getMax() {
      return metric.getSnapshot().getMax() * durationFactor;
    }

    @Override
    public double getMean() {
      return metric.getSnapshot().getMean() * durationFactor;
    }

    @Override
    public double getStdDev() {
      return metric.getSnapshot().getStdDev() * durationFactor;
    }

    @Override
    public double get75thPercentile() {
      return metric.getSnapshot().get75thPercentile() * durationFactor;
    }

    @Override
    public double get95thPercentile() {
      return metric.getSnapshot().get95thPercentile() * durationFactor;
    }

    @Override
    public double get98thPercentile() {
      return metric.getSnapshot().get98thPercentile() * durationFactor;
    }

    @Override
    public double get99thPercentile() {
      return metric.getSnapshot().get99thPercentile() * durationFactor;
    }

    @Override
    public double get999thPercentile() {
      return metric.getSnapshot().get999thPercentile() * durationFactor;
    }

    @Override
    public long[] values() {
      return metric.getSnapshot().getValues();
    }

    @Override
    public String getDurationUnit() {
      return durationUnit;
    }
  }

  static class JmxThroughputTimer extends JmxTimer implements JmxThroughputTimerMBean {
    private final ThroughputTimer metric;

    private JmxThroughputTimer(Timer metric,
                               ObjectName objectName,
                               TimeUnit rateUnit,
                               TimeUnit durationUnit) {
      super(metric, objectName, rateUnit, durationUnit);
      this.metric = (ThroughputTimer) metric;
    }

    @Override
    public double getInstantThroughput() {
      return metric.getValue();
    }
  }

  private static class JmxListener implements MetricRegistryListener {
    private final String name;
    private final MBeanServer mBeanServer;
    private final MetricFilter filter;
    private final MetricTimeUnits timeUnits;
    private final Set registered;

    private JmxListener(MBeanServer mBeanServer, String name, MetricFilter filter, MetricTimeUnits timeUnits) {
      this.mBeanServer = mBeanServer;
      this.name = name;
      this.filter = filter;
      this.timeUnits = timeUnits;
      this.registered = new CopyOnWriteArraySet();
    }

    @Override
    public void onGaugeAdded(String name, Gauge gauge) {
      try {
        if (filter.matches(name, gauge)) {
          final ObjectName objectName = createName("gauges", name);
          mBeanServer.registerMBean(new JmxGauge(gauge, objectName), objectName);
          registered.add(objectName);
        }
      } catch (InstanceAlreadyExistsException e) {
        LOGGER.debug("Unable to register gauge", e);
      } catch (JMException e) {
        LOGGER.warn("Unable to register gauge", e);
      }
    }

    @Override
    public void onGaugeRemoved(String name) {
      try {
        final ObjectName objectName = createName("gauges", name);
        mBeanServer.unregisterMBean(objectName);
        registered.remove(objectName);
      } catch (InstanceNotFoundException e) {
        LOGGER.debug("Unable to unregister gauge", e);
      } catch (MBeanRegistrationException e) {
        LOGGER.warn("Unable to unregister gauge", e);
      }
    }

    @Override
    public void onCounterAdded(String name, Counter counter) {
      try {
        if (filter.matches(name, counter)) {
          final ObjectName objectName = createName("counters", name);
          mBeanServer.registerMBean(new JmxCounter(counter, objectName), objectName);
          registered.add(objectName);
        }
      } catch (InstanceAlreadyExistsException e) {
        LOGGER.debug("Unable to register counter", e);
      } catch (JMException e) {
        LOGGER.warn("Unable to register counter", e);
      }
    }

    @Override
    public void onCounterRemoved(String name) {
      try {
        final ObjectName objectName = createName("counters", name);
        mBeanServer.unregisterMBean(objectName);
        registered.remove(objectName);
      } catch (InstanceNotFoundException e) {
        LOGGER.debug("Unable to unregister counter", e);
      } catch (MBeanRegistrationException e) {
        LOGGER.warn("Unable to unregister counter", e);
      }
    }

    @Override
    public void onHistogramAdded(String name, Histogram histogram) {
      try {
        if (filter.matches(name, histogram)) {
          final ObjectName objectName = createName("histograms", name);
          mBeanServer.registerMBean(new JmxHistogram(histogram, objectName), objectName);
          registered.add(objectName);
        }
      } catch (InstanceAlreadyExistsException e) {
        LOGGER.debug("Unable to register histogram", e);
      } catch (JMException e) {
        LOGGER.warn("Unable to register histogram", e);
      }
    }

    @Override
    public void onHistogramRemoved(String name) {
      try {
        final ObjectName objectName = createName("histograms", name);
        mBeanServer.unregisterMBean(objectName);
        registered.remove(objectName);
      } catch (InstanceNotFoundException e) {
        LOGGER.debug("Unable to unregister histogram", e);
      } catch (MBeanRegistrationException e) {
        LOGGER.warn("Unable to unregister histogram", e);
      }
    }

    @Override
    public void onMeterAdded(String name, Meter meter) {
      try {
        if (filter.matches(name, meter)) {
          final ObjectName objectName = createName("meters", name);

          if (meter instanceof ThroughputMeter) {
            mBeanServer.registerMBean(new JmxThroughputMeter(meter, objectName, timeUnits.rateFor(name)), objectName);
          } else {
            mBeanServer.registerMBean(new JmxMeter(meter, objectName, timeUnits.rateFor(name)), objectName);
          }

          registered.add(objectName);
        }
      } catch (InstanceAlreadyExistsException e) {
        LOGGER.debug("Unable to register meter", e);
      } catch (JMException e) {
        LOGGER.warn("Unable to register meter", e);
      }
    }

    @Override
    public void onMeterRemoved(String name) {
      try {
        final ObjectName objectName = createName("meters", name);
        mBeanServer.unregisterMBean(objectName);
        registered.remove(objectName);
      } catch (InstanceNotFoundException e) {
        LOGGER.debug("Unable to unregister meter", e);
      } catch (MBeanRegistrationException e) {
        LOGGER.warn("Unable to unregister meter", e);
      }
    }

    @Override
    public void onTimerAdded(String name, Timer timer) {
      try {
        if (filter.matches(name, timer)) {
          final ObjectName objectName = createName("timers", name);

          if (timer instanceof ThroughputTimer) {
            mBeanServer.registerMBean(new JmxThroughputTimer(timer, objectName, timeUnits.rateFor(name), timeUnits.durationFor(name)), objectName);
          } else {
            mBeanServer.registerMBean(new JmxTimer(timer, objectName, timeUnits.rateFor(name), timeUnits.durationFor(name)), objectName);
          }

          registered.add(objectName);
        }
      } catch (InstanceAlreadyExistsException e) {
        LOGGER.debug("Unable to register timer", e);
      } catch (JMException e) {
        LOGGER.warn("Unable to register timer", e);
      }
    }

    @Override
    public void onTimerRemoved(String name) {
      try {
        final ObjectName objectName = createName("timers", name);
        mBeanServer.unregisterMBean(objectName);
        registered.remove(objectName);
      } catch (InstanceNotFoundException e) {
        LOGGER.debug("Unable to unregister timer", e);
      } catch (MBeanRegistrationException e) {
        LOGGER.warn("Unable to unregister timer", e);
      }
    }

    private ObjectName createName(String type, String name) {
      try {
        return new ObjectName(this.name, "name", name);
      } catch (MalformedObjectNameException e) {
        try {
          return new ObjectName(this.name, "name", ObjectName.quote(name));
        } catch (MalformedObjectNameException e1) {
          LOGGER.warn("Unable to register " + type + " " + name, e1);
          throw new RuntimeException(e1);
        }
      }
    }

    void unregisterAll() {
      for (ObjectName name : registered) {
        try {
          mBeanServer.unregisterMBean(name);
        } catch (InstanceNotFoundException e) {
          LOGGER.debug("Unable to unregister metric", e);
        } catch (MBeanRegistrationException e) {
          LOGGER.warn("Unable to unregister metric", e);
        }
      }
      registered.clear();
    }
  }

  private static class MetricTimeUnits {
    private final TimeUnit defaultRate;
    private final TimeUnit defaultDuration;
    private final Map rateOverrides;
    private final Map durationOverrides;

    MetricTimeUnits(TimeUnit defaultRate,
                    TimeUnit defaultDuration,
                    Map rateOverrides,
                    Map durationOverrides) {
      this.defaultRate = defaultRate;
      this.defaultDuration = defaultDuration;
      this.rateOverrides = rateOverrides;
      this.durationOverrides = durationOverrides;
    }

    public TimeUnit durationFor(String name) {
      return durationOverrides.containsKey(name) ? durationOverrides.get(name) : defaultDuration;
    }

    public TimeUnit rateFor(String name) {
      return rateOverrides.containsKey(name) ? rateOverrides.get(name) : defaultRate;
    }
  }

  private final MetricRegistry registry;
  private final JmxListener listener;

  private JmxReporter(MBeanServer mBeanServer,
                      String domain,
                      MetricRegistry registry,
                      MetricFilter filter,
                      MetricTimeUnits timeUnits) {
    this.registry = registry;
    this.listener = new JmxListener(mBeanServer, domain, filter, timeUnits);
  }

  /**
   * Starts the reporter.
   */
  public void start() {
    registry.addListener(listener);
  }

  /**
   * Stops the reporter.
   */
  public void stop() {
    registry.removeListener(listener);
    listener.unregisterAll();
  }

  /**
   * Stops the reporter.
   */
  @Override
  public void close() {
    stop();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy