package net.onrc.onos.core.metrics; import com.codahale.metrics.Counter; import com.codahale.metrics.Gauge; import com.codahale.metrics.Histogram; import com.codahale.metrics.Meter; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricFilter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * This class acts a singleton to hold the Metrics registry for ONOS. * All metrics (Counter, Histogram, Timer, Meter, Gauge) use a hierarchical * string-based naming scheme: COMPONENT.FEATURE.NAME. * Example: "Topology.Counters.TopologyUpdates". * The COMPONENT and FEATURE names have to be registered in advance before * a metric can be created. Example: * <pre> * <code> * private static final OnosMetrics.MetricsComponent COMPONENT = * OnosMetrics.registerComponent("Topology"); * private static final OnosMetrics.MetricsFeature FEATURE = * COMPONENT.registerFeature("Counters"); * private final Counter counterTopologyUpdates = * OnosMetrics.createCounter(COMPONENT, FEATURE, "TopologyUpdates"); * </code> * </pre> * Gauges are slightly different because they are not created directly in * this class, but are allocated by the caller and passed in for registration: * <pre> * <code> * private final Gauge<Long> gauge = * new {@literal Gauge<Long>}() { * {@literal @}Override * public Long getValue() { * return gaugeValue; * } * }; * OnosMetrics.registerMetric(COMPONENT, FEATURE, GAUGE_NAME, gauge); * </code> * </pre> */ public final class OnosMetrics { /** * Registry to hold the Components defined in the system. */ private static ConcurrentMap<String, MetricsComponent> componentsRegistry = new ConcurrentHashMap<>(); /** * Registry for the Metrics objects created in the system. */ private static final MetricRegistry METRICS_REGISTRY = new MetricRegistry(); /** * Hide constructor. The only way to get the registry is through the * singleton getter. */ private OnosMetrics() {} /** * Components that can hold Metrics. This is used as the first part of * a Metric's name. */ public interface MetricsComponent { /** * Fetches the name of the Component. * * @return name of the Component */ public String getName(); /** * Registers a Feature for this component. * * @param featureName name of the Feature to register * @return Feature object that can be used when creating Metrics */ public MetricsFeature registerFeature(final String featureName); } /** * Features that can hold Metrics. This is used as the second part of * a Metric's name. */ public interface MetricsFeature { /** * Fetches the name of the Feature. * * @return name of the Feature */ public String getName(); } /** * Implementation of a class to represent the Component portion of a * Metric's name. */ private static final class Component implements MetricsComponent { private final String name; /** * Registry to hold the Features defined in this Component. */ private final ConcurrentMap<String, MetricsFeature> featuresRegistry = new ConcurrentHashMap<>(); /** * Constructs a component from a name. * * @param newName name of the component */ Component(final String newName) { name = newName; } @Override public String getName() { return name; } @Override public MetricsFeature registerFeature(final String featureName) { MetricsFeature feature = featuresRegistry.get(featureName); if (feature == null) { final MetricsFeature createdFeature = new Feature(featureName); feature = featuresRegistry.putIfAbsent(featureName, createdFeature); if (feature == null) { feature = createdFeature; } } return feature; } } /** * Implementation of a class to represent the Feature portion of a Metric's * name. */ private static final class Feature implements MetricsFeature { private final String name; /** * Constructs a Feature from a name. * * @param newName name of the Feature */ Feature(final String newName) { name = newName; } @Override public String getName() { return name; } } /** * Registers a component. * * @param name name of the Component to register * @return MetricsComponent object that can be used to create Metrics. */ public static MetricsComponent registerComponent(final String name) { MetricsComponent component = componentsRegistry.get(name); if (component == null) { final MetricsComponent createdComponent = new Component(name); component = componentsRegistry.putIfAbsent(name, createdComponent); if (component == null) { component = createdComponent; } } return component; } /** * Generates a name for a Metric from its component and feature. * * @param component component the metric is defined in * @param feature feature the metric is defined in * @param metricName local name of the metric * * @return full name of the metric */ public static String generateName(final MetricsComponent component, final MetricsFeature feature, final String metricName) { return MetricRegistry.name(component.getName(), feature.getName(), metricName); } /** * Creates a Counter metric. * * @param component component the Counter is defined in * @param feature feature the Counter is defined in * @param metricName local name of the metric * @return the created Counter Meteric */ public static Counter createCounter(final MetricsComponent component, final MetricsFeature feature, final String metricName) { final String name = generateName(component, feature, metricName); return METRICS_REGISTRY.counter(name); } /** * Creates a Histogram metric. * * @param component component the Histogram is defined in * @param feature feature the Histogram is defined in * @param metricName local name of the metric * @return the created Histogram Metric */ public static Histogram createHistogram(final MetricsComponent component, final MetricsFeature feature, final String metricName) { final String name = generateName(component, feature, metricName); return METRICS_REGISTRY.histogram(name); } /** * Creates a Timer metric. * * @param component component the Timer is defined in * @param feature feature the Timeer is defined in * @param metricName local name of the metric * @return the created Timer Metric */ public static Timer createTimer(final MetricsComponent component, final MetricsFeature feature, final String metricName) { final String name = generateName(component, feature, metricName); return METRICS_REGISTRY.timer(name); } /** * Creates a Meter metric. * * @param component component the Meter is defined in * @param feature feature the Meter is defined in * @param metricName local name of the metric * @return the created Meter Metric */ public static Meter createMeter(final MetricsComponent component, final MetricsFeature feature, final String metricName) { final String name = generateName(component, feature, metricName); return METRICS_REGISTRY.meter(name); } /** * Registers an already created Metric. This is used for situation where a * caller needs to allocate its own Metric, but still register it with the * system. * * @param <T> Metric type * @param component component the Metric is defined in * @param feature feature the Metric is defined in * @param metricName local name of the metric * @param metric Metric to register * @return the registered Metric */ public static <T extends Metric> T registerMetric( final MetricsComponent component, final MetricsFeature feature, final String metricName, final T metric) { final String name = generateName(component, feature, metricName); METRICS_REGISTRY.register(name, metric); return metric; } /** * Fetches the existing Timers. * * @param filter filter to use to select Timers * @return a map of the Timers that match the filter, with the key as the * name String to the Timer. */ public static Map<String, Timer> getTimers(final MetricFilter filter) { return METRICS_REGISTRY.getTimers(filter); } /** * Fetches the existing Gauges. * * @param filter filter to use to select Gauges * @return a map of the Gauges that match the filter, with the key as the * name String to the Gauge. */ @SuppressWarnings("rawtypes") public static Map<String, Gauge> getGauges(final MetricFilter filter) { return METRICS_REGISTRY.getGauges(filter); } /** * Fetches the existing Counters. * * @param filter filter to use to select Counters * @return a map of the Counters that match the filter, with the key as the * name String to the Counter. */ public static Map<String, Counter> getCounters(final MetricFilter filter) { return METRICS_REGISTRY.getCounters(filter); } /** * Fetches the existing Meters. * * @param filter filter to use to select Meters * @return a map of the Meters that match the filter, with the key as the * name String to the Meter. */ public static Map<String, Meter> getMeters(final MetricFilter filter) { return METRICS_REGISTRY.getMeters(filter); } /** * Fetches the existing Histograms. * * @param filter filter to use to select Histograms * @return a map of the Histograms that match the filter, with the key as the * name String to the Histogram. */ public static Map<String, Histogram> getHistograms(final MetricFilter filter) { return METRICS_REGISTRY.getHistograms(filter); } /** * Removes all Metrics that match a given filter. * * @param filter filter to use to select the Metrics to remove. */ public static void removeMatching(final MetricFilter filter) { METRICS_REGISTRY.removeMatching(filter); } }