Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
88ead64
move activemq to library
SylvainJuge Sep 30, 2025
684be1b
start basic activemq instance
SylvainJuge Sep 30, 2025
2822152
Merge branch 'main' of github.com:open-telemetry/opentelemetry-java-i…
SylvainJuge Oct 14, 2025
5a2569a
Merge branch 'main' of github.com:open-telemetry/opentelemetry-java-i…
SylvainJuge Oct 14, 2025
606f7b2
messaging + consumer/producer metrics
SylvainJuge Oct 15, 2025
a7939d5
start adding tests
SylvainJuge Oct 15, 2025
9378354
wip
SylvainJuge Oct 15, 2025
2bbd993
broker metrics to top level, destination lower
SylvainJuge Oct 15, 2025
974fce3
fix tests and move some to 'destination'
SylvainJuge Oct 15, 2025
aa88e69
add percentage unit conversion
SylvainJuge Oct 15, 2025
53e8dee
add percentage unit conversion
SylvainJuge Oct 15, 2025
0ebf5bd
reformat tests
SylvainJuge Oct 15, 2025
c4126bf
reformat table
SylvainJuge Oct 15, 2025
9efbb68
simplify bean definition
SylvainJuge Oct 15, 2025
413707f
use latest classic version
SylvainJuge Oct 15, 2025
2f38f92
add avg time spent in queue
SylvainJuge Oct 15, 2025
f38757b
minor wording + document
SylvainJuge Oct 15, 2025
7b63be1
./gradlew spotlessApply
otelbot[bot] Oct 15, 2025
3b6bd3a
Merge branch 'main' into jmx-activemq
SylvainJuge Oct 17, 2025
774d835
remove 'messaging.system'
SylvainJuge Oct 23, 2025
55841a4
update doc
SylvainJuge Oct 23, 2025
4fc2dca
post-review changes
SylvainJuge Oct 23, 2025
6ea8437
Merge branch 'main' of github.com:open-telemetry/opentelemetry-java-i…
SylvainJuge Nov 12, 2025
fbe7e10
Merge branch 'main' into jmx-activemq
SylvainJuge Nov 17, 2025
56cf85c
make activemq test package private
SylvainJuge Nov 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion instrumentation/jmx-metrics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ $ java -javaagent:path/to/opentelemetry-javaagent.jar \

No targets are enabled by default. The supported target environments are listed below.

