diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index ea7e27816e..d83de18775 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -37,6 +37,7 @@ Use subheadings with the "=====" level for adding notes for unreleased changes:
* Added support for AWS SDK 2.21 - {pull}3373[#3373]
* Capture bucket and object key to Lambda transaction as OTel attributes - `aws.s3.bueckt`, `aws.s3.key` - {pull}3364[#3364]
* Added `context_propagation_only` configuration option - {pull}3358[#3358]
+* Added attribute[*] for JMX pattern metrics (all metrics can now be generated with `object_name[*:type=*,name=*] attribute[*]`) - {pull}3376[#3376]
[float]
===== Bug fixes
diff --git a/apm-agent-builds/apm-agent-java8/pom.xml b/apm-agent-builds/apm-agent-java8/pom.xml
index 2ccc31a115..c2e4c18bf2 100644
--- a/apm-agent-builds/apm-agent-java8/pom.xml
+++ b/apm-agent-builds/apm-agent-java8/pom.xml
@@ -21,7 +21,7 @@
org.apache.logging.log4j
log4j-bom
- 2.21.0
+ 2.21.1
import
pom
diff --git a/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxConfiguration.java b/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxConfiguration.java
index e33848c0e2..e1026b4e8c 100644
--- a/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxConfiguration.java
+++ b/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxConfiguration.java
@@ -58,8 +58,9 @@ public class JmxConfiguration extends ConfigurationOptionProvider {
"\n" +
"The agent creates `labels` for each link:https://docs.oracle.com/javase/7/docs/api/javax/management/ObjectName.html#getKeyPropertyList()[JMX key property] such as `type` and `name`.\n" +
"\n" +
- "The link:https://docs.oracle.com/javase/7/docs/api/javax/management/ObjectName.html[JMX object name pattern] supports wildcards.\n" +
- "In this example, the agent will create a metricset for each memory pool `name` (such as `G1 Old Generation` and `G1 Young Generation`)\n" +
+ "The link:https://docs.oracle.com/javase/7/docs/api/javax/management/ObjectName.html[JMX object name pattern] supports wildcards. The attribute definition does NOT support wildcards, but a special definition `attribute[*]` is accepted (from 1.44.0) to mean match all possible (numeric) attributes for the associated object name pattern\n" +
+ "The definition `object_name[*:type=*,name=*] attribute[*]` would match all possible JMX metrics\n" +
+ "In the following example, the agent will create a metricset for each memory pool `name` (such as `G1 Old Generation` and `G1 Young Generation`)\n" +
"\n" +
"----\n" +
"object_name[java.lang:type=GarbageCollector,name=*] attribute[CollectionCount:metric_name=collection_count] attribute[CollectionTime]\n" +
diff --git a/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxMetric.java b/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxMetric.java
index 7427494bad..9fa1b4da6d 100644
--- a/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxMetric.java
+++ b/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxMetric.java
@@ -184,8 +184,12 @@ public String getJmxAttributeName() {
return jmxAttributeName;
}
- public String getMetricName() {
- return JMX_PREFIX + (metricName != null ? metricName : jmxAttributeName);
+ public String getMetricName(String foundAttributeName) {
+ if (!foundAttributeName.isEmpty() && "*".equals(jmxAttributeName)) {
+ return JMX_PREFIX + (metricName != null ? metricName : foundAttributeName);
+ } else {
+ return JMX_PREFIX + (metricName != null ? metricName : jmxAttributeName);
+ }
}
@Override
@@ -211,8 +215,8 @@ public Labels getLabels(ObjectName objectName) {
return Labels.Mutable.of(objectName.getKeyPropertyList());
}
- public String getCompositeMetricName(String key) {
- return getMetricName() + "." + key;
+ public String getCompositeMetricName(String key, String foundAttributeName) {
+ return getMetricName(foundAttributeName) + "." + key;
}
}
diff --git a/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxMetricTracker.java b/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxMetricTracker.java
index 300cbb22de..685eb59992 100644
--- a/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxMetricTracker.java
+++ b/apm-agent-plugins/apm-jmx-plugin/src/main/java/co/elastic/apm/agent/jmx/JmxMetricTracker.java
@@ -33,6 +33,8 @@
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MBeanServerFactory;
@@ -42,6 +44,7 @@
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
+import javax.management.RuntimeMBeanException;
import javax.management.openmbean.CompositeData;
import javax.management.relation.MBeanServerNotificationFilter;
import java.lang.management.ManagementFactory;
@@ -284,7 +287,7 @@ private void register(List jmxMetrics, MBeanServer server) {
}
/**
- * A single {@link JmxMetric} can yield multiple {@link JmxMetricRegistration}s if the {@link JmxMetric} contains multiple {@link JmxMetric#attributes}
+ * A single {@link JmxMetric} can yield multiple {@link JmxMetricRegistration}s if the {@link JmxMetric} contains multiple attributes
*/
private List compileJmxMetricRegistrations(List jmxMetrics, MBeanServer server) {
List registrations = new ArrayList<>();
@@ -308,40 +311,98 @@ private static void addJmxMetricRegistration(final JmxMetric jmxMetric, List keys = labels.getKeys();
+ for (int i = 0; i < keys.size(); i++) {
+ if ("type".equals(keys.get(i))) {
+ return labels.getValue(i) + ".";
+ }
+ }
+ return "";
+ }
+
+ private static void addJmxMetricRegistration(JmxMetric jmxMetric, List registrations, ObjectName objectName, Object value, JmxMetric.Attribute attribute, String attributeName, String metricPrepend) throws AttributeNotFoundException {
+ if (value instanceof Number) {
+ logger.debug("Found number attribute {}={}", attribute.getJmxAttributeName(), value);
+ registrations.add(
+ new JmxMetricRegistration(
+ attribute.getMetricName(
+ metricPrepend == null ?
+ attributeName :
+ metricPrepend + attributeName
+ ),
+ attribute.getLabels(objectName),
+ attributeName,
+ null,
+ objectName
+ )
+ );
+ } else if (value instanceof CompositeData) {
+ final CompositeData compositeValue = (CompositeData) value;
+ for (final String key : compositeValue.getCompositeType().keySet()) {
+ if (compositeValue.get(key) instanceof Number) {
+ logger.debug("Found composite number attribute {}.{}={}", attribute.getJmxAttributeName(), key, value);
+ registrations.add(
+ new JmxMetricRegistration(
+ attribute.getCompositeMetricName(
+ key,
+ metricPrepend == null ?
+ attributeName :
+ metricPrepend + attributeName
+ ),
+ attribute.getLabels(objectName),
+ attributeName,
+ key,
+ objectName
+ )
+ );
+ } else {
+ if (!isWildcard(attribute)) {
+ logger.warn("Can't create metric '{}' because composite value '{}' is not a number: '{}'", jmxMetric, key, value);
+ }
+ }
+ }
+ } else {
+ if (!isWildcard(attribute)) {
+ logger.warn("Can't create metric '{}' because attribute '{}' is not a number: '{}'", jmxMetric, attributeName, value);
+ }
+ }
+ }
+
static class JmxMetricRegistration {
private static final Logger logger = LoggerFactory.getLogger(JmxMetricRegistration.class);
private final String metricName;
diff --git a/apm-agent-plugins/apm-jmx-plugin/src/test/java/co/elastic/apm/agent/jmx/JmxMetricTrackerTest.java b/apm-agent-plugins/apm-jmx-plugin/src/test/java/co/elastic/apm/agent/jmx/JmxMetricTrackerTest.java
index 17fa88bae5..cebd3c78c5 100644
--- a/apm-agent-plugins/apm-jmx-plugin/src/test/java/co/elastic/apm/agent/jmx/JmxMetricTrackerTest.java
+++ b/apm-agent-plugins/apm-jmx-plugin/src/test/java/co/elastic/apm/agent/jmx/JmxMetricTrackerTest.java
@@ -83,6 +83,17 @@ void testGC() throws Exception {
printMetricSets();
}
+ @Test
+ void testAttributeWildcard() throws Exception {
+ setConfig(JmxMetric.valueOf("object_name[java.lang:type=GarbageCollector,name=*] attribute[*]"));
+ for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
+ String memoryManagerName = gcBean.getName();
+ assertThat(metricRegistry.getGaugeValue("jvm.jmx.collection_count", Labels.Mutable.of("name", memoryManagerName).add("type", "GarbageCollector"))).isNotNegative();
+ assertThat(metricRegistry.getGaugeValue("jvm.jmx.CollectionTime", Labels.Mutable.of("name", memoryManagerName).add("type", "GarbageCollector"))).isNotNegative();
+ }
+ printMetricSets();
+ }
+
@Test
void testRemoveMetric() throws Exception {
setConfig(JmxMetric.valueOf("object_name[java.lang:type=GarbageCollector,name=*] attribute[CollectionCount:metric_name=collection_count] attribute[CollectionTime]"));
diff --git a/apm-agent-plugins/apm-jmx-plugin/src/test/java/co/elastic/apm/agent/jmx/JmxMetricValueConverterTest.java b/apm-agent-plugins/apm-jmx-plugin/src/test/java/co/elastic/apm/agent/jmx/JmxMetricValueConverterTest.java
index 8f92231a9e..9cc2c106a5 100644
--- a/apm-agent-plugins/apm-jmx-plugin/src/test/java/co/elastic/apm/agent/jmx/JmxMetricValueConverterTest.java
+++ b/apm-agent-plugins/apm-jmx-plugin/src/test/java/co/elastic/apm/agent/jmx/JmxMetricValueConverterTest.java
@@ -40,7 +40,7 @@ private static void testMetricName(String s, String expectedMetricName, String e
JmxMetric metric = JmxMetric.valueOf(s);
assertThat(metric.getAttributes()).hasSize(1);
metric.getAttributes().forEach(a -> {
- assertThat(a.getMetricName()).isEqualTo(expectedMetricName);
+ assertThat(a.getMetricName("")).isEqualTo(expectedMetricName);
assertThat(a.getJmxAttributeName()).isEqualTo(expectedJmxAttributeName);
});
}
diff --git a/apm-agent-plugins/apm-micrometer-plugin/pom.xml b/apm-agent-plugins/apm-micrometer-plugin/pom.xml
index 688d25c9a3..318fa15616 100644
--- a/apm-agent-plugins/apm-micrometer-plugin/pom.xml
+++ b/apm-agent-plugins/apm-micrometer-plugin/pom.xml
@@ -29,7 +29,7 @@
io.micrometer
micrometer-core
- 1.11.5
+ 1.12.0
provided
diff --git a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/SpringAmqpBatchIT.java b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/SpringAmqpBatchIT.java
index 776a7f786e..d6b00fcfe0 100644
--- a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/SpringAmqpBatchIT.java
+++ b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/SpringAmqpBatchIT.java
@@ -19,12 +19,12 @@
package co.elastic.apm.agent.rabbitmq;
-import co.elastic.apm.agent.tracer.configuration.MessagingConfiguration;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.TraceContext;
import co.elastic.apm.agent.impl.transaction.Transaction;
+import co.elastic.apm.agent.rabbitmq.components.batch.BatchListenerComponent;
import co.elastic.apm.agent.rabbitmq.config.BatchConfiguration;
-import org.junit.Ignore;
+import co.elastic.apm.agent.tracer.configuration.MessagingConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.BatchingRabbitTemplate;
@@ -42,10 +42,9 @@
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.doReturn;
-@Ignore
@RunWith(SpringRunner.class)
@SpringBootTest
-@ContextConfiguration(classes = {BatchConfiguration.class}, initializers = {RabbitMqTestBase.Initializer.class})
+@ContextConfiguration(classes = {BatchConfiguration.class, BatchListenerComponent.class}, initializers = {RabbitMqTestBase.Initializer.class})
public class SpringAmqpBatchIT extends RabbitMqTestBase {
@Autowired
@@ -158,7 +157,7 @@ public void testTransactionPerBatch() {
.filter(span -> Objects.equals(span.getNameAsString(), "RabbitMQ SEND to "))
.collect(Collectors.toList());
assertThat(sendSpans.size()).isEqualTo(2);
- sendSpans.forEach(span -> {
+ sendSpans.forEach(span -> {
assertThat(span.getType()).isEqualTo("messaging");
assertThat(span.getTraceContext().getParentId()).isEqualTo(rootTraceTransaction.getTraceContext().getId());
});
@@ -193,3 +192,4 @@ public void testTransactionPerBatch() {
rootTraceTransaction.deactivate().end();
}
}
+
diff --git a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/components/batch/BatchListenerComponent.java b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/components/batch/BatchListenerComponent.java
new file mode 100644
index 0000000000..90e160aae1
--- /dev/null
+++ b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/components/batch/BatchListenerComponent.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.rabbitmq.components.batch;
+
+import co.elastic.apm.agent.rabbitmq.TestConstants;
+import co.elastic.apm.agent.sdk.logging.Logger;
+import co.elastic.apm.agent.sdk.logging.LoggerFactory;
+import co.elastic.apm.api.CaptureSpan;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+import static co.elastic.apm.agent.rabbitmq.TestConstants.QUEUE_NAME;
+
+@Component
+public class BatchListenerComponent {
+
+ public static final Logger logger = LoggerFactory.getLogger(BatchListenerComponent.class);
+
+ @RabbitListener(
+ queues = TestConstants.QUEUE_NAME,
+ containerFactory = "simpleRabbitListenerContainerFactory"
+ )
+ public void receiveWorkingBatch(List batchMessages) {
+ logger.info("Received batch of size {} from '{}'", batchMessages.size(), QUEUE_NAME);
+ batchMessages.forEach(message -> {
+ logger.info("Message in 'spring-boot' batch: {}", message.getBody());
+ testSpan();
+ });
+ }
+
+ @CaptureSpan(value = "testSpan", type = "custom", subtype = "anything", action = "test")
+ public void testSpan() {
+ }
+}
diff --git a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/config/BaseConfiguration.java b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/config/BaseConfiguration.java
index 9c9f8e9efe..863ec3f7cc 100644
--- a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/config/BaseConfiguration.java
+++ b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/config/BaseConfiguration.java
@@ -30,6 +30,7 @@
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.web.client.RestTemplate;
diff --git a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/config/BatchConfiguration.java b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/config/BatchConfiguration.java
index 8d8db7c3a4..eef1d4dcaa 100644
--- a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/config/BatchConfiguration.java
+++ b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/src/test/java/co/elastic/apm/agent/rabbitmq/config/BatchConfiguration.java
@@ -19,13 +19,10 @@
package co.elastic.apm.agent.rabbitmq.config;
-import co.elastic.apm.agent.rabbitmq.TestConstants;
import co.elastic.apm.agent.sdk.logging.Logger;
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
-import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
-import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.batch.SimpleBatchingStrategy;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
@@ -37,8 +34,6 @@
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import java.util.List;
-
import static co.elastic.apm.agent.rabbitmq.TestConstants.QUEUE_NAME;
@EnableRabbit
@@ -83,16 +78,4 @@ public Jackson2JsonMessageConverter converter() {
public Queue queue() {
return new Queue(QUEUE_NAME, false);
}
-
- @RabbitListener(
- queues = TestConstants.QUEUE_NAME,
- containerFactory = "simpleRabbitListenerContainerFactory"
- )
- public void receiveWorkingBatch(List batchMessages) {
- logger.info("Received batch of size {} from '{}'", batchMessages.size(), QUEUE_NAME);
- batchMessages.forEach(message -> {
- logger.info("Message in 'spring-boot' batch: {}", message.getBody());
- testSpan();
- });
- }
}
diff --git a/catalog-info.yaml b/catalog-info.yaml
index 6730746268..c7c179d53b 100644
--- a/catalog-info.yaml
+++ b/catalog-info.yaml
@@ -27,6 +27,8 @@ spec:
access_level: MANAGE_BUILD_AND_READ
observablt-robots:
access_level: MANAGE_BUILD_AND_READ
+ observablt-robots-automation:
+ access_level: BUILD_AND_READ
everyone:
access_level: READ_ONLY
@@ -59,6 +61,8 @@ spec:
access_level: MANAGE_BUILD_AND_READ
observablt-robots:
access_level: MANAGE_BUILD_AND_READ
+ observablt-robots-automation:
+ access_level: BUILD_AND_READ
everyone:
access_level: READ_ONLY
@@ -91,6 +95,8 @@ spec:
access_level: MANAGE_BUILD_AND_READ
observablt-robots:
access_level: MANAGE_BUILD_AND_READ
+ observablt-robots-automation:
+ access_level: BUILD_AND_READ
everyone:
access_level: READ_ONLY
@@ -124,6 +130,8 @@ spec:
access_level: MANAGE_BUILD_AND_READ
observablt-robots:
access_level: MANAGE_BUILD_AND_READ
+ observablt-robots-automation:
+ access_level: BUILD_AND_READ
everyone:
access_level: READ_ONLY
schedules:
diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc
index 6a40c95213..3c3b2fe75d 100644
--- a/docs/configuration.asciidoc
+++ b/docs/configuration.asciidoc
@@ -2030,8 +2030,9 @@ Note that all JMX metric names will be prefixed with `jvm.jmx.` by the agent.
The agent creates `labels` for each link:https://docs.oracle.com/javase/7/docs/api/javax/management/ObjectName.html#getKeyPropertyList()[JMX key property] such as `type` and `name`.
-The link:https://docs.oracle.com/javase/7/docs/api/javax/management/ObjectName.html[JMX object name pattern] supports wildcards.
-In this example, the agent will create a metricset for each memory pool `name` (such as `G1 Old Generation` and `G1 Young Generation`)
+The link:https://docs.oracle.com/javase/7/docs/api/javax/management/ObjectName.html[JMX object name pattern] supports wildcards. The attribute definition does NOT support wildcards, but a special definition `attribute[*]` is accepted (from 1.44.0) to mean match all possible (numeric) attributes for the associated object name pattern
+The definition `object_name[*:type=*,name=*] attribute[*]` would match all possible JMX metrics
+In the following example, the agent will create a metricset for each memory pool `name` (such as `G1 Old Generation` and `G1 Young Generation`)
----
object_name[java.lang:type=GarbageCollector,name=*] attribute[CollectionCount:metric_name=collection_count] attribute[CollectionTime]
@@ -4326,8 +4327,9 @@ Example: `5ms`.
#
# The agent creates `labels` for each link:https://docs.oracle.com/javase/7/docs/api/javax/management/ObjectName.html#getKeyPropertyList()[JMX key property] such as `type` and `name`.
#
-# The link:https://docs.oracle.com/javase/7/docs/api/javax/management/ObjectName.html[JMX object name pattern] supports wildcards.
-# In this example, the agent will create a metricset for each memory pool `name` (such as `G1 Old Generation` and `G1 Young Generation`)
+# The link:https://docs.oracle.com/javase/7/docs/api/javax/management/ObjectName.html[JMX object name pattern] supports wildcards. The attribute definition does NOT support wildcards, but a special definition `attribute[*]` is accepted (from 1.44.0) to mean match all possible (numeric) attributes for the associated object name pattern
+# The definition `object_name[*:type=*,name=*] attribute[*]` would match all possible JMX metrics
+# In the following example, the agent will create a metricset for each memory pool `name` (such as `G1 Old Generation` and `G1 Young Generation`)
#
# ----
# object_name[java.lang:type=GarbageCollector,name=*] attribute[CollectionCount:metric_name=collection_count] attribute[CollectionTime]