CubeAPM
CubeAPM CubeAPM

How to Monitor ActiveMQ with Prometheus 

How to Monitor ActiveMQ with Prometheus 

Table of Contents

Connecting Apache ActiveMQ to Prometheus requires different tooling depending on which broker you run. Apache ActiveMQ Classic (latest stable 6.2.5) has no native Prometheus endpoint. 

Apache Artemis (latest 2.53.0, now an independent Apache Top-Level Project at artemis.apache.org) requires a separate plugin that must be installed alongside the broker. Neither broker exposes Prometheus metrics out of the box. This guide covers the complete setup for both.

Key Takeaways

  • Apache ActiveMQ Classic requires the JMX Prometheus Exporter agent (v1.3.0, May 2025), a JAR that runs inside the broker JVM and converts JMX MBeans to Prometheus metrics at a configurable port
  • Apache Artemis does not ship a Prometheus plugin in its standard distribution. You must download artemis-prometheus-metrics-plugin from github.com/rh-messaging/artemis-prometheus-metrics-plugin, copy the JAR to lib/ and metrics.war to web/, and configure both broker.xml and bootstrap.xml
  • For Classic, a YAML mapping file controls which JMX MBeans become Prometheus metrics. Without explicit rules, the output is unusable raw JMX data
  • Artemis JVM memory metrics are enabled by default. JVM GC and thread metrics are disabled by default and must be explicitly enabled in broker.xml
  • Use deriv() on gauge metrics (QueueSize, memory percentages) in alert expressions. Use rate() on counter metrics (EnqueueCount, DequeueCount). Using rate() on a gauge produces unreliable results
  • Scrape interval for Classic should be 30 seconds on deployments with many destinations. JMX attribute resolution is synchronous and adds load at each scrape

How Each Broker Exposes Metrics to Prometheus

Apache ActiveMQ (Classic)Apache Artemis
What you installjmx_prometheus_javaagent-1.3.0.jar + YAML mapping fileartemis-prometheus-metrics-plugin.jar (to lib/) + metrics.war (to web/)
Configuration fileYAML rules file, loaded at startup via -javaagent flagbroker.xml plugin block + bootstrap.xml web app entry
Metrics endpointYour chosen port (e.g., 9404) at /metricsPort 8161 at /metrics
Metric namingControlled by your YAML rulesartemis_ prefix, Micrometer-managed
JVM metricsCaptured via JVM MBeans in the JMX exporterMemory: on by default. GC and threads: must be enabled

Step 1A: Install the JMX Exporter for Apache ActiveMQ Classic

Download the agent JAR

wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/1.3.0/jmx_prometheus_javaagent-1.3.0.jar \

  -O /opt/activemq/jmx_prometheus_javaagent-1.3.0.jar

Create the YAML mapping file

The mapping file tells the exporter which JMX MBean attributes to collect and what Prometheus metric names to assign. Without it, the exporter outputs everything in a raw, unlabeled format. Save this as /opt/activemq/activemq-prometheus.yml:

lowercaseOutputName: true

lowercaseOutputLabelNames: true

rules:

  # Broker-level metrics

  - pattern: 'org.apache.activemq<type=Broker, brokerName=([^,]+)><>(TotalEnqueueCount|TotalDequeueCount|TotalConnectionsCount|TotalConsumerCount|TotalProducerCount|MemoryPercentUsage|StorePercentUsage|TempPercentUsage|TotalMessageCount)'

    name: activemq_broker_$2

    labels:

      broker: "$1"

    attrNameSnakeCase: true

  # Per-queue metrics

  - pattern: 'org.apache.activemq<type=Broker, brokerName=([^,]+), destinationType=Queue, destinationName=([^,]+)><>(QueueSize|ConsumerCount|ProducerCount|EnqueueCount|DequeueCount|ExpiredCount|InFlightCount|AverageEnqueueTime|MemoryPercentUsage)'

    name: activemq_queue_$3

    labels:

      broker: "$1"

      destination: "$2"

    attrNameSnakeCase: true

  # Per-topic metrics

  - pattern: 'org.apache.activemq<type=Broker, brokerName=([^,]+), destinationType=Topic, destinationName=([^,]+)><>(QueueSize|ConsumerCount|ProducerCount|EnqueueCount|DequeueCount|ExpiredCount|InFlightCount|AverageEnqueueTime|MemoryPercentUsage)'

    name: activemq_topic_$3

    labels:

      broker: "$1"

      destination: "$2"

    attrNameSnakeCase: true

  # JVM heap memory

  - pattern: 'java.lang<type=Memory><HeapMemoryUsage>(used|committed|max)'

    name: jvm_heap_$1_bytes

    attrNameSnakeCase: true

  # JVM garbage collection

  - pattern: 'java.lang<type=GarbageCollector, name=([^,]+)><>(CollectionCount|CollectionTime)'

    name: jvm_gc_$2

    labels:

      gc: "$1"

    attrNameSnakeCase: true