- [activemq](javaagent/activemq.md)
- [activemq](library/activemq.md)
- [camel](javaagent/camel.md)
- [jetty](library/jetty.md)
- [kafka-broker](javaagent/kafka-broker.md)
Expand Down
17 changes: 0 additions & 17 deletions instrumentation/jmx-metrics/javaagent/activemq.md

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
class JmxMetricInsightInstallerTest {
private static final String PATH_TO_ALL_EXISTING_RULES = "src/main/resources/jmx/rules";
private static final Set<String> FILES_TO_BE_TESTED =
new HashSet<>(Arrays.asList("activemq.yaml", "camel.yaml", "kafka-broker.yaml"));
new HashSet<>(Arrays.asList("camel.yaml", "kafka-broker.yaml"));

@Test
void testToVerifyExistingRulesAreValid() throws Exception {
Expand Down
33 changes: 33 additions & 0 deletions instrumentation/jmx-metrics/library/activemq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ActiveMQ Metrics

Here is the list of metrics based on MBeans exposed by ActiveMQ.

For now, only ActiveMQ classic is supported.

| Metric Name | Type | Unit | Attributes | Description |
|-------------------------------------------|---------------|--------------|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|
| activemq.producer.count | UpDownCounter | {producer} | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The number of producers attached to this destination |
| activemq.consumer.count | UpDownCounter | {consumer} | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The number of consumers subscribed to this destination |
| activemq.memory.destination.usage | UpDownCounter | By | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The amount of used memory by this destination |
| activemq.memory.destination.limit | UpDownCounter | By | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The amount of configured memory limit for this destination |
| activemq.temp.destination.utilization | gauge | 1 | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The percentage of non-persistent storage used by this destination |
| activemq.temp.destination.limit | UpDownCounter | By | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The amount of configured non-persistent storage limit |
| activemq.message.queue.size | UpDownCounter | {message} | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The current number of messages waiting to be consumed |
| activemq.message.expired | Counter | {message} | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The number of messages not delivered because they expired |
| activemq.message.enqueued | Counter | {message} | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The number of messages sent to this destination |
| activemq.message.dequeued | Counter | {message} | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The number of messages acknowledged and removed from this destination |
| activemq.message.enqueue.average_duration | Gauge | s | messaging.system, messaging.destination.name, activemq.broker.name, activemq.destination.type | The average time a message was held on this destination |
| activemq.connection.count | UpDownCounter | {connection} | messaging.system, activemq.broker.name | The number of active connections |
| activemq.memory.utilization | Gauge | 1 | messaging.system, activemq.broker.name | The percentage of broker memory used |
| activemq.memory.limit | UpDownCounter | By | messaging.system, activemq.broker.name | The amount of configured broker memory limit |
| activemq.store.utilization | Gauge | 1 | messaging.system, activemq.broker.name | The percentage of broker persistent storage used |
| activemq.store.limit | UpDownCounter | By | messaging.system, activemq.broker.name | The amount of configured broker persistent storage limit |
| activemq.temp.utilization | Gauge | 1 | messaging.system, activemq.broker.name | The percentage of broker non-persistent storage used |
| activemq.temp.limit | UpDownCounter | By | messaging.system, activemq.broker.name | The amount of configured broker non-persistent storage limit |

## Attributes

- `messaging.system` is always set to `activemq` ([semconv](https://opentelemetry.io/docs/specs/semconv/registry/attributes/messaging/#messaging-system))
- `messaging.destination.name` contains the destination.name ([semconv](https://opentelemetry.io/docs/specs/semconv/registry/attributes/messaging/#messaging-destination-name))
- `activemq.broker.name` contains the name of the broker
- `activemq.destination.type` is set to `queue` for queues (point-to-point), `topic` for topics (multicast).
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class UnitConverter {
registerConversion("ms", "s", value -> value.doubleValue() / TimeUnit.SECONDS.toMillis(1));
registerConversion("us", "s", value -> value.doubleValue() / TimeUnit.SECONDS.toMicros(1));
registerConversion("ns", "s", value -> value.doubleValue() / TimeUnit.SECONDS.toNanos(1));
registerConversion("%", "1", value -> value.doubleValue() / 100d);
}

private final Function<Number, Number> convertingFunction;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
rules:

# Topic and Queues level metrics
- bean: org.apache.activemq:type=Broker,brokerName=*,destinationType=*,destinationName=*
metricAttribute:
messaging.destination.name: param(destinationName)
messaging.system: const(activemq)
# 'topic' or 'queue'
activemq.destination.type: lowercase(param(destinationType))
activemq.broker.name: param(brokerName)
prefix: activemq.
mapping:
# activemq.producer.count
ProducerCount:
metric: producer.count
unit: "{producer}"
type: updowncounter
desc: The number of producers attached to this destination
# activemq.consumer.count
ConsumerCount:
metric: consumer.count
unit: "{consumer}"
type: updowncounter
desc: The number of consumers subscribed to this destination
# activemq.memory.destination.usage
MemoryUsageByteCount:
metric: memory.destination.usage
unit: By
type: updowncounter
desc: The amount of used memory by this destination
# activemq.memory.destination.limit
MemoryLimit:
metric: memory.destination.limit
unit: By
type: updowncounter
desc: The amount of configured memory limit for this destination
# activemq.temp.destination.utilization
TempUsagePercentUsage:
metric: temp.destination.utilization
unit: "1"
type: gauge
desc: The percentage of non-persistent storage used by this destination
# activemq.temp.destination.limit
TempUsageLimit:
metric: temp.destination.limit
unit: By
type: updowncounter
desc: The amount of configured non-persistent storage limit for this destination
# activemq.message.queue.size
QueueSize:
metric: message.queue.size
unit: "{message}"
type: updowncounter
desc: The current number of messages waiting to be consumed
# activemq.message.expired
ExpiredCount:
metric: message.expired
unit: "{message}"
type: counter
desc: The number of messages not delivered because they expired
# activemq.message.enqueued
EnqueueCount:
metric: message.enqueued
unit: "{message}"
type: counter
desc: The number of messages sent to this destination
# activemq.message.dequeued
DequeueCount:
metric: message.dequeued
unit: "{message}"
type: counter
desc: The number of messages acknowledged and removed from this destination
# activemq.message.enqueue.average_duration
AverageEnqueueTime:
metric: message.enqueue.average_duration
sourceUnit: ms
unit: s
type: gauge
desc: The average time a message was held on this destination

# Broker-level metrics
- bean: org.apache.activemq:type=Broker,brokerName=*
metricAttribute:
messaging.system: const(activemq)
activemq.broker.name: param(brokerName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider messaging.activemq.broker.name

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even messaging.brooker.name so it can be reused.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is messaging.broker.name a part of semconv?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not currently but it could be added.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to generalize activemq.broker.name by adding messaging.broker.name to semconv we need to have more than one implementation to serve as an example (this PR provides one).

From experience, it usually takes a while as name-related discussions always take time to settle, I don't think we should make this PR depend on it. We can always iterate on it later and change that once a stable semconv attribute is available.

prefix: activemq.
mapping:
# activemq.connection.count
CurrentConnectionsCount:
metric: connection.count
type: updowncounter
unit: "{connection}"
desc: The number of active connections
# activemq.memory.utilization
MemoryPercentUsage:
metric: memory.utilization
type: gauge
sourceUnit: "%"
unit: "1"
desc: The percentage of broker memory used
# activemq.memory.limit
MemoryLimit:
metric: memory.limit
type: updowncounter
unit: By
desc: The amount of configured broker memory limit
# activemq.store.utilization
StorePercentUsage:
metric: store.utilization
type: gauge
sourceUnit: "%"
unit: "1"
desc: The percentage of broker persistent storage used
# activemq.store.limit
StoreLimit:
metric: store.limit
type: updowncounter
unit: By
desc: The amount of configured broker persistent storage limit
# activemq.temp.utilization
TempPercentUsage:
metric: temp.utilization
type: gauge
sourceUnit: "%"
unit: "1"
desc: The percentage of broker non-persistent storage used
# activemq.temp.limit
TempLimit:
metric: temp.limit
type: updowncounter
unit: By
desc: The amount of configured broker non-persistent storage limit
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,26 @@ class UnitConverterTest {
})
void shouldSupportPredefined_to_s_Conversions(
Long originalValue, String originalUnit, Double expectedConvertedValue) {
// Given
String targetUnit = "s";
testConversion(originalValue, originalUnit, expectedConvertedValue, "s");
}

// When
@ParameterizedTest
@CsvSource({
"100,1.0", "99,0.99", "1,0.01", "0,0",
})
void shouldSupportPredefined_percent_Conversions(
Long originalValue, Double expectedConvertedValue) {
testConversion(originalValue, "%", expectedConvertedValue, "1");
}

private static void testConversion(
Long originalValue, String originalUnit, Double expectedConvertedValue, String targetUnit) {
UnitConverter converter = UnitConverter.getInstance(originalUnit, targetUnit);

assertThat(converter).isNotNull();
Number actualValue = converter.convert(originalValue);

// Then
assertEquals(expectedConvertedValue, actualValue);
assertThat(expectedConvertedValue).isEqualTo(actualValue);
}

@ParameterizedTest
Expand Down
Loading
Loading