/* * 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.lib; import java.lang.reflect.Method; import static com.google.common.base.Preconditions.*; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.metrics2.MetricsException; import org.apache.hadoop.metrics2.MetricsRecordBuilder; import org.apache.hadoop.metrics2.MetricsInfo; import org.apache.hadoop.metrics2.annotation.Metric; import static org.apache.hadoop.metrics2.util.Contracts.*; /** * Metric generated from a method, mostly used by annotation */ class MethodMetric extends MutableMetric { private static final Log LOG = LogFactory.getLog(MethodMetric.class); private final Object obj; private final Method method; private final MetricsInfo info; private final MutableMetric impl; MethodMetric(Object obj, Method method, MetricsInfo info, Metric.Type type) { this.obj = checkNotNull(obj, "object"); this.method = checkArg(method, method.getParameterTypes().length == 0, "Metric method should have no arguments"); this.method.setAccessible(true); this.info = checkNotNull(info, "info"); impl = newImpl(checkNotNull(type, "metric type")); } private MutableMetric newImpl(Metric.Type metricType) { Class<?> resType = method.getReturnType(); switch (metricType) { case COUNTER: return newCounter(resType); case GAUGE: return newGauge(resType); case DEFAULT: return resType == String.class ? newTag(resType) : newGauge(resType); case TAG: return newTag(resType); default: checkArg(metricType, false, "unsupported metric type"); return null; } } MutableMetric newCounter(final Class<?> type) { if (isInt(type) || isLong(type)) { return new MutableMetric() { @Override public void snapshot(MetricsRecordBuilder rb, boolean all) { try { Object ret = method.invoke(obj, (Object[])null); if (isInt(type)) rb.addCounter(info, ((Integer) ret).intValue()); else rb.addCounter(info, ((Long) ret).longValue()); } catch (Exception ex) { LOG.error("Error invoking method "+ method.getName(), ex); } } }; } throw new MetricsException("Unsupported counter type: "+ type.getName()); } static boolean isInt(Class<?> type) { boolean ret = type == Integer.TYPE || type == Integer.class; return ret; } static boolean isLong(Class<?> type) { return type == Long.TYPE || type == Long.class; } static boolean isFloat(Class<?> type) { return type == Float.TYPE || type == Float.class; } static boolean isDouble(Class<?> type) { return type == Double.TYPE || type == Double.class; } MutableMetric newGauge(final Class<?> t) { if (isInt(t) || isLong(t) || isFloat(t) || isDouble(t)) { return new MutableMetric() { @Override public void snapshot(MetricsRecordBuilder rb, boolean all) { try { Object ret = method.invoke(obj, (Object[]) null); if (isInt(t)) rb.addGauge(info, ((Integer) ret).intValue()); else if (isLong(t)) rb.addGauge(info, ((Long) ret).longValue()); else if (isFloat(t)) rb.addGauge(info, ((Float) ret).floatValue()); else rb.addGauge(info, ((Double) ret).doubleValue()); } catch (Exception ex) { LOG.error("Error invoking method "+ method.getName(), ex); } } }; } throw new MetricsException("Unsupported gauge type: "+ t.getName()); } MutableMetric newTag(Class<?> resType) { if (resType == String.class) { return new MutableMetric() { @Override public void snapshot(MetricsRecordBuilder rb, boolean all) { try { Object ret = method.invoke(obj, (Object[]) null); rb.tag(info, (String) ret); } catch (Exception ex) { LOG.error("Error invoking method "+ method.getName(), ex); } } }; } throw new MetricsException("Unsupported tag type: "+ resType.getName()); } @Override public void snapshot(MetricsRecordBuilder builder, boolean all) { impl.snapshot(builder, all); } static MetricsInfo metricInfo(Method method) { return Interns.info(nameFrom(method), "Metric for "+ method.getName()); } static String nameFrom(Method method) { String methodName = method.getName(); if (methodName.startsWith("get")) { return StringUtils.capitalize(methodName.substring(3)); } return StringUtils.capitalize(methodName); } }