| /** |
| * 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.hdfs.server.common; |
| |
| import java.lang.management.ManagementFactory; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import javax.management.Attribute; |
| import javax.management.AttributeList; |
| import javax.management.MBeanAttributeInfo; |
| import javax.management.MBeanInfo; |
| import javax.management.MBeanServer; |
| import javax.management.MalformedObjectNameException; |
| import javax.management.ObjectName; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.impl.Log4JLogger; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.apache.hadoop.metrics2.util.MBeans; |
| import org.apache.log4j.Appender; |
| import org.apache.log4j.AsyncAppender; |
| |
| /** |
| * MetricsLoggerTask can be used as utility to dump metrics to log. |
| */ |
| public class MetricsLoggerTask implements Runnable { |
| |
| public static final Logger LOG = |
| LoggerFactory.getLogger(MetricsLoggerTask.class); |
| |
| private static ObjectName objectName = null; |
| |
| static { |
| try { |
| objectName = new ObjectName("Hadoop:*"); |
| } catch (MalformedObjectNameException m) { |
| // This should not occur in practice since we pass |
| // a valid pattern to the constructor above. |
| } |
| } |
| |
| private Log metricsLog; |
| private String nodeName; |
| private short maxLogLineLength; |
| |
| public MetricsLoggerTask(Log metricsLog, String nodeName, |
| short maxLogLineLength) { |
| this.metricsLog = metricsLog; |
| this.nodeName = nodeName; |
| this.maxLogLineLength = maxLogLineLength; |
| } |
| |
| /** |
| * Write metrics to the metrics appender when invoked. |
| */ |
| @Override |
| public void run() { |
| // Skip querying metrics if there are no known appenders. |
| if (!metricsLog.isInfoEnabled() || !hasAppenders(metricsLog) |
| || objectName == null) { |
| return; |
| } |
| |
| metricsLog.info(" >> Begin " + nodeName + " metrics dump"); |
| final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); |
| |
| // Iterate over each MBean. |
| for (final ObjectName mbeanName : server.queryNames(objectName, null)) { |
| try { |
| MBeanInfo mBeanInfo = server.getMBeanInfo(mbeanName); |
| final String mBeanNameName = MBeans.getMbeanNameName(mbeanName); |
| final Set<String> attributeNames = getFilteredAttributes(mBeanInfo); |
| |
| final AttributeList attributes = server.getAttributes(mbeanName, |
| attributeNames.toArray(new String[attributeNames.size()])); |
| |
| for (Object o : attributes) { |
| final Attribute attribute = (Attribute) o; |
| final Object value = attribute.getValue(); |
| final String valueStr = (value != null) ? value.toString() : "null"; |
| // Truncate the value if it is too long |
| metricsLog.info(mBeanNameName + ":" + attribute.getName() + "=" |
| + trimLine(valueStr)); |
| } |
| } catch (Exception e) { |
| metricsLog.error("Failed to get " + nodeName + " metrics for mbean " |
| + mbeanName.toString(), e); |
| } |
| } |
| metricsLog.info(" << End " + nodeName + " metrics dump"); |
| } |
| |
| private String trimLine(String valueStr) { |
| if (maxLogLineLength <= 0) { |
| return valueStr; |
| } |
| |
| return (valueStr.length() < maxLogLineLength ? valueStr : valueStr |
| .substring(0, maxLogLineLength) + "..."); |
| } |
| |
| private static boolean hasAppenders(Log logger) { |
| if (!(logger instanceof Log4JLogger)) { |
| // Don't bother trying to determine the presence of appenders. |
| return true; |
| } |
| Log4JLogger log4JLogger = ((Log4JLogger) logger); |
| return log4JLogger.getLogger().getAllAppenders().hasMoreElements(); |
| } |
| |
| /** |
| * Get the list of attributes for the MBean, filtering out a few attribute |
| * types. |
| */ |
| private static Set<String> getFilteredAttributes(MBeanInfo mBeanInfo) { |
| Set<String> attributeNames = new HashSet<>(); |
| for (MBeanAttributeInfo attributeInfo : mBeanInfo.getAttributes()) { |
| if (!attributeInfo.getType().equals( |
| "javax.management.openmbean.TabularData") |
| && !attributeInfo.getType().equals( |
| "javax.management.openmbean.CompositeData") |
| && !attributeInfo.getType().equals( |
| "[Ljavax.management.openmbean.CompositeData;")) { |
| attributeNames.add(attributeInfo.getName()); |
| } |
| } |
| return attributeNames; |
| } |
| |
| /** |
| * Make the metrics logger async and add all pre-existing appenders to the |
| * async appender. |
| */ |
| public static void makeMetricsLoggerAsync(Log metricsLog) { |
| if (!(metricsLog instanceof Log4JLogger)) { |
| LOG.warn("Metrics logging will not be async since " |
| + "the logger is not log4j"); |
| return; |
| } |
| org.apache.log4j.Logger logger = ((Log4JLogger) metricsLog).getLogger(); |
| logger.setAdditivity(false); // Don't pollute actual logs with metrics dump |
| |
| @SuppressWarnings("unchecked") |
| List<Appender> appenders = Collections.list(logger.getAllAppenders()); |
| // failsafe against trying to async it more than once |
| if (!appenders.isEmpty() && !(appenders.get(0) instanceof AsyncAppender)) { |
| AsyncAppender asyncAppender = new AsyncAppender(); |
| // change logger to have an async appender containing all the |
| // previously configured appenders |
| for (Appender appender : appenders) { |
| logger.removeAppender(appender); |
| asyncAppender.addAppender(appender); |
| } |
| logger.addAppender(asyncAppender); |
| } |
| } |
| } |