001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hdfs.server.common; 019 020import java.lang.management.ManagementFactory; 021import java.util.Collections; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Set; 025 026import javax.management.Attribute; 027import javax.management.AttributeList; 028import javax.management.MBeanAttributeInfo; 029import javax.management.MBeanInfo; 030import javax.management.MBeanServer; 031import javax.management.MalformedObjectNameException; 032import javax.management.ObjectName; 033 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036import org.apache.commons.logging.impl.Log4JLogger; 037import org.apache.hadoop.metrics2.util.MBeans; 038import org.apache.log4j.Appender; 039import org.apache.log4j.AsyncAppender; 040 041/** 042 * MetricsLoggerTask can be used as utility to dump metrics to log. 043 */ 044public class MetricsLoggerTask implements Runnable { 045 046 public static final Log LOG = LogFactory.getLog(MetricsLoggerTask.class); 047 048 private static ObjectName objectName = null; 049 050 static { 051 try { 052 objectName = new ObjectName("Hadoop:*"); 053 } catch (MalformedObjectNameException m) { 054 // This should not occur in practice since we pass 055 // a valid pattern to the constructor above. 056 } 057 } 058 059 private Log metricsLog; 060 private String nodeName; 061 private short maxLogLineLength; 062 063 public MetricsLoggerTask(Log metricsLog, String nodeName, 064 short maxLogLineLength) { 065 this.metricsLog = metricsLog; 066 this.nodeName = nodeName; 067 this.maxLogLineLength = maxLogLineLength; 068 } 069 070 /** 071 * Write metrics to the metrics appender when invoked. 072 */ 073 @Override 074 public void run() { 075 // Skip querying metrics if there are no known appenders. 076 if (!metricsLog.isInfoEnabled() || !hasAppenders(metricsLog) 077 || objectName == null) { 078 return; 079 } 080 081 metricsLog.info(" >> Begin " + nodeName + " metrics dump"); 082 final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 083 084 // Iterate over each MBean. 085 for (final ObjectName mbeanName : server.queryNames(objectName, null)) { 086 try { 087 MBeanInfo mBeanInfo = server.getMBeanInfo(mbeanName); 088 final String mBeanNameName = MBeans.getMbeanNameName(mbeanName); 089 final Set<String> attributeNames = getFilteredAttributes(mBeanInfo); 090 091 final AttributeList attributes = server.getAttributes(mbeanName, 092 attributeNames.toArray(new String[attributeNames.size()])); 093 094 for (Object o : attributes) { 095 final Attribute attribute = (Attribute) o; 096 final Object value = attribute.getValue(); 097 final String valueStr = (value != null) ? value.toString() : "null"; 098 // Truncate the value if it is too long 099 metricsLog.info(mBeanNameName + ":" + attribute.getName() + "=" 100 + trimLine(valueStr)); 101 } 102 } catch (Exception e) { 103 metricsLog.error("Failed to get " + nodeName + " metrics for mbean " 104 + mbeanName.toString(), e); 105 } 106 } 107 metricsLog.info(" << End " + nodeName + " metrics dump"); 108 } 109 110 private String trimLine(String valueStr) { 111 if (maxLogLineLength <= 0) { 112 return valueStr; 113 } 114 115 return (valueStr.length() < maxLogLineLength ? valueStr : valueStr 116 .substring(0, maxLogLineLength) + "..."); 117 } 118 119 private static boolean hasAppenders(Log logger) { 120 if (!(logger instanceof Log4JLogger)) { 121 // Don't bother trying to determine the presence of appenders. 122 return true; 123 } 124 Log4JLogger log4JLogger = ((Log4JLogger) logger); 125 return log4JLogger.getLogger().getAllAppenders().hasMoreElements(); 126 } 127 128 /** 129 * Get the list of attributes for the MBean, filtering out a few attribute 130 * types. 131 */ 132 private static Set<String> getFilteredAttributes(MBeanInfo mBeanInfo) { 133 Set<String> attributeNames = new HashSet<>(); 134 for (MBeanAttributeInfo attributeInfo : mBeanInfo.getAttributes()) { 135 if (!attributeInfo.getType().equals( 136 "javax.management.openmbean.TabularData") 137 && !attributeInfo.getType().equals( 138 "javax.management.openmbean.CompositeData") 139 && !attributeInfo.getType().equals( 140 "[Ljavax.management.openmbean.CompositeData;")) { 141 attributeNames.add(attributeInfo.getName()); 142 } 143 } 144 return attributeNames; 145 } 146 147 /** 148 * Make the metrics logger async and add all pre-existing appenders to the 149 * async appender. 150 */ 151 public static void makeMetricsLoggerAsync(Log metricsLog) { 152 if (!(metricsLog instanceof Log4JLogger)) { 153 LOG.warn("Metrics logging will not be async since " 154 + "the logger is not log4j"); 155 return; 156 } 157 org.apache.log4j.Logger logger = ((Log4JLogger) metricsLog).getLogger(); 158 logger.setAdditivity(false); // Don't pollute actual logs with metrics dump 159 160 @SuppressWarnings("unchecked") 161 List<Appender> appenders = Collections.list(logger.getAllAppenders()); 162 // failsafe against trying to async it more than once 163 if (!appenders.isEmpty() && !(appenders.get(0) instanceof AsyncAppender)) { 164 AsyncAppender asyncAppender = new AsyncAppender(); 165 // change logger to have an async appender containing all the 166 // previously configured appenders 167 for (Appender appender : appenders) { 168 logger.removeAppender(appender); 169 asyncAppender.addAppender(appender); 170 } 171 logger.addAppender(asyncAppender); 172 } 173 } 174}