/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.metrics; import alluxio.Configuration; import alluxio.PropertyKey; import alluxio.metrics.sink.Sink; import alluxio.util.network.NetworkAddressUtils; import com.codahale.metrics.Counter; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.codahale.metrics.jvm.GarbageCollectorMetricSet; import com.codahale.metrics.jvm.MemoryUsageGaugeSet; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; /** * A MetricsSystem is created by a specific instance(master, worker). It polls the metrics sources * periodically and pass the data to the sinks. * * The syntax of the metrics configuration file is: * sink.[name].[options]=[value] */ @ThreadSafe public final class MetricsSystem { private static final Logger LOG = LoggerFactory.getLogger(MetricsSystem.class); // Supported special instance names. public static final String MASTER_INSTANCE = "master"; public static final String WORKER_INSTANCE = "worker"; public static final String CLIENT_INSTANCE = "client"; public static final MetricRegistry METRIC_REGISTRY; static { METRIC_REGISTRY = new MetricRegistry(); METRIC_REGISTRY.registerAll(new GarbageCollectorMetricSet()); METRIC_REGISTRY.registerAll(new MemoryUsageGaugeSet()); } @GuardedBy("MetricsSystem") private static List<Sink> sSinks; public static final String SINK_REGEX = "^sink\\.(.+)\\.(.+)"; private static final TimeUnit MINIMAL_POLL_UNIT = TimeUnit.SECONDS; private static final int MINIMAL_POLL_PERIOD = 1; /** * Starts sinks specified in the configuration. This is an no-op if the sinks have already been * started. * Note: This has to be called after Alluxio configuration is initialized. */ public static void startSinks() { synchronized (MetricsSystem.class) { if (sSinks != null) { LOG.info("Sinks have already been started."); return; } } String metricsConfFile = Configuration.get(PropertyKey.METRICS_CONF_FILE); if (metricsConfFile.isEmpty()) { LOG.info("Metrics is not enabled."); return; } MetricsConfig config = new MetricsConfig(metricsConfFile); startSinksFromConfig(config); } /** * Starts sinks from a given metrics configuration. This is made public for unit test. * * @param config the metrics config */ public static synchronized void startSinksFromConfig(MetricsConfig config) { if (sSinks != null) { LOG.info("Sinks have already been started."); return; } LOG.info("Starting sinks with config: {}.", config); sSinks = new ArrayList<>(); Map<String, Properties> sinkConfigs = MetricsConfig.subProperties(config.getProperties(), SINK_REGEX); for (Map.Entry<String, Properties> entry : sinkConfigs.entrySet()) { String classPath = entry.getValue().getProperty("class"); if (classPath != null) { LOG.info("Starting sink {}.", classPath); try { Sink sink = (Sink) Class.forName(classPath).getConstructor(Properties.class, MetricRegistry.class) .newInstance(entry.getValue(), METRIC_REGISTRY); sink.start(); sSinks.add(sink); } catch (Exception e) { LOG.error("Sink class {} cannot be instantiated", classPath, e); } } } } /** * Stops all the sinks. */ public static synchronized void stopSinks() { if (sSinks != null) { for (Sink sink : sSinks) { sink.stop(); } } sSinks = null; } /** * @return the number of sinks started */ public static synchronized int getNumSinks() { int sz = 0; if (sSinks != null) { sz = sSinks.size(); } return sz; } /** * Builds metric registry names for master instance. The pattern is instance.metricName. * * @param name the metric name * @return the metric registry name */ public static String getMasterMetricName(String name) { return Joiner.on(".").join(MASTER_INSTANCE, name); } /** * Builds metric registry name for worker instance. The pattern is instance.uniqueId.metricName. * * @param name the metric name * @return the metric registry name */ public static String getWorkerMetricName(String name) { return getMetricNameWithUniqueId(WORKER_INSTANCE, name); } /** * Builds metric registry name for client instance. The pattern is instance.uniqueId.metricName. * * @param name the metric name * @return the metric registry name */ public static String getClientMetricName(String name) { return getMetricNameWithUniqueId(CLIENT_INSTANCE, name); } /** * Builds unique metric registry names with unique ID (set to host name). The pattern is * instance.hostname.metricName. * * @param instance the instance name * @param name the metric name * @return the metric registry name */ public static String getMetricNameWithUniqueId(String instance, String name) { return Joiner.on(".") .join(instance, NetworkAddressUtils.getLocalHostName().replace('.', '_'), name); } /** * Checks if the poll period is smaller that the minimal poll period which is 1 second. * * @param pollUnit the polling unit * @param pollPeriod the polling period * @throws IllegalArgumentException if the polling period is invalid */ public static void checkMinimalPollingPeriod(TimeUnit pollUnit, int pollPeriod) throws IllegalArgumentException { int period = (int) MINIMAL_POLL_UNIT.convert(pollPeriod, pollUnit); Preconditions.checkArgument(period >= MINIMAL_POLL_PERIOD, "Polling period %d %d is below the minimal polling period", pollPeriod, pollUnit); } /** * Util function to remove get the metrics name without instance and host. * @param metricsName the long metrics name with instance and host name * @return the metrics name without instance and host name */ public static String stripInstanceAndHost(String metricsName) { String[] pieces = metricsName.split("\\."); Preconditions.checkArgument(pieces.length > 1, "Incorrect metrics name: %s.", metricsName); // Master metrics doesn't have hostname included. if (!pieces[0].equals(MASTER_INSTANCE)) { pieces[1] = null; } pieces[0] = null; return Joiner.on(".").skipNulls().join(pieces); } // Some helper functions. /** * @param name the metric name * @return the timer */ public static Timer masterTimer(String name) { return METRIC_REGISTRY.timer(getMasterMetricName(name)); } /** * @param name the metric name * @return the counter */ public static Counter masterCounter(String name) { return METRIC_REGISTRY.counter((getMasterMetricName(name))); } /** * @param name the metric name * @return the timer */ public static Timer workerTimer(String name) { return METRIC_REGISTRY.timer(getWorkerMetricName(name)); } /** * @param name the metric name * @return the counter */ public static Counter workerCounter(String name) { return METRIC_REGISTRY.counter((getWorkerMetricName(name))); } /** * @param name the metric name * @return the timer */ public static Timer clientTimer(String name) { return METRIC_REGISTRY.timer(getClientMetricName(name)); } /** * @param name the metric name * @return the counter */ public static Counter clientCounter(String name) { return METRIC_REGISTRY.counter(getClientMetricName(name)); } /** * Registers a gauge if it has not been registered. * * @param name the gauge name * @param metric the gauge * @param <T> the type */ public static synchronized <T> void registerGaugeIfAbsent(String name, Gauge<T> metric) { if (!METRIC_REGISTRY.getGauges().containsKey(name)) { METRIC_REGISTRY.register(name, metric); } } /** * Resets all the counters to 0 for testing. */ public static void resetAllCounters() { for (Map.Entry<String, Counter> entry : METRIC_REGISTRY.getCounters().entrySet()) { entry.getValue().dec(entry.getValue().getCount()); } } /** * Disallows any explicit initialization. */ private MetricsSystem() { } }