From 9030283475a0457757413ad4e18ea7900c411f4d Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Wed, 10 Jul 2024 11:48:25 +0200 Subject: [PATCH] Support jsr77 j2ee statistics (#531) * Support jsr77 j2ee statistics --- pom.xml | 7 + .../datadog/jmxfetch/JmxComplexAttribute.java | 12 + .../util/JeeStatisticsAttributes.java | 345 ++++++++++++++++++ .../datadog/jmxfetch/SimpleTestJavaApp.java | 80 ++++ .../jmxfetch/SimpleTestJavaAppMBean.java | 17 + .../java/org/datadog/jmxfetch/TestApp.java | 42 +++ .../datadog/jmxfetch/jee/BaseStatistic.java | 36 ++ .../jmxfetch/jee/BoundaryStatisticImpl.java | 24 ++ .../jee/BoundedRangeStatisticImpl.java | 25 ++ .../jmxfetch/jee/CountStatisticImpl.java | 17 + .../org/datadog/jmxfetch/jee/JeeStats.java | 28 ++ .../jmxfetch/jee/RangeStatisticImpl.java | 31 ++ .../jmxfetch/jee/TimeStatisticImpl.java | 38 ++ .../jee/UnsupportedStatisticImpl.java | 12 + src/test/resources/jmx_jee_data.yaml | 18 + 15 files changed, 732 insertions(+) create mode 100644 src/main/java/org/datadog/jmxfetch/util/JeeStatisticsAttributes.java create mode 100644 src/test/java/org/datadog/jmxfetch/jee/BaseStatistic.java create mode 100644 src/test/java/org/datadog/jmxfetch/jee/BoundaryStatisticImpl.java create mode 100644 src/test/java/org/datadog/jmxfetch/jee/BoundedRangeStatisticImpl.java create mode 100644 src/test/java/org/datadog/jmxfetch/jee/CountStatisticImpl.java create mode 100644 src/test/java/org/datadog/jmxfetch/jee/JeeStats.java create mode 100644 src/test/java/org/datadog/jmxfetch/jee/RangeStatisticImpl.java create mode 100644 src/test/java/org/datadog/jmxfetch/jee/TimeStatisticImpl.java create mode 100644 src/test/java/org/datadog/jmxfetch/jee/UnsupportedStatisticImpl.java create mode 100644 src/test/resources/jmx_jee_data.yaml diff --git a/pom.xml b/pom.xml index 3b6c8c295..9096d68af 100644 --- a/pom.xml +++ b/pom.xml @@ -185,6 +185,13 @@ snakeyaml ${snakeyaml.version} + + javax.management.j2ee + javax.management.j2ee-api + 1.1.2 + test + + junit junit diff --git a/src/main/java/org/datadog/jmxfetch/JmxComplexAttribute.java b/src/main/java/org/datadog/jmxfetch/JmxComplexAttribute.java index 6849116fa..e57cb84f4 100644 --- a/src/main/java/org/datadog/jmxfetch/JmxComplexAttribute.java +++ b/src/main/java/org/datadog/jmxfetch/JmxComplexAttribute.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.datadog.jmxfetch.service.ServiceNameProvider; +import org.datadog.jmxfetch.util.JeeStatisticsAttributes; import java.io.IOException; import java.util.ArrayList; @@ -26,9 +27,12 @@ public class JmxComplexAttribute extends JmxSubAttribute { Arrays.asList( "javax.management.openmbean.CompositeData", "javax.management.openmbean.CompositeDataSupport", + "javax.management.j2ee.statistics.Statistic", + "javax.management.j2ee.statistics.Stats", "java.util.HashMap", "java.util.Map"); + private List subAttributeList = new ArrayList(); /** JmxComplexAttribute constructor. */ @@ -68,6 +72,10 @@ private void populateSubAttributeList(Object attributeValue) { for (String key : data.keySet()) { this.subAttributeList.add(key); } + } else if (JeeStatisticsAttributes.isJeeStatistic(attributeValue)) { + this.subAttributeList.addAll(JeeStatisticsAttributes.attributesFor(attributeValue)); + } else if (JeeStatisticsAttributes.isJeeStat(attributeValue)) { + this.subAttributeList.addAll(JeeStatisticsAttributes.getStatisticNames(attributeValue)); } } @@ -96,6 +104,10 @@ private Object getValue(String subAttribute) } else if (value instanceof java.util.Map) { Map data = (Map) value; return data.get(subAttribute); + } else if (JeeStatisticsAttributes.isJeeStatistic(value)) { + return JeeStatisticsAttributes.dataFor(value, subAttribute); + } else if (JeeStatisticsAttributes.isJeeStat(value)) { + return JeeStatisticsAttributes.getStatisticDataFor(value, subAttribute); } throw new NumberFormatException(); } diff --git a/src/main/java/org/datadog/jmxfetch/util/JeeStatisticsAttributes.java b/src/main/java/org/datadog/jmxfetch/util/JeeStatisticsAttributes.java new file mode 100644 index 000000000..e3338bbc4 --- /dev/null +++ b/src/main/java/org/datadog/jmxfetch/util/JeeStatisticsAttributes.java @@ -0,0 +1,345 @@ +package org.datadog.jmxfetch.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.ref.SoftReference; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.WeakHashMap; +import javax.management.AttributeNotFoundException; +import javax.management.ReflectionException; + +public class JeeStatisticsAttributes { + /** Attributes for @see javax.management.j2ee.statistics.CountStatistic */ + private static final List COUNT_ATTRIBUTES = Collections.singletonList("count"); + + /** Attributes for @see javax.management.j2ee.statistics.BoundaryStatistic */ + private static final List BOUNDARY_ATTRIBUTES = + Collections.unmodifiableList(Arrays.asList("upperBound", "lowerBound")); + + /** Attributes for @see javax.management.j2ee.statistics.TimeStatistic */ + private static final List TIME_ATTRIBUTES = + Collections.unmodifiableList(Arrays.asList("count", "minTime", "maxTime", "totalTime")); + + /** Attributes for @see javax.management.j2ee.statistics.RangeStatistic */ + private static final List RANGE_ATTRIBUTES = + Collections.unmodifiableList(Arrays.asList("highWaterMark", "lowWaterMark", "current")); + + /** Attributes for @see javax.management.j2ee.statistics.BoundedRangeStatistic */ + private static final List BOUNDED_RANGE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList("upperBound", "lowerBound", "highWaterMark", "lowWaterMark", "current")); + + private static final Logger LOGGER = LoggerFactory.getLogger(JeeStatisticsAttributes.class); + private static final WeakHashMap> + REFLECTION_CACHE = new WeakHashMap<>(); + + private static class ReflectionHolder { + + public final Class classStat; + public final MethodHandle mhStatGetStatisticNames; + public final MethodHandle mhStatGetStatistic; + public final Class classStatistic; + private final Class classCountStatistic; + private final Class classTimeStatistic; + private final Class classRangeStatistic; + private final Class classBoundaryStatistic; + private final Class classBoundedRangeStatistic; + + private final Map, Map> methodCache; + + ReflectionHolder(final ClassLoader classLoader) { + classStat = maybeLookupClass("javax.management.j2ee.statistics.Stats", classLoader); + if (classStat != null) { + mhStatGetStatisticNames = maybeFindMethodHandleFor(classStat, "getStatisticNames"); + mhStatGetStatistic = maybeFindMethodHandleFor(classStat, "getStatistic", + String.class); + classStatistic = maybeLookupClass("javax.management.j2ee.statistics.Statistic", + classLoader); + classCountStatistic = + maybeLookupClass("javax.management.j2ee.statistics.CountStatistic", + classLoader); + classTimeStatistic = + maybeLookupClass("javax.management.j2ee.statistics.TimeStatistic", + classLoader); + classRangeStatistic = + maybeLookupClass("javax.management.j2ee.statistics.RangeStatistic", + classLoader); + classBoundaryStatistic = + maybeLookupClass("javax.management.j2ee.statistics.BoundaryStatistic", + classLoader); + classBoundedRangeStatistic = + maybeLookupClass("javax.management.j2ee.statistics.BoundedRangeStatistic", + classLoader); + methodCache = buildMethodCache(); + } else { + mhStatGetStatisticNames = null; + mhStatGetStatistic = null; + classStatistic = null; + classCountStatistic = null; + classTimeStatistic = null; + classRangeStatistic = null; + classBoundaryStatistic = null; + classBoundedRangeStatistic = null; + methodCache = new WeakHashMap<>(); + } + } + + private Map, Map> buildMethodCache() { + Map, Map> map = new HashMap<>(); + if (classCountStatistic != null) { + map.put(classCountStatistic, + buildMethodCacheFor(classCountStatistic, COUNT_ATTRIBUTES)); + } + if (classTimeStatistic != null) { + map.put(classTimeStatistic, + buildMethodCacheFor(classTimeStatistic, TIME_ATTRIBUTES)); + } + if (classBoundaryStatistic != null) { + map.put( + classBoundaryStatistic, + buildMethodCacheFor(classBoundaryStatistic, BOUNDARY_ATTRIBUTES)); + } + if (classRangeStatistic != null) { + map.put(classRangeStatistic, + buildMethodCacheFor(classRangeStatistic, RANGE_ATTRIBUTES)); + } + if (classBoundedRangeStatistic != null) { + map.put( + classBoundedRangeStatistic, + buildMethodCacheFor(classBoundedRangeStatistic, BOUNDED_RANGE_ATTRIBUTES)); + } + return map; + } + + private static MethodHandle maybeFindMethodHandleFor( + final Class cls, final String name, final Class... parameterTypes) { + if (cls == null) { + return null; + } + return AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public MethodHandle run() { + try { + return MethodHandles.lookup() + .unreflect(cls.getMethod(name, parameterTypes)); + } catch (Throwable t) { + LOGGER.debug("Unable to find method {} for class {}: {}", name, cls, + t.getMessage()); + } + return null; + } + }); + } + + private static Map buildMethodCacheFor( + final Class cls, final List attributes) { + final Map map = new HashMap<>(); + for (String attribute : attributes) { + MethodHandle methodHandle = + maybeFindMethodHandleFor(cls, getterMethodName(attribute)); + if (methodHandle != null) { + map.put(attribute, methodHandle); + } + } + return map; + } + + private static Class maybeLookupClass(final String name, final ClassLoader classLoader) { + try { + return Class.forName(name, false, classLoader); + } catch (Throwable t) { + LOGGER.debug( + "Class {} is unavailable for classloader {}. JEE statistics won't be extracted", + name, + classLoader); + } + return null; + } + + private static String getterMethodName(String attribute) { + // inspired from JavaBean PropertyDescriptor + return "get" + attribute.substring(0, 1).toUpperCase(Locale.ROOT) + + attribute.substring(1); + } + } + + private static ReflectionHolder getOrCreateReflectionHolder(final ClassLoader classLoader) { + // no need to lock here. At worst, we'll do it more time if there is contention. + SoftReference ref = REFLECTION_CACHE.get(classLoader); + if (ref != null && ref.get() != null) { + return ref.get(); + } + final ReflectionHolder holder = new ReflectionHolder(classLoader); + REFLECTION_CACHE.put(classLoader, new SoftReference<>(holder)); + return holder; + } + + /** + * Get the list of attributes for a @see javax.management.j2ee.statistics.Statistic object. + * + * @param instance a @see javax.management.j2ee.statistics.Statistic instance + * @return the list of attributes + */ + public static List attributesFor(Object instance) { + ReflectionHolder rh = getOrCreateReflectionHolder(instance.getClass().getClassLoader()); + if (rh.classCountStatistic != null && rh.classCountStatistic.isInstance(instance)) { + return COUNT_ATTRIBUTES; + } + if (rh.classTimeStatistic != null && rh.classTimeStatistic.isInstance(instance)) { + return TIME_ATTRIBUTES; + } + if (rh.classBoundedRangeStatistic != null + && rh.classBoundedRangeStatistic.isInstance(instance)) { + return BOUNDED_RANGE_ATTRIBUTES; + } + if (rh.classRangeStatistic != null && rh.classRangeStatistic.isInstance(instance)) { + return RANGE_ATTRIBUTES; + } + if (rh.classBoundaryStatistic != null && rh.classBoundaryStatistic.isInstance(instance)) { + return BOUNDARY_ATTRIBUTES; + } + return Collections.emptyList(); + } + + /** + * Fetch the data for a @see javax.management.j2ee.statistics.Statistic instance given an + * attribute name. + * + * @param instance the @see javax.management.j2ee.statistics.Statistic object + * @param attribute the attribute name + * @return the data if any + * @throws ReflectionException in case of reflection issues. + * @throws AttributeNotFoundException in case the attribute is not found. + */ + public static long dataFor(Object instance, String attribute) + throws ReflectionException, AttributeNotFoundException { + Class cls = null; + ReflectionHolder rh = getOrCreateReflectionHolder(instance.getClass().getClassLoader()); + if (rh.classCountStatistic != null && rh.classCountStatistic.isInstance(instance)) { + cls = rh.classCountStatistic; + } else if (rh.classTimeStatistic != null && rh.classTimeStatistic.isInstance(instance)) { + cls = rh.classTimeStatistic; + } else if (rh.classBoundedRangeStatistic != null + && rh.classBoundedRangeStatistic.isInstance(instance)) { + cls = rh.classBoundedRangeStatistic; + } else if (rh.classRangeStatistic != null && rh.classRangeStatistic.isInstance(instance)) { + cls = rh.classRangeStatistic; + } else if (rh.classBoundaryStatistic != null + && rh.classBoundaryStatistic.isInstance(instance)) { + cls = rh.classBoundaryStatistic; + } + if (cls == null) { + throw new AttributeNotFoundException( + "Not supported JSR-77 class: " + instance.getClass().getName()); + } + MethodHandle methodHandle = rh.methodCache.get(cls).get(attribute); + if (methodHandle == null) { + throw new AttributeNotFoundException( + "Unable to find getter for attribute " + attribute + " on class " + cls.getName()); + } + try { + return (long) methodHandle.invoke(instance); + } catch (Throwable t) { + throw new ReflectionException( + new Exception(t), + "Unable to invoke getter for attribute" + attribute + " on class " + cls.getName()); + } + } + + /** + * Get names for a Stat complex object. + * + * @param instance a Stat instance + * @return the names of inner statistics it holds. + */ + public static List getStatisticNames(Object instance) { + ReflectionHolder rh = getOrCreateReflectionHolder(instance.getClass().getClassLoader()); + if (rh.mhStatGetStatisticNames == null || rh.mhStatGetStatistic == null) { + return Collections.emptyList(); + } + try { + String[] names = (String[]) rh.mhStatGetStatisticNames.invoke(instance); + if (names != null) { + List ret = new ArrayList<>(); + for (String name : names) { + Object stat = rh.mhStatGetStatistic.invoke(instance, name); + for (String attr : attributesFor(stat)) { + ret.add(name + "." + attr); + } + } + return ret; + } + } catch (Throwable t) { + LOGGER.warn( + "Unable to get statistic names from jee stat class {}: {}", + instance.getClass(), + t.getMessage()); + } + return Collections.emptyList(); + } + + /** + * Get the statistic data for a complex Stat object. + * + * @param instance the Stat instance + * @param name the name (dot separated) of the statistic + * @return the value. + * @throws AttributeNotFoundException if the attribute is invalid or not found. + */ + public static long getStatisticDataFor(Object instance, String name) + throws AttributeNotFoundException { + ReflectionHolder rh = getOrCreateReflectionHolder(instance.getClass().getClassLoader()); + if (rh.mhStatGetStatistic == null) { + throw new IllegalStateException( + "Cannot fetch statistic data for instance type " + instance.getClass()); + } + int idx = name.indexOf("."); + if (idx == -1) { + throw new AttributeNotFoundException("Invalid attribute name " + name); + } + String statName = name.substring(0, idx); + String attrName = name.substring(idx + 1); + try { + Object stat = rh.mhStatGetStatistic.invoke(instance, statName); + return dataFor(stat, attrName); + } catch (Throwable t) { + throw new AttributeNotFoundException( + "Unable to get statistic with name " + + name + + "from jee stat class " + + instance.getClass()); + } + } + + /** + * Check that's instance is instance of Stat. + * @param instance the instance + * @return a boolean + */ + public static boolean isJeeStat(Object instance) { + ReflectionHolder rh = getOrCreateReflectionHolder(instance.getClass().getClassLoader()); + return rh.classStat != null && rh.classStat.isInstance(instance); + } + + /** + * Check that's instance is instance of Statistic. + * @param instance the instance + * @return a boolean + */ + public static boolean isJeeStatistic(Object instance) { + ReflectionHolder rh = getOrCreateReflectionHolder(instance.getClass().getClassLoader()); + return rh.classStatistic != null && rh.classStatistic.isInstance(instance); + } +} diff --git a/src/test/java/org/datadog/jmxfetch/SimpleTestJavaApp.java b/src/test/java/org/datadog/jmxfetch/SimpleTestJavaApp.java index d245cb183..d0a099c75 100644 --- a/src/test/java/org/datadog/jmxfetch/SimpleTestJavaApp.java +++ b/src/test/java/org/datadog/jmxfetch/SimpleTestJavaApp.java @@ -1,10 +1,26 @@ package org.datadog.jmxfetch; +import org.datadog.jmxfetch.jee.BoundaryStatisticImpl; +import org.datadog.jmxfetch.jee.BoundedRangeStatisticImpl; +import org.datadog.jmxfetch.jee.CountStatisticImpl; +import org.datadog.jmxfetch.jee.JeeStats; +import org.datadog.jmxfetch.jee.RangeStatisticImpl; +import org.datadog.jmxfetch.jee.TimeStatisticImpl; +import org.datadog.jmxfetch.jee.UnsupportedStatisticImpl; + import java.math.BigDecimal; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import javax.management.j2ee.statistics.BoundaryStatistic; +import javax.management.j2ee.statistics.BoundedRangeStatistic; +import javax.management.j2ee.statistics.CountStatistic; +import javax.management.j2ee.statistics.RangeStatistic; +import javax.management.j2ee.statistics.Statistic; +import javax.management.j2ee.statistics.Stats; +import javax.management.j2ee.statistics.TimeStatistic; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.CompositeType; @@ -45,7 +61,19 @@ public class SimpleTestJavaApp implements SimpleTestJavaAppMBean { private final CompositeData nestedCompositeData; + // JEE Stats + private final CountStatistic countStatistic; + private final TimeStatistic timeStatistic; + private final RangeStatistic rangeStatistic; + private final BoundaryStatistic boundaryStatistic; + private final BoundedRangeStatistic boundedRangeStatistic; + private final Statistic unsupportedStatistic; + private final Stats jeeStats; + SimpleTestJavaApp() { + this(false); + } + SimpleTestJavaApp(boolean includeJeeStats) { hashmap.put("thisis0", 0); hashmap.put("thisis10", 10); hashmap.put("thisiscounter", 0); @@ -57,6 +85,23 @@ public class SimpleTestJavaApp implements SimpleTestJavaAppMBean { } nestedCompositeData = buildNestedCompositeData(); + if (includeJeeStats) { + countStatistic = new CountStatisticImpl("Sample Counter", long42424242); + timeStatistic = new TimeStatisticImpl("Sample Time", 0, Long.MAX_VALUE, long42424242, 1); + rangeStatistic = new RangeStatisticImpl("Sample Range", Long.MIN_VALUE, Long.MAX_VALUE, long42424242); + boundaryStatistic = new BoundaryStatisticImpl("Sample Boundary", Long.MIN_VALUE, Long.MAX_VALUE); + boundedRangeStatistic = new BoundedRangeStatisticImpl("Sample BoundedRange", Long.MIN_VALUE, Long.MAX_VALUE, 0, -1, +1); + unsupportedStatistic = new UnsupportedStatisticImpl("Sample Unsupported Statistic"); + jeeStats = new JeeStats(Collections.singletonMap("MyCounter", countStatistic)); + } else { + countStatistic = null; + timeStatistic = null; + rangeStatistic = null; + boundaryStatistic = null; + boundedRangeStatistic = null; + unsupportedStatistic = null; + jeeStats = null; + } } public int getShouldBe100() { @@ -203,6 +248,41 @@ public CompositeData getNestedCompositeData() { return this.nestedCompositeData; } + @Override + public Statistic getJeeCounter() { + return countStatistic; + } + + @Override + public Statistic getJeeRange() { + return rangeStatistic; + } + + @Override + public Statistic getJeeTime() { + return timeStatistic; + } + + @Override + public Statistic getJeeBoundary() { + return boundaryStatistic; + } + + @Override + public Statistic getJeeBoundedRange() { + return boundedRangeStatistic; + } + + @Override + public Statistic getJeeUnsupported() { + return unsupportedStatistic; + } + + @Override + public Stats getJeeStat() { + return jeeStats; + } + private CompositeData buildCompositeData(Integer i) { try { return new CompositeDataSupport( diff --git a/src/test/java/org/datadog/jmxfetch/SimpleTestJavaAppMBean.java b/src/test/java/org/datadog/jmxfetch/SimpleTestJavaAppMBean.java index 161724721..6c156bb12 100644 --- a/src/test/java/org/datadog/jmxfetch/SimpleTestJavaAppMBean.java +++ b/src/test/java/org/datadog/jmxfetch/SimpleTestJavaAppMBean.java @@ -4,6 +4,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import javax.management.j2ee.statistics.Statistic; +import javax.management.j2ee.statistics.Stats; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.TabularData; @@ -42,7 +44,22 @@ public interface SimpleTestJavaAppMBean { Float getInstanceFloat(); TabularData getTabulardata(); + TabularDataSupport getTabularDataSupport(); CompositeData getNestedCompositeData(); + + Statistic getJeeCounter(); + + Statistic getJeeRange(); + + Statistic getJeeTime(); + + Statistic getJeeBoundary(); + + Statistic getJeeBoundedRange(); + + Statistic getJeeUnsupported(); + + Stats getJeeStat(); } diff --git a/src/test/java/org/datadog/jmxfetch/TestApp.java b/src/test/java/org/datadog/jmxfetch/TestApp.java index 2f39e5840..1c58ac231 100644 --- a/src/test/java/org/datadog/jmxfetch/TestApp.java +++ b/src/test/java/org/datadog/jmxfetch/TestApp.java @@ -1126,6 +1126,48 @@ public void testTabularDataTagged() throws Exception { assertCoverage(); } + @Test + public void testJeeStatistics() throws Exception { + // We expose a few metrics through JMX + SimpleTestJavaApp testApp = new SimpleTestJavaApp(true); + registerMBean(testApp, "org.datadog.jmxfetch.test:type=SimpleTestJavaApp"); + + // We do a first collection + when(appConfig.isTargetDirectInstances()).thenReturn(true); + initApplication("jmx_jee_data.yaml"); + + run(); + List> metrics = getMetrics(); + + // 13 metrics from java.lang + 17 defined - 1 undefined + assertEquals(29, metrics.size()); + + List tags = Arrays.asList( + "instance:jmx_test_instance", + "jmx_domain:org.datadog.jmxfetch.test", + "type:SimpleTestJavaApp" + ); + final String prefix = "jmx.org.datadog.jmxfetch.test."; + + assertMetric(prefix + "jee_counter.count", testApp.getLong42424242(), tags, -1); + assertMetric(prefix + "jee_time.count", 1, tags, -1); + assertMetric(prefix + "jee_time.min_time", 0, tags, -1); + assertMetric(prefix + "jee_time.max_time", Long.MAX_VALUE, tags, -1); + assertMetric(prefix + "jee_time.total_time", testApp.getLong42424242(), tags, -1); + assertMetric(prefix + "jee_range.low_water_mark", Long.MIN_VALUE, tags, -1); + assertMetric(prefix + "jee_range.high_water_mark", Long.MAX_VALUE, tags, -1); + assertMetric(prefix + "jee_range.current", testApp.getLong42424242(), tags, -1); + assertMetric(prefix + "jee_boundary.lower_bound", Long.MIN_VALUE, tags, -1); + assertMetric(prefix + "jee_boundary.upper_bound", Long.MAX_VALUE, tags, -1); + assertMetric(prefix + "jee_bounded_range.low_water_mark", Long.MIN_VALUE, tags, -1); + assertMetric(prefix + "jee_bounded_range.high_water_mark", Long.MAX_VALUE, tags, -1); + assertMetric(prefix + "jee_bounded_range.current", 0, tags, -1); + assertMetric(prefix + "jee_bounded_range.lower_bound", -1, tags, -1); + assertMetric(prefix + "jee_bounded_range.upper_bound", 1, tags, -1); + assertMetric(prefix + "jee_stat.my_counter.count", testApp.getLong42424242(), tags, -1); + assertCoverage(); + } + @Test public void testNestedCompositeData() throws Exception { SimpleTestJavaApp testApp = new SimpleTestJavaApp(); diff --git a/src/test/java/org/datadog/jmxfetch/jee/BaseStatistic.java b/src/test/java/org/datadog/jmxfetch/jee/BaseStatistic.java new file mode 100644 index 000000000..1f45600d0 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/jee/BaseStatistic.java @@ -0,0 +1,36 @@ +package org.datadog.jmxfetch.jee; + +import javax.management.j2ee.statistics.Statistic; + +public abstract class BaseStatistic implements Statistic { + private final String name; + + public BaseStatistic(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getUnit() { + return ""; + } + + @Override + public String getDescription() { + return ""; + } + + @Override + public long getStartTime() { + return 0; + } + + @Override + public long getLastSampleTime() { + return 0; + } +} diff --git a/src/test/java/org/datadog/jmxfetch/jee/BoundaryStatisticImpl.java b/src/test/java/org/datadog/jmxfetch/jee/BoundaryStatisticImpl.java new file mode 100644 index 000000000..dbac88422 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/jee/BoundaryStatisticImpl.java @@ -0,0 +1,24 @@ +package org.datadog.jmxfetch.jee; + +import javax.management.j2ee.statistics.BoundaryStatistic; + +public class BoundaryStatisticImpl extends BaseStatistic implements BoundaryStatistic { + private final long low; + private final long high; + + public BoundaryStatisticImpl(String name, long low, long high) { + super(name); + this.low = low; + this.high = high; + } + + @Override + public long getUpperBound() { + return high; + } + + @Override + public long getLowerBound() { + return low; + } +} diff --git a/src/test/java/org/datadog/jmxfetch/jee/BoundedRangeStatisticImpl.java b/src/test/java/org/datadog/jmxfetch/jee/BoundedRangeStatisticImpl.java new file mode 100644 index 000000000..a6cfad715 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/jee/BoundedRangeStatisticImpl.java @@ -0,0 +1,25 @@ +package org.datadog.jmxfetch.jee; + +import javax.management.j2ee.statistics.BoundedRangeStatistic; + +public class BoundedRangeStatisticImpl extends RangeStatisticImpl implements BoundedRangeStatistic { + private final long low; + private final long high; + + public BoundedRangeStatisticImpl( + String name, long min, long max, long current, long low, long high) { + super(name, min, max, current); + this.low = low; + this.high = high; + } + + @Override + public long getUpperBound() { + return high; + } + + @Override + public long getLowerBound() { + return low; + } +} diff --git a/src/test/java/org/datadog/jmxfetch/jee/CountStatisticImpl.java b/src/test/java/org/datadog/jmxfetch/jee/CountStatisticImpl.java new file mode 100644 index 000000000..0946a2326 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/jee/CountStatisticImpl.java @@ -0,0 +1,17 @@ +package org.datadog.jmxfetch.jee; + +import javax.management.j2ee.statistics.CountStatistic; + +public class CountStatisticImpl extends BaseStatistic implements CountStatistic { + private final long count; + + public CountStatisticImpl(String name, long count) { + super(name); + this.count = count; + } + + @Override + public long getCount() { + return count; + } +} diff --git a/src/test/java/org/datadog/jmxfetch/jee/JeeStats.java b/src/test/java/org/datadog/jmxfetch/jee/JeeStats.java new file mode 100644 index 000000000..8f024a829 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/jee/JeeStats.java @@ -0,0 +1,28 @@ +package org.datadog.jmxfetch.jee; + +import javax.management.j2ee.statistics.Statistic; +import javax.management.j2ee.statistics.Stats; +import java.util.Map; + +public class JeeStats implements Stats { + private final Map statistics; + + public JeeStats(Map statistics) { + this.statistics = statistics; + } + + @Override + public Statistic getStatistic(String statisticName) { + return statistics.get(statisticName); + } + + @Override + public String[] getStatisticNames() { + return statistics.keySet().toArray(new String[0]); + } + + @Override + public Statistic[] getStatistics() { + return statistics.values().toArray(new Statistic[0]); + } +} diff --git a/src/test/java/org/datadog/jmxfetch/jee/RangeStatisticImpl.java b/src/test/java/org/datadog/jmxfetch/jee/RangeStatisticImpl.java new file mode 100644 index 000000000..6eacb8353 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/jee/RangeStatisticImpl.java @@ -0,0 +1,31 @@ +package org.datadog.jmxfetch.jee; + +import javax.management.j2ee.statistics.RangeStatistic; + +public class RangeStatisticImpl extends BaseStatistic implements RangeStatistic { + private final long current; + private final long max; + private final long min; + + public RangeStatisticImpl(String name, long min, long max, long current) { + super(name); + this.current = current; + this.max = max; + this.min = min; + } + + @Override + public long getHighWaterMark() { + return max; + } + + @Override + public long getLowWaterMark() { + return min; + } + + @Override + public long getCurrent() { + return current; + } +} diff --git a/src/test/java/org/datadog/jmxfetch/jee/TimeStatisticImpl.java b/src/test/java/org/datadog/jmxfetch/jee/TimeStatisticImpl.java new file mode 100644 index 000000000..a2979dff2 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/jee/TimeStatisticImpl.java @@ -0,0 +1,38 @@ +package org.datadog.jmxfetch.jee; + +import javax.management.j2ee.statistics.TimeStatistic; + +public class TimeStatisticImpl extends BaseStatistic implements TimeStatistic { + private final long count; + private final long min; + private final long max; + private final long total; + + public TimeStatisticImpl(String name, long min, long max, long total, long count) { + super(name); + this.count = count; + this.min = min; + this.max = max; + this.total = total; + } + + @Override + public long getCount() { + return count; + } + + @Override + public long getMaxTime() { + return max; + } + + @Override + public long getMinTime() { + return min; + } + + @Override + public long getTotalTime() { + return total; + } +} diff --git a/src/test/java/org/datadog/jmxfetch/jee/UnsupportedStatisticImpl.java b/src/test/java/org/datadog/jmxfetch/jee/UnsupportedStatisticImpl.java new file mode 100644 index 000000000..a7959432b --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/jee/UnsupportedStatisticImpl.java @@ -0,0 +1,12 @@ +package org.datadog.jmxfetch.jee; + +public class UnsupportedStatisticImpl extends BaseStatistic { + + public UnsupportedStatisticImpl(String name) { + super(name); + } + + public long getUnsupportedValue() { + return 0; + } +} diff --git a/src/test/resources/jmx_jee_data.yaml b/src/test/resources/jmx_jee_data.yaml new file mode 100644 index 000000000..2f759faac --- /dev/null +++ b/src/test/resources/jmx_jee_data.yaml @@ -0,0 +1,18 @@ +--- +init_config: + +instances: + - jvm_direct: true + refresh_beans: 4 + name: jmx_test_instance + conf: + - include: + domain: org.datadog.jmxfetch.test + attribute: + - JeeCounter # 1 metric + - JeeTime # 4 metrics + - JeeRange # 3 metrics + - JeeBoundary # 2 metrics + - JeeBoundedRange # 5 metrics + - JeeUnsupported # 0 metrics + - JeeStat # 1 child metric