com.linkedin.pinot.common.metrics.MetricsHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.linkedin.pinot.common.metrics.MetricsHelper.java

Source

/**
 * Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.linkedin.pinot.common.metrics;

import com.yammer.metrics.core.Timer;
import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;

import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Histogram;
import com.yammer.metrics.core.Meter;
import com.yammer.metrics.core.Metered;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.MetricsRegistry;
import com.yammer.metrics.core.Sampling;
import com.yammer.metrics.core.Stoppable;
import org.apache.commons.configuration.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MetricsHelper {
    private static final Logger LOGGER = LoggerFactory.getLogger(MetricsHelper.class);

    private static Map<MetricsRegistry, Object> metricsRegistryMap = new WeakHashMap<MetricsRegistry, Object>();

    private static Map<MetricsRegistryRegistrationListener, Object> metricsRegistryRegistrationListenersMap = new WeakHashMap<MetricsRegistryRegistrationListener, Object>();

    /**
     * Initializes the metrics system by initializing the registry registration listeners present in the configuration.
     *
     * @param configuration The subset of the configuration containing the metrics-related keys
     */
    public static void initializeMetrics(Configuration configuration) {
        synchronized (MetricsHelper.class) {
            String[] listenerClassNames = configuration.getStringArray("metricsRegistryRegistrationListeners");

            if (listenerClassNames.length < 1) {
                listenerClassNames = new String[] {
                        JmxReporterMetricsRegistryRegistrationListener.class.getName() };
            }

            // Build each listener using their default constructor and add them
            for (String listenerClassName : listenerClassNames) {
                try {
                    Class<? extends MetricsRegistryRegistrationListener> clazz = (Class<? extends MetricsRegistryRegistrationListener>) Class
                            .forName(listenerClassName);
                    Constructor<? extends MetricsRegistryRegistrationListener> defaultConstructor = clazz
                            .getDeclaredConstructor();
                    MetricsRegistryRegistrationListener listener = defaultConstructor.newInstance();

                    addMetricsRegistryRegistrationListener(listener);
                } catch (Exception e) {
                    LOGGER.warn("Caught exception while initializing MetricsRegistryRegistrationListener "
                            + listenerClassName, e);
                }
            }
        }
    }

    /**
     * Adds a metrics registry registration listener. When adding a metrics registry registration listener, events are
     * fired to add all previously registered metrics registries to the newly added metrics registry registration
     * listener.
     *
     * @param listener The listener to add
     */
    public static void addMetricsRegistryRegistrationListener(MetricsRegistryRegistrationListener listener) {
        synchronized (MetricsHelper.class) {
            metricsRegistryRegistrationListenersMap.put(listener, null);

            // Fire events to register all previously registered metrics registries
            Set<MetricsRegistry> metricsRegistries = metricsRegistryMap.keySet();
            for (MetricsRegistry metricsRegistry : metricsRegistries) {
                listener.onMetricsRegistryRegistered(metricsRegistry);
            }
        }
    }

    /**
     * Registers the metrics registry with the metrics helper.
     *
     * @param registry The registry to register
     */
    public static void registerMetricsRegistry(MetricsRegistry registry) {
        synchronized (MetricsHelper.class) {
            metricsRegistryMap.put(registry, null);

            // Fire event to all registered listeners
            Set<MetricsRegistryRegistrationListener> metricsRegistryRegistrationListeners = metricsRegistryRegistrationListenersMap
                    .keySet();
            for (MetricsRegistryRegistrationListener metricsRegistryRegistrationListener : metricsRegistryRegistrationListeners) {
                metricsRegistryRegistrationListener.onMetricsRegistryRegistered(registry);
            }
        }
    }

    /**
     *
     * Return an existing meter if
     *  (a) A meter already exist with the same metric name.
     * Otherwise, creates a new meter and registers
     *
     * @param registry MetricsRegistry
     * @param name metric name
     * @param eventType Event Type
     * @param unit TimeUnit for rate determination
     * @return Meter
     */
    public static Meter newMeter(MetricsRegistry registry, MetricName name, String eventType, TimeUnit unit) {
        if (registry != null) {
            return registry.newMeter(name, eventType, unit);
        } else {
            return Metrics.newMeter(name, eventType, unit);
        }
    }

    /**
     *
     * Return an existing aggregated meter if registry is not null and a aggregated meter already exist
     * with the same metric name. Otherwise, creates a new aggregated meter and registers (if registry not null)
     *
     * @param registry MetricsRegistry
     * @param name metric name
     * @return AggregatedMeter
     */
    public static <T extends Metered & Stoppable> AggregatedMeter<T> newAggregatedMeter(
            AggregatedMetricsRegistry registry, MetricName name) {
        if (registry != null) {
            return registry.newAggregatedMeter(name);
        } else {
            return new AggregatedMeter<T>(); //not registered
        }
    }

    /**
     *
     * Return an existing counter if
     *  (a) A counter already exist with the same metric name.
     * Otherwise, creates a new meter and registers
     *
     * @param registry MetricsRegistry
     * @param name metric name
     * @return Counter
     */
    public static Counter newCounter(MetricsRegistry registry, MetricName name) {
        if (registry != null) {
            return registry.newCounter(name);
        } else {
            return Metrics.newCounter(name);
        }
    }

    /**
     *
     * Return an existing aggregated counter if registry is not null and a aggregated counter already exist
     * with the same metric name. Otherwise, creates a new aggregated counter and registers (if registry not null)
     *
     * @param registry MetricsRegistry
     * @param name metric name
     * @return AggregatedCounter
     */
    public static AggregatedCounter newAggregatedCounter(AggregatedMetricsRegistry registry, MetricName name) {
        if (registry != null) {
            return registry.newAggregatedCounter(name);
        } else {
            return new AggregatedCounter();
        }
    }

    /**
     *
     * Return an existing histogram if
     *  (a) A histogram already exist with the same metric name.
     * Otherwise, creates a new meter and registers
     *
     * @param registry MetricsRegistry
     * @param name metric name
     * @param biased (true if uniform distribution, otherwise exponential weighted)
     * @return histogram
     */
    public static Histogram newHistogram(MetricsRegistry registry, MetricName name, boolean biased) {
        if (registry != null) {
            return registry.newHistogram(name, biased);
        } else {
            return Metrics.newHistogram(name, biased);
        }
    }

    /**
     *
     * Return an existing aggregated histogram if registry is not null and a aggregated histogram already exist
     * with the same metric name. Otherwise, creates a new aggregated histogram and registers (if registry not null)
     *
     * @param registry MetricsRegistry
     * @param name metric name
     * @return AggregatedHistogram
     */
    public static <T extends Sampling> AggregatedHistogram<T> newAggregatedHistogram(
            AggregatedMetricsRegistry registry, MetricName name) {
        if (registry != null) {
            return registry.newAggregatedHistogram(name);
        } else {
            return new AggregatedHistogram<T>();
        }
    }

    /**
     *
     * Return an existing gauge if
     *  (a) A gauge already exist with the same metric name.
     * Otherwise, creates a new meter and registers
     *
     * @param registry MetricsRegistry
     * @param name metric name
     * @param gauge Underlying gauge to be tracked
     * @return gauge
     */
    public static <T> Gauge<T> newGauge(MetricsRegistry registry, MetricName name, Gauge<T> gauge) {
        if (registry != null) {
            return registry.newGauge(name, gauge);
        } else {
            return Metrics.newGauge(name, gauge);
        }
    }

    /**
     *
     * Return an existing aggregated long gauge if registry is not null and a aggregated long gauge already exist
     * with the same metric name. Otherwise, creates a new aggregated long gauge and registers (if registry not null)
     *
     * @param registry MetricsRegistry
     * @param name metric name
     * @return AggregatedLongGauge
     */
    public static <T extends Number, V extends Gauge<T>> AggregatedLongGauge<T, V> newAggregatedLongGauge(
            AggregatedMetricsRegistry registry, MetricName name) {
        if (registry != null) {
            return registry.newAggregatedLongGauge(name);
        } else {
            return new AggregatedLongGauge<T, V>();
        }
    }

    /**
     *
     * Return an existing timer if
     *  (a) A timer already exist with the same metric name.
     * Otherwise, creates a new timer and registers
     *
     * @param registry MetricsRegistry
     * @param name metric name
     * @param durationUnit TimeUnit for duration
     * @param rateUnit TimeUnit for rate determination
     * @return Timer
     */
    public static Timer newTimer(MetricsRegistry registry, MetricName name, TimeUnit durationUnit,
            TimeUnit rateUnit) {
        if (registry != null) {
            return registry.newTimer(name, durationUnit, rateUnit);
        } else {
            return Metrics.newTimer(name, durationUnit, rateUnit);
        }
    }

    /**
     * Useful for measuring elapsed times.
     *
     * Usage :
     * <pre>
     * {@code
     *   TimerContext tc = MtericsHelper.startTimer();
     *   ....
     *   Your code to be measured
     *   ....
     *   tc.stop();
     *   long elapsedTimeMs = tc.getLatencyMs();
     *
     * }
     * </pre>
     * @return
     */
    public static TimerContext startTimer() {
        return new TimerContext();
    }

    /**
     *
     * TimerContext to measure elapsed time
     *
     */
    public static class TimerContext {
        private final long _startTimeNanos;
        private long _stopTimeNanos;
        private boolean _isDone;

        public TimerContext() {
            _startTimeNanos = System.nanoTime();
            _isDone = false;
        }

        public void stop() {
            _isDone = true;
            _stopTimeNanos = System.nanoTime();
        }

        /**
         *
         * @return
         */
        public long getLatencyMs() {
            if (!_isDone) {
                stop();
            }
            return (_stopTimeNanos - _startTimeNanos) / 1000000L;
        }
    }
}