This is an automated email from the ASF dual-hosted git repository.
joerghoh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
The following commit(s) were added to refs/heads/master by this push:
new 49e17d5e improved JMXExporter
new f9aee5ce Merge branch 'master' of github.com:apache/sling-whiteboard
49e17d5e is described below
commit 49e17d5e58ac5d37250ee18c3e48e712077d29e5
Author: Joerg Hoh <[email protected]>
AuthorDate: Mon Apr 4 17:46:55 2022 +0200
improved JMXExporter
---
jmx-exporter/README.md | 4 ++
jmx-exporter/pom.xml | 6 ++
.../jmxexporter/impl/JmxExporterImplFactory.java | 77 ++++++++++++++--------
.../jmxexporter/impl/JmxExporterFactoryTest.java | 65 +++++++++++++++---
jmx-exporter/src/test/resources/logback-test.xml | 30 +++++++++
5 files changed, 145 insertions(+), 37 deletions(-)
diff --git a/jmx-exporter/README.md b/jmx-exporter/README.md
new file mode 100644
index 00000000..d0274663
--- /dev/null
+++ b/jmx-exporter/README.md
@@ -0,0 +1,4 @@
+
+# JMX Exporter
+
+Make values of mbeans available via Sling Metrics
diff --git a/jmx-exporter/pom.xml b/jmx-exporter/pom.xml
index 06639a83..4a5d905b 100644
--- a/jmx-exporter/pom.xml
+++ b/jmx-exporter/pom.xml
@@ -154,6 +154,12 @@
<version>3.2.2</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.2.6</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>junit</groupId>
diff --git
a/jmx-exporter/src/main/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterImplFactory.java
b/jmx-exporter/src/main/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterImplFactory.java
index 1da4f58a..acdd5797 100644
---
a/jmx-exporter/src/main/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterImplFactory.java
+++
b/jmx-exporter/src/main/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterImplFactory.java
@@ -49,19 +49,19 @@ import
org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-@Component(configurationPolicy = ConfigurationPolicy.REQUIRE, immediate=true)
+@Component()
@Designate(ocd=JmxExporterImplFactory.Config.class, factory=true)
public class JmxExporterImplFactory {
@ObjectClassDefinition(name="JMX to Metrics Exporter")
public @interface Config {
- @AttributeDefinition(name="objectname", description="A objectname
pattern "
+ @AttributeDefinition(name="objectnames", description="export all
attribute of the MBeans matching these objectnames as Sling Metrics"
+ "(see
https://docs.oracle.com/en/java/javase/11/docs/api/java.management/javax/management/ObjectName.html")
- String objectname();
+ String[] objectnames();
@AttributeDefinition
- String webconsole_configurationFactory_nameHint() default "JMX to
Metrics Exporter for pattern {objectname}";
+ String webconsole_configurationFactory_nameHint() default "Pattern:
{objectnames}";
}
@@ -75,7 +75,7 @@ public class JmxExporterImplFactory {
@Activate
public void activate(Config config) {
server = ManagementFactory.getPlatformMBeanServer();
- registerMetrics(config.objectname());
+ registerMetrics(config.objectnames());
}
@@ -83,24 +83,29 @@ public class JmxExporterImplFactory {
* Register all applicable metrics for an objectname pattern
* @param pattern describes a objectname pattern
*/
- private void registerMetrics(String patternString) {
+ private void registerMetrics(String[] patterns) {
- try {
- ObjectName pattern = new ObjectName(patternString);
- Set<ObjectName> allMBeans = server.queryNames(pattern, null);
- allMBeans.forEach(objectname -> {
- LOG.debug("registering properties for {}",
objectname.toString());
- try {
- registerMBeanProperties(objectname);
- } catch (IntrospectionException | InstanceNotFoundException |
ReflectionException e) {
- LOG.error("Cannot register metrics for objectname = {}",
objectname.toString());
+ for (String patternString : patterns) {
+ try {
+ ObjectName pattern = new ObjectName(patternString);
+ Set<ObjectName> allMBeans = server.queryNames(pattern, null);
+ if (allMBeans.isEmpty()) {
+ LOG.info("pattern {} does not match any MBean",
patternString);
+ } else {
+ allMBeans.forEach(objectname -> {
+ LOG.debug("registering properties for {}",
objectname.toString());
+ try {
+ registerMBeanProperties(objectname);
+ } catch (IntrospectionException |
InstanceNotFoundException | ReflectionException e) {
+ LOG.error("Cannot register metrics for objectname
= {}", objectname.toString());
+ }
+ });
}
- });
- } catch (MalformedObjectNameException e) {
- LOG.error("cannot create an objectname from pattern
{}",patternString,e);
+ } catch (MalformedObjectNameException e) {
+ LOG.error("cannot create an objectname from pattern
{}",patternString,e);
+ }
}
-
}
@@ -110,24 +115,38 @@ public class JmxExporterImplFactory {
for (MBeanAttributeInfo attr : attributes) {
LOG.debug("Checking mbean = {}, name = {}, type={}",objectname,
attr.getName(), attr.getType());
+ Supplier<?> supplier = null;
if ("int".equals(attr.getType())) {
+ supplier = getSupplier(objectname, attr.getName(),-1);
+ } else if ("long".equals(attr.getType())) {
+ supplier = getSupplier(objectname, attr.getName(),-1L);
+ }
+
+ if (supplier != null) {
String metricName = toMetricName(objectname, attr.getName());
- LOG.info("Registering as metric: {}", metricName);
- Supplier<Integer> supplier = () -> {
- try {
- return (Integer) server.getAttribute(objectname,
attr.getName());
- } catch (InstanceNotFoundException |
AttributeNotFoundException | ReflectionException
- | MBeanException e) {
- LOG.warn("error when retrieving value for mbean
{}/attribute {}",objectname, attr.getName(),e);
- return -1;
- }
- };
+ LOG.info("Registering metric {} from MBean (objectname={},
name={}, type={})",
+ metricName,objectname.toString(), attr.getName(),
attr.getType());
metrics.gauge(metricName, supplier);
}
}
}
+ private <T> Supplier<T> getSupplier ( ObjectName name, String
attributeName, T defaultValue ) {
+
+ Supplier<T> supplier = () -> {
+ try {
+ return (T) server.getAttribute(name, attributeName);
+ } catch (InstanceNotFoundException | AttributeNotFoundException |
ReflectionException
+ | MBeanException e) {
+ LOG.warn("error when retrieving value for mbean {}/attribute
{}",name, attributeName,e);
+ return defaultValue;
+ }
+
+ };
+ return supplier;
+ }
+
protected String toMetricName(ObjectName objectName, String attributeName)
{
String name = "sling"; // default domain
diff --git
a/jmx-exporter/src/test/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterFactoryTest.java
b/jmx-exporter/src/test/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterFactoryTest.java
index 841db99b..6d3ab80e 100644
---
a/jmx-exporter/src/test/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterFactoryTest.java
+++
b/jmx-exporter/src/test/java/org/apache/sling/whiteboard/jmxexporter/impl/JmxExporterFactoryTest.java
@@ -22,6 +22,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Map;
import java.util.function.Supplier;
import javax.management.InstanceAlreadyExistsException;
@@ -53,21 +55,36 @@ public class JmxExporterFactoryTest {
@Captor
ArgumentCaptor<Supplier<Integer>> intSupplierCaptor;
+ @Captor
+ ArgumentCaptor<Supplier<Long>> longSupplierCaptor;
+
JmxExporterImplFactory exporter;
- private static final String OBJECT_NAME =
"org.apache.sling.whiteboard.jmxexporter.impl:type=sample";
+ private static final String OBJECT_NAME_0 =
"org.apache.sling.whiteboard.jmxexporter.impl0:type=sample1";
+ private static final String OBJECT_NAME_1 =
"org.apache.sling.whiteboard.jmxexporter.impl0.impl2:type=sample2";
+ private static final String OBJECT_NAME_2 =
"org.apache.sling.whiteboard.jmxexporter.impl1:type=sample3";
+
+ // Query which will only match OBJECT_NAME_0 and OBJECT_NAME_1
+ private static final String OBJECT_NAME_QUERY =
"org.apache.sling.whiteboard.jmxexporter.impl0*:type=*";
- private static final String EXPECTED_INT_NAME =
"org.apache.sling.whiteboard.jmxexporter.impl.sample.Int";
+ private static final String EXPECTED_0_INT_NAME =
"org.apache.sling.whiteboard.jmxexporter.impl0.sample1.Int";
+ private static final String EXPECTED_0_LONG_NAME =
"org.apache.sling.whiteboard.jmxexporter.impl0.sample1.Long";
+
+ private static final String EXPECTED_1_INT_NAME =
"org.apache.sling.whiteboard.jmxexporter.impl0.impl2.sample2.Int";
+ private static final String EXPECTED_1_LONG_NAME =
"org.apache.sling.whiteboard.jmxexporter.impl0.impl2.sample2.Long";
MetricsService metrics;
+ SimpleBean mbeans[] = { new SimpleBean(0,0L), new SimpleBean(1,1L), new
SimpleBean(2,2L)};
+
@Before
public void setup() throws MalformedObjectNameException,
InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
- ObjectName mbeanName = new ObjectName(OBJECT_NAME);
- SimpleBean mbean = new SimpleBean();
- server.registerMBean(mbean, mbeanName);
+
+ server.registerMBean(mbeans[0],new ObjectName(OBJECT_NAME_0));
+ server.registerMBean(mbeans[1],new ObjectName(OBJECT_NAME_1));
+ server.registerMBean(mbeans[2],new ObjectName(OBJECT_NAME_2));
exporter = new JmxExporterImplFactory();
metrics = Mockito.mock(MetricsService.class);
@@ -76,22 +93,53 @@ public class JmxExporterFactoryTest {
@Test
public void test() {
- context.registerInjectActivateService(exporter,
"objectname",OBJECT_NAME);
- Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_INT_NAME),
intSupplierCaptor.capture());
+ Map<String,Object> props = new HashMap<>();
+ props.put("objectnames", new String[]{OBJECT_NAME_QUERY});
+ context.registerInjectActivateService(exporter, props);
+
+
+ // Integer
+ Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_0_INT_NAME),
intSupplierCaptor.capture());
+ assertEquals(new Integer(0),intSupplierCaptor.getValue().get());
+ mbeans[0].setInt(10);
+ Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_0_INT_NAME),
intSupplierCaptor.capture());
+ assertEquals(new Integer(10),intSupplierCaptor.getValue().get());
+
+ // Long
+ Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_0_LONG_NAME),
longSupplierCaptor.capture());
+ assertEquals(new Long(0L),longSupplierCaptor.getValue().get());
+
+ // MBean 1
+ Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_1_INT_NAME),
intSupplierCaptor.capture());
assertEquals(new Integer(1),intSupplierCaptor.getValue().get());
+ Mockito.verify(metrics).gauge(Mockito.eq(EXPECTED_1_LONG_NAME),
longSupplierCaptor.capture());
+ assertEquals(new Long(1L),longSupplierCaptor.getValue().get());
+
+
}
static class SimpleBean implements SimpleBeanMBean {
- int internalInt = 1;
+ int internalInt = 0;
+ long internalLong = 0L;
+
+ public SimpleBean(int i, long l) {
+ internalInt = i;
+ internalLong = l;
+ }
@Override
public int getInt() {
return internalInt;
}
+ @Override
+ public long getLong() {
+ return internalLong;
+ }
+
public void setInt(int value) {
internalInt = value;
}
@@ -103,6 +151,7 @@ public class JmxExporterFactoryTest {
static public interface SimpleBeanMBean {
public int getInt();
+ public long getLong();
}
diff --git a/jmx-exporter/src/test/resources/logback-test.xml
b/jmx-exporter/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..c0f6ce11
--- /dev/null
+++ b/jmx-exporter/src/test/resources/logback-test.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
%msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <root level="info">
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration>
\ No newline at end of file