| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.hadoop.metrics2.impl; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.PrintStream; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import static java.security.AccessController.*; |
| import java.security.PrivilegedAction; |
| import java.util.Iterator; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.base.Splitter; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Maps; |
| |
| import org.apache.commons.configuration.Configuration; |
| import org.apache.commons.configuration.ConfigurationException; |
| import org.apache.commons.configuration.PropertiesConfiguration; |
| import org.apache.commons.configuration.SubsetConfiguration; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.metrics2.MetricsFilter; |
| import org.apache.hadoop.metrics2.MetricsPlugin; |
| import org.apache.hadoop.metrics2.filter.GlobFilter; |
| |
| /** |
| * Metrics configuration for MetricsSystemImpl |
| */ |
| class MetricsConfig extends SubsetConfiguration { |
| static final Log LOG = LogFactory.getLog(MetricsConfig.class); |
| |
| static final String DEFAULT_FILE_NAME = "hadoop-metrics2.properties"; |
| static final String PREFIX_DEFAULT = "*."; |
| |
| static final String PERIOD_KEY = "period"; |
| static final int PERIOD_DEFAULT = 10; // seconds |
| |
| static final String QUEUE_CAPACITY_KEY = "queue.capacity"; |
| static final int QUEUE_CAPACITY_DEFAULT = 1; |
| |
| static final String RETRY_DELAY_KEY = "retry.delay"; |
| static final int RETRY_DELAY_DEFAULT = 10; // seconds |
| static final String RETRY_BACKOFF_KEY = "retry.backoff"; |
| static final int RETRY_BACKOFF_DEFAULT = 2; // back off factor |
| static final String RETRY_COUNT_KEY = "retry.count"; |
| static final int RETRY_COUNT_DEFAULT = 1; |
| |
| static final String JMX_CACHE_TTL_KEY = "jmx.cache.ttl"; |
| static final String START_MBEANS_KEY = "source.start_mbeans"; |
| static final String PLUGIN_URLS_KEY = "plugin.urls"; |
| |
| static final String CONTEXT_KEY = "context"; |
| static final String NAME_KEY = "name"; |
| static final String DESC_KEY = "description"; |
| static final String SOURCE_KEY = "source"; |
| static final String SINK_KEY = "sink"; |
| static final String METRIC_FILTER_KEY = "metric.filter"; |
| static final String RECORD_FILTER_KEY = "record.filter"; |
| static final String SOURCE_FILTER_KEY = "source.filter"; |
| |
| static final Pattern INSTANCE_REGEX = Pattern.compile("([^.*]+)\\..+"); |
| static final Splitter SPLITTER = Splitter.on(',').trimResults(); |
| private ClassLoader pluginLoader; |
| |
| MetricsConfig(Configuration c, String prefix) { |
| super(c, prefix.toLowerCase(Locale.US), "."); |
| } |
| |
| static MetricsConfig create(String prefix) { |
| return loadFirst(prefix, "hadoop-metrics2-"+ prefix.toLowerCase(Locale.US) |
| +".properties", DEFAULT_FILE_NAME); |
| } |
| |
| static MetricsConfig create(String prefix, String... fileNames) { |
| return loadFirst(prefix, fileNames); |
| } |
| |
| /** |
| * Load configuration from a list of files until the first successful load |
| * @param conf the configuration object |
| * @param files the list of filenames to try |
| * @return the configuration object |
| */ |
| static MetricsConfig loadFirst(String prefix, String... fileNames) { |
| for (String fname : fileNames) { |
| try { |
| Configuration cf = new PropertiesConfiguration(fname) |
| .interpolatedConfiguration(); |
| LOG.info("loaded properties from "+ fname); |
| LOG.debug(toString(cf)); |
| MetricsConfig mc = new MetricsConfig(cf, prefix); |
| LOG.debug(mc); |
| return mc; |
| } |
| catch (ConfigurationException e) { |
| if (e.getMessage().startsWith("Cannot locate configuration")) { |
| continue; |
| } |
| throw new MetricsConfigException(e); |
| } |
| } |
| LOG.warn("Cannot locate configuration: tried "+ |
| Joiner.on(",").join(fileNames)); |
| // default to an empty configuration |
| return new MetricsConfig(new PropertiesConfiguration(), prefix); |
| } |
| |
| @Override |
| public MetricsConfig subset(String prefix) { |
| return new MetricsConfig(this, prefix); |
| } |
| |
| /** |
| * Return sub configs for instance specified in the config. |
| * Assuming format specified as follows:<pre> |
| * [type].[instance].[option] = [value]</pre> |
| * Note, '*' is a special default instance, which is excluded in the result. |
| * @param type of the instance |
| * @return a map with [instance] as key and config object as value |
| */ |
| Map<String, MetricsConfig> getInstanceConfigs(String type) { |
| Map<String, MetricsConfig> map = Maps.newHashMap(); |
| MetricsConfig sub = subset(type); |
| |
| for (String key : sub.keys()) { |
| Matcher matcher = INSTANCE_REGEX.matcher(key); |
| if (matcher.matches()) { |
| String instance = matcher.group(1); |
| if (!map.containsKey(instance)) { |
| map.put(instance, sub.subset(instance)); |
| } |
| } |
| } |
| return map; |
| } |
| |
| Iterable<String> keys() { |
| return new Iterable<String>() { |
| @SuppressWarnings("unchecked") |
| @Override |
| public Iterator<String> iterator() { |
| return (Iterator<String>) getKeys(); |
| } |
| }; |
| } |
| |
| /** |
| * Will poke parents for defaults |
| * @param key to lookup |
| * @return the value or null |
| */ |
| @Override |
| public Object getProperty(String key) { |
| Object value = super.getProperty(key); |
| if (value == null) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("poking parent '"+ getParent().getClass().getSimpleName() + |
| "' for key: "+ key); |
| } |
| return getParent().getProperty(key.startsWith(PREFIX_DEFAULT) ? key |
| : PREFIX_DEFAULT + key); |
| } |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("returning '"+ value +"' for key: "+ key); |
| } |
| return value; |
| } |
| |
| <T extends MetricsPlugin> T getPlugin(String name) { |
| String clsName = getClassName(name); |
| if (clsName == null) return null; |
| try { |
| Class<?> cls = Class.forName(clsName, true, getPluginLoader()); |
| @SuppressWarnings("unchecked") |
| T plugin = (T) cls.newInstance(); |
| plugin.init(name.isEmpty() ? this : subset(name)); |
| return plugin; |
| } |
| catch (Exception e) { |
| throw new MetricsConfigException("Error creating plugin: "+ clsName, e); |
| } |
| } |
| |
| String getClassName(String prefix) { |
| String classKey = prefix.isEmpty() ? "class" : prefix +".class"; |
| String clsName = getString(classKey); |
| LOG.debug(clsName); |
| if (clsName == null || clsName.isEmpty()) { |
| return null; |
| } |
| return clsName; |
| } |
| |
| ClassLoader getPluginLoader() { |
| if (pluginLoader != null) return pluginLoader; |
| final ClassLoader defaultLoader = getClass().getClassLoader(); |
| Object purls = super.getProperty(PLUGIN_URLS_KEY); |
| if (purls == null) return defaultLoader; |
| Iterable<String> jars = SPLITTER.split((String) purls); |
| int len = Iterables.size(jars); |
| if ( len > 0) { |
| final URL[] urls = new URL[len]; |
| try { |
| int i = 0; |
| for (String jar : jars) { |
| LOG.debug(jar); |
| urls[i++] = new URL(jar); |
| } |
| } |
| catch (Exception e) { |
| throw new MetricsConfigException(e); |
| } |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("using plugin jars: "+ Iterables.toString(jars)); |
| } |
| pluginLoader = doPrivileged(new PrivilegedAction<ClassLoader>() { |
| @Override public ClassLoader run() { |
| return new URLClassLoader(urls, defaultLoader); |
| } |
| }); |
| return pluginLoader; |
| } |
| if (parent instanceof MetricsConfig) { |
| return ((MetricsConfig) parent).getPluginLoader(); |
| } |
| return defaultLoader; |
| } |
| |
| @Override public void clear() { |
| super.clear(); |
| // pluginLoader.close(); // jdk7 is saner |
| } |
| |
| MetricsFilter getFilter(String prefix) { |
| // don't create filter instances without out options |
| MetricsConfig conf = subset(prefix); |
| if (conf.isEmpty()) return null; |
| MetricsFilter filter = getPlugin(prefix); |
| if (filter != null) return filter; |
| // glob filter is assumed if pattern is specified but class is not. |
| filter = new GlobFilter(); |
| filter.init(conf); |
| return filter; |
| } |
| |
| @Override |
| public String toString() { |
| return toString(this); |
| } |
| |
| static String toString(Configuration c) { |
| ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |
| PrintStream ps = new PrintStream(buffer); |
| PropertiesConfiguration tmp = new PropertiesConfiguration(); |
| tmp.copy(c); |
| try { tmp.save(ps); } |
| catch (Exception e) { |
| throw new MetricsConfigException(e); |
| } |
| return buffer.toString(); |
| } |
| } |