Register the agent in ActiveMQ’s startup config

Edit <activemq-install>/bin/env and append to ACTIVEMQ_OPTS:

ACTIVEMQ_OPTS="$ACTIVEMQ_OPTS \

  -javaagent:/opt/activemq/jmx_prometheus_javaagent-1.3.0.jar=9404:/opt/activemq/activemq-prometheus.yml"

Restart ActiveMQ:

./bin/activemq restart

Verify metrics are being served:

curl -s http://localhost:9404/metrics | grep activemq_broker

Expected output:

activemq_broker_memory_percent_usage{broker=”localhost”} 12.0

activemq_broker_store_percent_usage{broker=”localhost”} 4.0

activemq_broker_total_consumer_count{broker=”localhost”} 3.0

If the endpoint responds but returns no activemq_ metrics, the MBean patterns in your YAML do not match the actual object names on your broker. Open the web console at http://localhost:8161, navigate to the MBeans tab, and verify exact object name formats before adjusting your patterns.

Docker Compose for Classic

services:

  activemq:

    image: apache/activemq-classic:latest

    ports:

      - "61616:61616"

      - "8161:8161"

      - "9404:9404"

    volumes:

      - ./jmx_prometheus_javaagent-1.3.0.jar:/opt/apache-activemq/bin/jmx_prometheus_javaagent-1.3.0.jar

      - ./activemq-prometheus.yml:/opt/apache-activemq/conf/activemq-prometheus.yml

    environment:

      ACTIVEMQ_OPTS: "-javaagent:/opt/apache-activemq/bin/jmx_prometheus_javaagent-1.3.0.jar=9404:/opt/apache-activemq/conf/activemq-prometheus.yml"

Step 1B: Install the Prometheus Plugin for Apache Artemis

The Prometheus plugin is not included in the standard Artemis distribution. The setup involves three files across two configuration locations.

Download the plugin files

curl -L -o artemis-prometheus-metrics-plugin-3.1.0.jar \

https://github.com/rh-messaging/artemis-prometheus-metrics-plugin/releases/download/v3.1.0/artemis-prometheus-metrics-plugin-3.1.0.jar


curl -L -o metrics.war \

https://github.com/rh-messaging/artemis-prometheus-metrics-plugin/releases/download/v3.1.0/metrics.war

Copy both files to the broker instance. Create web/ if it does not exist:

cp artemis-prometheus-metrics-plugin-3.1.0.jar <ARTEMIS_INSTANCE>/lib/

mkdir -p <ARTEMIS_INSTANCE>/web

cp metrics.war <ARTEMIS_INSTANCE>/web/

Edit broker.xml

Add the <metrics> block as a direct child of <core> in <artemis-instance>/etc/broker.xml. GC and thread metrics are off by default – enable them explicitly:

<metrics>

  <jvm-memory>true</jvm-memory>

  <jvm-gc>true</jvm-gc>

  <jvm-threads>true</jvm-threads>

  <plugin class-name="org.apache.activemq.artemis.core.server.metrics.plugins.ArtemisPrometheusMetricsPlugin"/>

</metrics>

Edit bootstrap.xml

Register the metrics web app inside the <web> element in <artemis-instance>/etc/bootstrap.xml:

<web path="web">

  <binding uri="http://0.0.0.0:8161">

    <app url="activemq-branding" war="activemq-branding.war"/>

    <app url="artemis-plugin" war="artemis-plugin.war"/>

    <app url="console" war="console.war"/>

    <app url="metrics" war="metrics.war"/>

  </binding>

</web>

Restart the broker:

./bin/artemis-service restart

Verify metrics are being served:

curl -s http://localhost:8161/metrics | grep artemis_message_count

Expected output:

artemis_message_count{address=”orders”,broker=”0.0.0.0″,queue=”orders”} 0.0

artemis_consumer_count{address=”orders”,broker=”0.0.0.0″,queue=”orders”} 1.0

Step 2: Configure Prometheus to Scrape

Classic (port 9404)

global:

  scrape_interval: 15s

  evaluation_interval: 15s

rule_files:

  - "activemq-alerts.yml"

alerting:

  alertmanagers:

    - static_configs:

        - targets:

            - alertmanager:9093

scrape_configs:

  - job_name: activemq-classic

    scrape_interval: 30s

    scrape_timeout: 25s

    static_configs:

      - targets:

          - activemq-host1:9404

          - activemq-host2:9404

        labels:

          env: production

