Skip to content

Commit

Permalink
Support for dynamic mbeans.
Browse files Browse the repository at this point in the history
  • Loading branch information
johnou committed Dec 5, 2017
1 parent 1722438 commit d13f33e
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 3 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ Create `src/main/resources/jmxtrans.json`, add your mbeans and declare both `Con

In this sample, Graphite host & port are defaulted to `localhost:2003` and can be overwritten with system properties or environment variables, for example in `$CATALINA_BASE/conf/catalina.properties`.

#### Dynamic MBeans

If metrics are provided by a Dynamic MBeans all attributes can be collected by declaring an empty attributes array, for example :

```json
{
"queries": [
{
"objectName": "com.cocktail:type=CocktailService,name=cocktailService",
"resultAlias": "cocktail.controller",
"attributes": [ ]
}
]
}
```

### Start application and check metrics

#### Check metrics in the Console
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/org/jmxtrans/embedded/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public class Query implements QueryMBean {
* JMX attributes to collect. As an array for {@link javax.management.MBeanServer#getAttributes(javax.management.ObjectName, String[])}
*/
@Nonnull
private Map<String, QueryAttribute> attributesByName = new HashMap<String, QueryAttribute>();
private final Map<String, QueryAttribute> attributesByName = new HashMap<String, QueryAttribute>();
/**
* Copy of {@link #attributesByName}'s {@link java.util.Map#entrySet()} for performance optimization
*/
Expand Down Expand Up @@ -166,6 +166,11 @@ public void collectMetrics() {
logger.trace("Query {} returned {}", matchingObjectName, jmxAttributes);
for (Attribute jmxAttribute : jmxAttributes.asList()) {
QueryAttribute queryAttribute = this.attributesByName.get(jmxAttribute.getName());
if (queryAttribute == null) { // support for dynamic attributes
logger.trace("Creating query attribute for {}", jmxAttribute.getName());
queryAttribute = new QueryAttribute(jmxAttribute.getName(), null, null);
addAttribute(queryAttribute);
}
Object value = jmxAttribute.getValue();
int count = queryAttribute.collectMetrics(matchingObjectName, value, epochInMillis, this.queryResults);
collectedMetricsCount.addAndGet(count);
Expand Down
74 changes: 72 additions & 2 deletions src/test/java/org/jmxtrans/embedded/QueryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
import org.junit.BeforeClass;
import org.junit.Test;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.*;
import java.lang.management.ManagementFactory;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

Expand Down Expand Up @@ -93,6 +93,76 @@ public void test_composite_jmx_attribute() throws Exception {
assertThat(result2.getValue(), instanceOf(Number.class));
}

@Test
public void testDynamicAttributeSupport() throws Exception {
final int count = 42;
ObjectName objectName = new ObjectName("test:type=dynamic,category=metrics");
mbeanServer.registerMBean(new DynamicMBean() {
@Override
public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException {
if (attribute.equalsIgnoreCase("count1")) {
return count;
}
if (attribute.equalsIgnoreCase("count2")) {
return count + 1;
}
return 0;
}

@Override
public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
throw new UnsupportedOperationException();
}

@Override
public AttributeList getAttributes(String[] attributes) {
AttributeList list = new AttributeList();
list.add(new Attribute("count1", count));
list.add(new Attribute("count2", count + 1));
return list;
}

@Override
public AttributeList setAttributes(AttributeList attributes) {
throw new UnsupportedOperationException();
}

@Override
public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException {
throw new UnsupportedOperationException();
}

@Override
public MBeanInfo getMBeanInfo() {
MBeanAttributeInfo[] attrs = new MBeanAttributeInfo[2];
attrs[0] = new MBeanAttributeInfo("count1", "java.lang.Integer", "Test count 1", true, false, false);
attrs[1] = new MBeanAttributeInfo("count2", "java.lang.Integer", "Test count 2", true, false, false);
return new MBeanInfo(QueryTest.class.getCanonicalName(), "Provides access to test counts", attrs, null, null, null);
}
}, objectName);
try {
EmbeddedJmxTrans embeddedJmxTrans = new EmbeddedJmxTrans();

Query query = new Query("test:type=dynamic,category=metrics");

embeddedJmxTrans.addQuery(query);
query.collectMetrics();
assertThat(query.getResults().size(), is(2));

QueryResult result1 = query.getResults().poll();
assertThat(result1.getValue(), instanceOf(Number.class));
assertEquals("test.category__metrics.type__dynamic.count1", String.valueOf(result1.getName()));
assertEquals(count, Integer.parseInt(String.valueOf(result1.getValue())));

QueryResult result2 = query.getResults().poll();
assertThat(result2.getValue(), instanceOf(Number.class));
assertEquals("test.category__metrics.type__dynamic.count2", String.valueOf(result2.getName()));
assertEquals(count + 1, Integer.parseInt(String.valueOf(result2.getValue())));
} finally {
mbeanServer.unregisterMBean(objectName);
}
}

@Test
public void testExportResults() {
EmbeddedJmxTrans embeddedJmxTrans = new EmbeddedJmxTrans();
Expand Down

0 comments on commit d13f33e

Please sign in to comment.