tutorial

Monitoring IBM MQ with Vigilmon: Channel Health, Queue Depth & Dead-Letter Alerting

Practical guide to uptime and health monitoring for IBM MQ — HTTP health endpoints for MQ-connected services, heartbeat patterns for message consumers, dead-letter queue alerting, and channel availability checks with Vigilmon.

IBM MQ is the backbone of many enterprise messaging architectures — banks, airlines, and healthcare systems depend on it for guaranteed delivery. But MQ failures are notoriously quiet: a blocked channel, a full queue, or a dead-letter queue overflow can go undetected for hours while application logs show nothing. IBM MQ Operations Console and MQ Explorer live inside your infrastructure perimeter and can be misconfigured or unreachable during the same incidents that affect MQ itself. Vigilmon adds an independent external layer: HTTP health checks on your MQ-connected services, heartbeat monitors for message consumers, and webhook alerts when channels or queues are degraded.

This tutorial shows you how to build meaningful monitoring for IBM MQ architectures using Vigilmon.

What You'll Build

  • A health endpoint on your MQ consumer service that checks real queue manager connectivity
  • A Vigilmon HTTP monitor with appropriate timeout settings for MQ consumers
  • A heartbeat pattern for batch/scheduled MQ message processors
  • A dead-letter queue (DLQ) depth probe
  • An alerting strategy for channel failures and queue backlog spikes

Prerequisites

  • IBM MQ queue manager accessible from your application (on-premises, container, or MQ on Cloud)
  • An MQ-connected application in any runtime (Node.js, Java, Python, Go)
  • IBM MQ client libraries installed for your language
  • A free account at vigilmon.online

Step 1: Add a Health Endpoint to Your MQ Consumer Service

Your MQ consumer is typically a long-running service. Add a /health endpoint that probes real connectivity to the queue manager and checks queue depth.

Node.js (with ibmmq package)

// routes/health.js
import MQI from 'ibmmq';

const { MQC, MQCD, MQCNO, MQOD } = MQI;

export async function healthHandler(req, res) {
  const checks = {};
  let ok = true;

  let hConn;
  try {
    const cd = new MQCD();
    cd.ChannelName = process.env.MQ_CHANNEL;
    cd.ConnectionName = process.env.MQ_CONN_NAME;

    const cno = new MQCNO();
    cno.ClientConn = cd;
    cno.Options = MQC.MQCNO_CLIENT_BINDING;

    hConn = await new Promise((resolve, reject) => {
      MQI.Connx(process.env.MQ_QMGR, cno, (err, conn) => {
        if (err) reject(err);
        else resolve(conn);
      });
    });

    // Probe: open the queue for inquiry to check depth
    const od = new MQOD();
    od.ObjectName = process.env.MQ_QUEUE_NAME;
    od.ObjectType = MQC.MQOT_Q;

    const hObj = await new Promise((resolve, reject) => {
      MQI.Open(hConn, od, MQC.MQOO_INQUIRE, (err, obj) => {
        if (err) reject(err);
        else resolve(obj);
      });
    });

    const selectors = [MQC.MQIA_CURRENT_Q_DEPTH];
    const intAttrs = await new Promise((resolve, reject) => {
      MQI.Inq(hObj, selectors, 0, (err, intA) => {
        if (err) reject(err);
        else resolve(intA);
      });
    });

    const depth = intAttrs[0];
    checks.queueManager = 'ok';
    checks.queueDepth = depth;

    const warnThreshold = parseInt(process.env.MQ_DEPTH_WARN || '5000', 10);
    if (depth > warnThreshold) {
      checks.queueDepth = `backlogged (${depth})`;
      ok = false;
    }

    MQI.Close(hObj, 0, () => {});
    MQI.Disc(hConn, () => {});
  } catch (err) {
    checks.queueManager = `error: ${err.message}`;
    ok = false;
  }

  res.status(ok ? 200 : 503).json({
    status: ok ? 'ok' : 'degraded',
    service: 'mq-consumer',
    checks,
  });
}

Java (Spring Boot with IBM MQ Spring Starter)