Artemis (port 8161)

scrape_configs:

  - job_name: activemq-artemis

    scrape_interval: 15s

    metrics_path: /metrics

    static_configs:

      - targets:

          - artemis-host1:8161

          - artemis-host2:8161

        labels:

          env: production

Kubernetes with Prometheus Operator

For Classic, use a PodMonitor targeting the sidecar container port:

apiVersion: monitoring.coreos.com/v1

kind: PodMonitor

metadata:

  name: activemq-classic

  namespace: monitoring

spec:

  selector:

    matchLabels:

      app: activemq

  podMetricsEndpoints:

    - port: jmx-metrics

      interval: 30s

      scrapeTimeout: 25s

For Artemis, use a ServiceMonitor targeting port 8161:

apiVersion: monitoring.coreos.com/v1

kind: ServiceMonitor

metadata:

  name: activemq-artemis

  namespace: monitoring

spec:

  selector:

    matchLabels:

      app: artemis

  endpoints:

    - port: web

      interval: 15s

      path: /metrics

Step 3: Verify and Query Metrics

After Prometheus starts scraping, confirm data is present at http://prometheus:9090 before writing alert rules.

For Classic:

# Check broker is visible

activemq_broker_memory_percent_usage


# Confirm per-queue metrics have destination labels

activemq_queue_queue_size


# Enqueue rate

rate(activemq_queue_enqueue_count[5m])

For Artemis:

# Confirm queue metrics with address and queue labels

artemis_message_count


# Broker disk store usage

artemis_disk_store_usage


# Address memory usage percentage

artemis_address_memory_usage_percentage


# Message add rate

rate(artemis_messages_added_total[5m])

If targets show UP but metrics are missing, check the YAML patterns (Classic) or confirm the plugin JAR is in lib/ and bootstrap.xml was saved correctly (Artemis).

Step 4: Alert Rules

groups:

  - name: activemq-classic

    rules:

      - alert: ActiveMQQueueDepthGrowing

        expr: deriv(activemq_queue_queue_size[10m]) > 0

        for: 10m

        labels:

          severity: warning

        annotations:

          summary: "ActiveMQ queue depth is growing"

          description: >

            Queue {{ $labels.destination }} on {{ $labels.broker }}

            has been growing for 10 consecutive minutes.

      - alert: ActiveMQQueueNoConsumers

        expr: activemq_queue_consumer_count == 0

        for: 2m

        labels:

          severity: critical

        annotations:

          summary: "ActiveMQ queue has no consumers"

          description: >

            Queue {{ $labels.destination }} on {{ $labels.broker }}

            has zero consumers.

      - alert: ActiveMQMemoryHigh

        expr: activemq_broker_memory_percent_usage > 70

        for: 5m

        labels:

          severity: warning

        annotations:

          summary: "ActiveMQ broker memory above 70%"

          description: >

            Broker {{ $labels.broker }} memory is at {{ $value }}%.

            Producer Flow Control activates at 100%.

      - alert: ActiveMQStoreHigh

        expr: activemq_broker_store_percent_usage > 70

        for: 5m

        labels:

          severity: warning

        annotations:

          summary: "ActiveMQ persistent store above 70%"

          description: >

            Broker {{ $labels.broker }} store is at {{ $value }}%.

      - alert: ActiveMQJVMHeapHigh

        expr: jvm_heap_used_bytes / jvm_heap_max_bytes * 100 > 80

        for: 5m

        labels:

          severity: warning

        annotations:

          summary: "ActiveMQ JVM heap above 80%"

          description: >

            Instance {{ $labels.instance }} heap is at {{ $value | humanize }}%.

  - name: activemq-artemis

    rules:

      - alert: ArtemisQueueDepthGrowing

        expr: deriv(artemis_message_count[10m]) > 0

        for: 10m

        labels:

          severity: warning

        annotations:

          summary: "Artemis queue depth is growing"

          description: >

            Queue {{ $labels.queue }} on address {{ $labels.address }}

            has been growing for 10 consecutive minutes.

      - alert: ArtemisQueueNoConsumers

        expr: artemis_consumer_count == 0

        for: 2m

        labels:

          severity: critical

        annotations:

          summary: "Artemis queue has no consumers"

          description: >

            Queue {{ $labels.queue }} on address {{ $labels.address }}

            has zero consumers.

      - alert: ArtemisAddressMemoryHigh

        expr: artemis_address_memory_usage_percentage > 70

        for: 5m

        labels:

          severity: warning

        annotations:

          summary: "Artemis address memory above 70%"

          description: >

            Broker {{ $labels.broker }} address memory is at {{ $value }}%.

      - alert: ArtemisUnroutedMessages

        expr: deriv(artemis_unrouted_message_count{address=~".+"}[5m]) > 0

        for: 5m

        labels:

          severity: warning

        annotations:

          summary: "Artemis has unrouted messages"

          description: >

            Address {{ $labels.address }} on broker {{ $labels.broker }}

            is receiving messages but has no queues bound to it.

Common Setup Problems

ProblemLikely causeFix
Open http://localhost:8161, browse MBeans, and compare object name formats against your patternsVerify the path in bin/env, restart, and check the startup log for the javaagentClassic: endpoint returns nothingThe
Verify the path in bin/env, restart, and check startup log for the javaagentYAML patterns do not match actual MBean object namesAgent JAR path in ACTIVEMQ_OPTS is wrong, or ActiveMQ was not restarted
Classic: Prometheus scrape timeoutsClassic: endpoint responds, but no activemq_ metricsIncrease scrape_timeout to 25s and scrape_interval to 30s. Narrow YAML patterns to only needed attributes
Artemis: metrics endpoint 404metrics.war missing from bootstrap.xml or web/ directoryAdd <app url=”metrics” war=”metrics.war”/> to bootstrap.xml and confirm metrics.war exists in web/
Artemis: no metrics after broker.xml change<metrics> block outside <core>, or plugin JAR missing from lib/Confirm <metrics> is a direct child of <core>. Confirm the JAR is in the broker’s lib/ directory
Artemis: GC and thread metrics absentDisabled by defaultAdd <jvm-gc>true</jvm-gc> and <jvm-threads>true</jvm-threads> inside the <metrics> block
Both: Prometheus shows target DOWNFirewall blocking scrape portOpen port 9404 (Classic) or 8161 (Artemis) from the Prometheus host

Connecting Broker Metrics to Application Traces

A working Prometheus setup gives you visibility into the broker. It tells you the queue depth is climbing, memory is under pressure, or a consumer has disappeared. What it does not give you is a path from the broker symptom to the root cause in your application.

When an alert fires and you check the dashboard, the next question is invariably: is this a throughput problem (not enough consumer instances), a processing problem (each message takes too long), or a dependency problem (the consumer is waiting on a slow downstream call)? Those three causes look identical on a queue depth chart.

CubeAPM instruments your consumer application via OpenTelemetry and correlates application traces directly with broker metrics. When ActiveMQQueueDepthGrowing fires, you can jump from the Prometheus alert into CubeAPM and see exactly which consumer instance is falling behind, the per-message processing time broken down by span, and which downstream service call inside the consumer is responsible. The setup works the same whether your broker is ActiveMQ Classic, Artemis, RabbitMQ, or Kafka. CubeAPM runs self-hosted inside your own infrastructure at $0.15/GB ingestion pricing.

Summary

The difference between monitoring ActiveMQ Classic and Artemis with Prometheus is entirely in the installation step. Classic requires the JMX Prometheus Exporter agent and a YAML mapping file. Artemis requires the separately downloaded artemis-prometheus-metrics-plugin JAR and metrics.war, plus two config file edits. Once metrics are flowing, Prometheus scrapes both on the same cadence, and the same alert rule patterns apply to both.

Your chosen port (e.g., 9404) at /metricsClassicArtemis
InstallAdd javaagent to ACTIVEMQ_OPTS in bin/envCopy JAR to lib/, WAR to web/, edit broker.xml + bootstrap.xml
Metrics portYour chosen port (e.g. 9404) at /metricsPort 8161 at /metrics
Scrape interval30 seconds15 seconds
Verifycurl localhost:9404/metricscurl localhost:8161/metrics
Queue depth metricactivemq_queue_queue_sizeartemis_message_count
Consumer count metricactivemq_queue_consumer_countartemis_consumer_count
Broker memory metricactivemq_broker_memory_percent_usageartemis_address_memory_usage_percentage
Alert for gaugesderiv()deriv()
Alert for countersrate()rate()

Disclaimer: Configuration syntax, metric names, and plugin details are verified against Apache ActiveMQ Classic 6.2.5 documentation (activemq.apache.org), Apache Artemis 2.53.0 documentation (artemis.apache.org/components/artemis/documentation/latest/metrics.html), and the artemis-prometheus-metrics-plugin GitHub repository (github.com/rh-messaging/artemis-prometheus-metrics-plugin) as of May 2026.

Also read:

What Are the Key ActiveMQ Metrics to Monitor for Performance? 

RabbitMQ Alerts: How to Alert on RabbitMQ Queue Depth and Consumer Count 

How to Build a RabbitMQ Grafana Dashboard From Scratch 

×
×