Message brokers

Embed
Preview of the card
<a href="https://watchtowerops.net/docs/brokers"><img src="https://watchtowerops.net/docs/_api/public/orgs/aa5959cb-1a0b-4a9a-bc9a-913e7ac31cd9/spaces/b7aac181-9916-4805-b253-becc5fd2ca62/pages/7c5c4bed-e66f-4f32-a283-41c08113be48/social-card.svg?v=3e7ae33eec8b" alt="Message brokers" width="600" height="315"></a>

The agent reads each broker's own management or monitoring API; nothing is installed next to the broker. Credentials stay in the agent's local config file. Per-queue series are capped (200 queues per broker unless a section says otherwise) so a broker with thousands of queues stays cheap. Counters are cumulative; dashboards and alerts use rate()/increase(). Every series carries host_name (the agent's host).

RabbitMQ (rabbitmq module)

Config: url (management plugin, default http://127.0.0.1:15672), username, password, interval. Give the user the monitoring tag so nodes and all vhosts are visible; without it only queues and the overview are read.

Each round reads:

Endpoint

What becomes series

/api/queues (200 deepest, one paged call)

depth, ready, unacknowledged, consumers, memory, consumer capacity (consumer_capacity, or consumer_utilisation before 3.12), age of the head message (from its timestamp property: backlog in seconds, not just count), quorum/stream replicas and replicas offline, published/delivered/acked/redelivered counters, and rabbitmq.queue.info{type,state,leader,dead_letter_exchange} (the DLX from queue arguments or the effective policy)

/api/overview

cluster-wide message counters including confirmed, returned_unroutable and dropped_unroutable (silent data loss), connection/channel/queue churn counters, object totals; labelled cluster

/api/nodes (monitoring tag)

per node: running, memory used/limit/alarm, disk free/limit/alarm, file descriptors, sockets, Erlang processes, run queue, partitions seen

/api/consumers

per kept queue: consumers with unlimited prefetch (manual ack, prefetch 0), auto-ack consumers, single-active-consumer standbys

/api/stream/consumers (stream plugin)

per stream: consumer count and the largest offset lag

/api/shovels, /api/federation-links (plugins)

rabbitmq.shovel.up, rabbitmq.federation.link.up (0 when not running; state/status as a label)

The round fails (and wt.integration.up drops to 0) only when queues or the overview cannot be read; the other endpoints are skipped quietly when a plugin is off or the user lacks the tag.

Stock alerts: memory alarm, disk alarm, network partition, quorum replicas offline, unroutable messages dropped, shovel down, federation link down, backlog, unreachable; off by default: oldest message too old (needs publisher timestamps), queue without consumers, connection churn.

NATS and JetStream (nats module)

Config: url (monitoring port, default http://127.0.0.1:8222), optional username/password, interval.

  • /varz: connections, subscriptions, memory, CPU, slow consumers, messages and bytes in/out.

  • /jsz?accounts=true&streams=true&consumers=true (skipped when JetStream is off): JetStream memory and storage used against their limits, API requests and errors; per stream (at most 200, KV buckets and object stores included as kind=kv|object with bucket): messages, bytes, consumers, subjects, replicas not current, leader; per consumer (at most 500): pending, ack pending, redelivered, waiting pull requests, deliveries, and nats.jetstream.consumer.ack_floor_age: while work is pending, how long the ack floor has not moved (a stuck consumer in seconds, not message counts).

In a cluster each server reports the stream replicas it hosts; series differ by host_name.

Stock alerts: slow consumers, consumer stuck (ack floor age over 10 minutes), stream replica lagging, storage over 90%, unreachable; off by default: JetStream API errors.

Redis Streams (redis module)

The redis module (also Valkey, KeyDB, Dragonfly) inspects streams after INFO. streams: ["orders", ...] names the keys to read; without it the module finds stream keys itself with SCAN ... TYPE stream (5 calls of COUNT 1000 per round, the cursor carrying over, at most 100 streams remembered; a key that stops being a stream is forgotten).

Per stream: XINFO STREAM (length, entries added, groups, age of the newest entry). Per group (at most 50 per stream): XINFO GROUPS (pending, consumers, lag on Redis 7+) and lag in time: stream entry ids are millisecond times, so redis.stream.group.lag_time is the gap between the newest entry and the group's last delivered entry; with pending entries, XPENDING gives the age of the oldest unacknowledged one. Per consumer (at most 500 series): pending and idle seconds (XINFO CONSUMERS).

Stock alerts: group lagging over 5 minutes, entry pending over 15 minutes (a dead consumer holding it).

Kafka lag in time (kafka module)

Besides lag in messages, kafka.consumer_group.lag_time reports how many seconds each group is behind, per group and topic (the worst partition, same series cap as lag_sum). Each round the agent records every consumed partition's end offset with the time it read it (60 samples, at most 5000 partitions). The time at which the end offset equalled the group's committed offset is when the next unconsumed record was written: interpolated inside the history, extrapolated at the history's produce rate for an older commit. No records are fetched, so it costs nothing beyond the lag call; the estimate is good to about one collection interval, and a fresh agent needs two rounds before a lagging partition has a rate. A group with zero message lag reports 0.

Stock alert: a group over 10 minutes behind for 10 minutes.

Apache Pulsar (pulsar module)

Config: url (broker HTTP port, default http://127.0.0.1:8080), optional token (Bearer) or username/password, prefixes, interval.

  • /metrics: every pulsar_* family, including per topic and per subscription backlog, unacked messages, redelivery rate and storage (5000 series per scrape).

  • Admin API, for the 20 topics with the largest subscription backlog in that scrape: /admin/v2/<domain>/<tenant>/<namespace>/<topic>/stats?getEarliestTimeInBacklog=true gives per subscription pulsar.subscription.backlog_age (age of the oldest unconsumed message, Pulsar 2.11+), last_ack_age, consumers and blocked_on_unacked. A broker that refuses the call only loses these four series.

Stock alerts: backlog older than 15 minutes, subscription blocked on unacked messages, unreachable.

ActiveMQ classic (activemq module)

Config: url (Jolokia, default http://127.0.0.1:8161/api/jolokia), username, password, interval.

Per queue (200 deepest): size, consumers, producers, in flight, enqueued/dequeued/dispatched/expired counters, blocked sends, average and longest time in queue, memory share. Per broker: memory, store and temp usage percent (producer flow control at 100), connections, messages. Dead-letter queues (ActiveMQ.DLQ, DLQ.*) are ordinary queues; the stock alert watches their growth.

Stock alerts: store over 85%, memory limit reached, dead-letter queue growing, unreachable; off by default: queue without consumers.

ActiveMQ Artemis (artemis module)

Config: url (the console's Jolokia, default http://127.0.0.1:8161/console/jolokia), username, password (a user in a role allowed to read MBeans), interval.

Per broker: address memory used and percent of global-max-size, journal disk use against max-disk-usage, connections, messages, active/backup and replica sync. Per queue (200 deepest; labels address, queue, routing_type): messages, consumers, delivering, scheduled, added/acknowledged/expired/killed counters (killed = sent to the dead-letter address), paused, and the age of the first message (FirstMessageAge).

Stock alerts: address memory over 90%, disk over 85%, messages dead-lettered, unreachable; off by default: oldest message over 15 minutes, backup out of sync.

Mosquitto (mosquitto module)

Config: addr (an MQTT listener, default 127.0.0.1:1883), username, password, tls, interval. The agent connects with MQTT 3.1.1 (its own client, no library), subscribes to $SYS/#, takes what arrives until the stream is quiet for 500 ms (Mosquitto publishes $SYS retained, so one round needs no waiting for sys_interval) and disconnects. The user needs read access to $SYS/# in the ACL file.

Series: clients connected, offline persistent sessions, maximum, total, expired; messages received/sent/stored; PUBLISH received/sent/dropped; bytes; subscriptions; retained messages; store count and bytes; heap; uptime; and mosquitto.bridge.up{bridge} from $SYS/broker/connection/<bridge>/state.

Stock alerts: publishes dropped, bridge down, unreachable.

EMQX (emqx module)

Config: url (dashboard/API listener, default http://127.0.0.1:18083), username = API key, password = API secret, interval. EMQX 5 REST API:

  • /api/v5/stats?aggregate=true: every <x>.count as emqx.<x> and its peak as emqx.<x>.max (connections, live connections, sessions, subscriptions, topics, retained, ...).

  • /api/v5/metrics?aggregate=true: message, delivery-drop (queue full, expired, too large), client connect, auth failure, authorization deny, session and byte counters.

  • /api/v5/nodes: per node running, connections, memory, load, Erlang processes, descriptors, uptime.

  • /api/v5/alarms?activated=true: active alarms in total and per alarm name (the per-client suffix of conn_congestion/<client>/<user> is dropped so clients cannot add series).

Stock alerts: node not running, alarm active, deliveries dropped on full queues, unreachable; off by default: authentication failures.

Cloud queues (the cloud pull modules)

Managed queues come through the existing cloud metric pollers (docs/product/infra/cloud-pull.md); there is no separate module.

  • AWS SQS / SNS (aws module): add "AWS/SQS" and "AWS/SNS" to namespaces and "Maximum" to stats. amazonaws.com/AWS/SQS/ApproximateAgeOfOldestMessage (label aws.dim.QueueName) is the backlog in seconds; ApproximateNumberOfMessagesVisible and ...NotVisible are ready and in-flight; a DLQ is its own queue. SNS gives NumberOfNotificationsFailed. CloudWatch publishes SQS every minute, so set period_s: 60.

  • GCP Pub/Sub (GCP pull): presets: ["pubsub"] adds per subscription undelivered messages, cloud.gcp.pubsub.subscription.oldest_unacked_message_age (backlog in seconds), dead-lettered and expired-ack counts, acks, and per topic publish operations.

  • Azure Service Bus (Azure Monitor pull): list the namespace's ARM id in resources and ActiveMessages, DeadletteredMessages, ScheduledMessages, ServerErrors, ThrottledRequests in metrics. Service Bus exposes no age of the oldest message through Azure Monitor, so there is no lag in time for it.

Updated