// health/MQHealthIndicator.java
import com.ibm.mq.spring.boot.MQConfigurationProperties;
import jakarta.jms.Connection;
import jakarta.jms.ConnectionFactory;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class MQHealthIndicator implements HealthIndicator {

    private final ConnectionFactory connectionFactory;

    public MQHealthIndicator(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    @Override
    public Health health() {
        try (Connection conn = connectionFactory.createConnection()) {
            conn.start();
            return Health.up()
                    .withDetail("queueManager", "reachable")
                    .build();
        } catch (Exception e) {
            return Health.down()
                    .withDetail("queueManager", "unreachable")
                    .withDetail("error", e.getMessage())
                    .build();
        }
    }
}

Expose /actuator/health (Spring Boot default) and configure Vigilmon to assert { "status": "UP" } in the response body.

Python (pymqi)

# routers/health.py
import os
import pymqi
from fastapi import APIRouter

router = APIRouter()

@router.get("/health")
async def health():
    checks = {}
    ok = True

    qmgr = None
    try:
        conn_info = f"{os.environ['MQ_HOST']}({os.environ['MQ_PORT']})"
        qmgr = pymqi.connect(
            os.environ['MQ_QMGR'],
            os.environ['MQ_CHANNEL'],
            conn_info,
            os.environ.get('MQ_USER', ''),
            os.environ.get('MQ_PASSWORD', ''),
        )

        queue = pymqi.Queue(qmgr, os.environ['MQ_QUEUE_NAME'])
        depth = queue.inquire(pymqi.CMQC.MQIA_CURRENT_Q_DEPTH)
        queue.close()

        checks['queueManager'] = 'ok'
        checks['queueDepth'] = depth

        warn = int(os.environ.get('MQ_DEPTH_WARN', '5000'))
        if depth > warn:
            checks['queueDepth'] = f'backlogged ({depth})'
            ok = False
    except pymqi.MQMIError as e:
        checks['queueManager'] = f'error: CC={e.comp} RC={e.reason}'
        ok = False
    finally:
        if qmgr:
            qmgr.disconnect()

    status_code = 200 if ok else 503
    return {
        'status': 'ok' if ok else 'degraded',
        'service': 'mq-consumer',
        'checks': checks,
    }

Step 2: Configure the Vigilmon HTTP Monitor

  1. Log in to VigilmonAdd Monitor → HTTP.
  2. URL: your consumer service's health endpoint (e.g. https://mq-consumer.example.com/health).
  3. Check interval: 60 seconds.
  4. Response timeout: 15 seconds — MQ connection setup can be slower than REST APIs.
  5. Expected status: 200.
  6. JSON assertion: path status, expected value ok.

Tip: IBM MQ's default listener port is 1414. If your consumer service runs in a private network, expose the health endpoint through a reverse proxy (nginx, HAProxy) on a public IP while keeping port 1414 internal.


Step 3: Heartbeat Monitoring for MQ Message Processors

Scheduled batch consumers that process MQ messages in waves are invisible to HTTP monitors between runs. Use Vigilmon's Heartbeat monitor and have the consumer ping it after each successful batch.

Node.js heartbeat pattern

// worker/mq-processor.mjs
import MQI from 'ibmmq';

async function processBatch(hConn, queueName) {
  const od = new MQI.MQOD();
  od.ObjectName = queueName;

  const hObj = await openQueue(hConn, od, MQI.MQC.MQOO_INPUT_AS_Q_DEF);
  let processed = 0;

  while (true) {
    const msg = await getMessage(hObj).catch(() => null);
    if (!msg) break;
    await handleMessage(msg);
    processed++;
  }

  await closeQueue(hObj);
  return processed;
}

async function run() {
  const hConn = await connectQM();

  while (true) {
    const count = await processBatch(hConn, process.env.MQ_QUEUE_NAME);

    if (count > 0) {
      // Ping heartbeat only when messages were successfully processed
      await fetch(process.env.VIGILMON_HEARTBEAT_URL, { method: 'POST' });
      console.log(`Processed ${count} messages — heartbeat sent`);
    }

    await sleep(parseInt(process.env.POLL_INTERVAL_MS || '30000', 10));
  }
}

run().catch(console.error);

In Vigilmon, create a Heartbeat monitor:

  • Set the grace period to 3× your expected poll interval.
  • A missed heartbeat means the worker crashed, MQ connection was lost, or messages stopped flowing unexpectedly.

Step 4: Dead-Letter Queue Depth Probe

IBM MQ routes undeliverable messages to the Dead Letter Queue (SYSTEM.DEAD.LETTER.QUEUE by default). Monitor it with a lightweight scheduled probe.

# dlq_probe.py — run via cron or Lambda
import os, requests, pymqi

def probe():
    qmgr = None
    try:
        conn_info = f"{os.environ['MQ_HOST']}({os.environ['MQ_PORT']})"
        qmgr = pymqi.connect(
            os.environ['MQ_QMGR'],
            os.environ['MQ_CHANNEL'],
            conn_info,
        )
        dlq = pymqi.Queue(qmgr, 'SYSTEM.DEAD.LETTER.QUEUE')
        depth = dlq.inquire(pymqi.CMQC.MQIA_CURRENT_Q_DEPTH)
        dlq.close()

        if depth == 0:
            requests.post(os.environ['VIGILMON_DLQ_HEARTBEAT_URL'], timeout=5)
            print(f"DLQ empty — heartbeat sent")
        else:
            print(f"DLQ has {depth} messages — withholding heartbeat")
    finally:
        if qmgr:
            qmgr.disconnect()

probe()

Run this probe every 5 minutes. In Vigilmon, set the DLQ heartbeat grace period to 10 minutes. Any messages accumulating in the DLQ for more than 10 minutes will trigger an alert.


Step 5: Alerting Strategy

| Alert channel | When to use | |---|---| | Email / PagerDuty | Consumer service returns 503 (queue manager unreachable) | | Slack webhook | Heartbeat missed (processor stalled or crashed) | | Slack + Email | DLQ heartbeat missed (messages failing delivery) | | Status page | Customer-facing features backed by MQ message flows |

Configure alert escalation in Vigilmon: notify immediately on first failure, re-notify after 5 minutes if still down. MQ channel outages are typically hard failures, not transient blips.


What Vigilmon Catches That IBM MQ Console Misses

| Scenario | IBM MQ Console / Operations Console | Vigilmon | |---|---|---| | Consumer application crashes (HTTP 503) | Only visible if MQ can detect the disconnect | HTTP monitor catches immediately | | Batch processor silently stops | No built-in alerting for idle consumers | Heartbeat grace period fires alert | | DLQ accumulates failed messages | Manual inspection or MQ Explorer | DLQ probe + heartbeat monitor | | MQ Operations Console is itself down | Your alerting path is gone | Vigilmon is external — unaffected | | Network partition isolates queue manager | Console unreachable too | HTTP monitor catches from external | | Channel authentication failure | Logged locally | Health endpoint catches at service layer |


IBM MQ delivers messages reliably, but reliability requires active monitoring. A blocked channel, overflowing queue, or crashed consumer can silently break enterprise workflows. External health checks from Vigilmon give you the independent signal you need — before your SLAs are breached.

Start monitoring your IBM MQ consumers in under 5 minutes — register free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →