Solace PubSub+ is the backbone of many enterprise integration platforms — routing millions of events per second across financial systems, logistics pipelines, and IoT infrastructure. But when a message VPN degrades, a queue backs up beyond its high-water mark, or an HA pair loses synchronization, your event mesh silently stalls while downstream consumers wait.
Unlike HTTP services, Solace failures don't surface as obvious 500 errors. Queue depth climbs, throughput drops, and consumer group lag spikes — often for minutes before anyone notices. Vigilmon gives you external visibility into Solace health through HTTP probe monitoring against the Solace SEMP (Solace Element Management Protocol) REST API and heartbeat monitors for your consumer applications.
Why Solace Needs External Monitoring
Solace ships with PubSub+ Monitor and the Solace CLI for internal metrics, but these require you to be watching. External monitoring with Vigilmon adds:
- Proactive alerting when message VPN health endpoints return non-200 or report degraded state
- Queue depth threshold monitoring before queues hit their maximum message count
- HA pair status checks so you know immediately if the standby broker loses sync
- Heartbeat monitoring for consumer applications that must prove they are actively processing
- Multi-region probe consensus to avoid false alerts from transient network blips
Step 1: Enable the Solace SEMP REST API
Solace exposes broker health and statistics through its SEMP v2 REST API. Enable it on your Solace PS+ software broker or hardware appliance:
# Verify SEMP is enabled (Solace CLI)
solace> show service semp
# SEMP v2 is available at:
# http://<broker-host>:8080/SEMP/v2/monitor/
# https://<broker-host>:943/SEMP/v2/monitor/
Create a read-only SEMP user for monitoring:
solace> configure
solace(configure)> username vigilmon-monitor password <strong-password>
solace(configure)> username vigilmon-monitor global-access-level read-only
solace(configure)> end
Test the SEMP API
# Get message VPN health
curl -u vigilmon-monitor:<password> \
"http://localhost:8080/SEMP/v2/monitor/msgVpns/default" \
| jq '.data.enabled, .data.operationalState'
# Get all queues in a VPN
curl -u vigilmon-monitor:<password> \
"http://localhost:8080/SEMP/v2/monitor/msgVpns/default/queues?select=queueName,msgCount,maxMsgCount,bindCount" \
| jq '.data[]'
Step 2: Build a Solace Health Endpoint
SEMP responses contain rich data but need interpretation before Vigilmon can probe them. Build a lightweight health sidecar that queries SEMP and surfaces a clean /health endpoint:
# solace_health.py
from flask import Flask, jsonify
import requests, os
app = Flask(__name__)
SEMP_BASE = os.environ.get('SOLACE_SEMP_URL', 'http://localhost:8080/SEMP/v2/monitor')
SEMP_USER = os.environ.get('SOLACE_SEMP_USER', 'admin')
SEMP_PASS = os.environ.get('SOLACE_SEMP_PASS', 'admin')
VPN_NAME = os.environ.get('SOLACE_VPN', 'default')
QUEUE_FILL_THRESHOLD = float(os.environ.get('SOLACE_QUEUE_FILL_THRESHOLD', '0.80'))
def semp_get(path):
r = requests.get(f"{SEMP_BASE}{path}",
auth=(SEMP_USER, SEMP_PASS), timeout=5)
r.raise_for_status()
return r.json()
@app.route('/health/solace')
def health():
issues = []
# Check message VPN operational state
vpn = semp_get(f'/msgVpns/{VPN_NAME}')['data']
if not vpn.get('enabled'):
issues.append('message_vpn_disabled')
if vpn.get('state') != 'Up':
issues.append(f"vpn_state_{vpn.get('state', 'unknown')}")
# Check queue depth across all queues in the VPN
queues = semp_get(f'/msgVpns/{VPN_NAME}/queues'
'?select=queueName,msgCount,maxMsgCount,bindCount')['data']
for q in queues:
max_msgs = q.get('maxMsgCount', 0)
if max_msgs > 0:
fill = q.get('msgCount', 0) / max_msgs
if fill >= QUEUE_FILL_THRESHOLD:
issues.append(f"queue_{q['queueName']}_fill_{int(fill*100)}pct")
if q.get('bindCount', 0) == 0:
issues.append(f"queue_{q['queueName']}_no_consumers")
if issues:
return jsonify(status='degraded', issues=issues), 503
return jsonify(
status='ok',
vpn=VPN_NAME,
queue_count=len(queues),
active_consumers=sum(q.get('bindCount', 0) for q in queues),
), 200
@app.route('/health/solace/throughput')
def throughput():
vpn = semp_get(f'/msgVpns/{VPN_NAME}')['data']
return jsonify(
status='ok',
msgs_rx_per_sec=vpn.get('averageRxMsgRate', 0),
msgs_tx_per_sec=vpn.get('averageTxMsgRate', 0),
bytes_rx_per_sec=vpn.get('averageRxByteRate', 0),
bytes_tx_per_sec=vpn.get('averageTxByteRate', 0),
), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3005)
HA Pair Status Endpoint
For high-availability Solace deployments, add a dedicated HA check:
@app.route('/health/solace/ha')
def ha_status():
try:
# Solace HA status via SEMP
redundancy = semp_get('/about/user/msgVpns') # placeholder path
# Hardware appliances: check /redundancy
ha = semp_get('/redundancy') if '/redundancy' in SEMP_BASE else {}
ha_data = ha.get('data', {})
mode = ha_data.get('redundancyMode', 'NonRedundant')
active_standby = ha_data.get('activeStandbyRole', 'Active')
if mode != 'NonRedundant' and ha_data.get('configSyncState') not in ('Up', None):
return jsonify(
status='degraded',
reason='ha_config_sync_down',
role=active_standby,
), 503
return jsonify(status='ok', mode=mode, role=active_standby), 200
except Exception as e:
return jsonify(status='unknown', error=str(e)), 200
# Run the health sidecar
pip install flask requests
SOLACE_SEMP_URL=http://your-broker:8080/SEMP/v2/monitor \
SOLACE_SEMP_USER=vigilmon-monitor \
SOLACE_SEMP_PASS=your-password \
SOLACE_VPN=production \
python solace_health.py
Step 3: Enable Prometheus Stats Endpoint
Solace PubSub+ can expose Prometheus-format metrics natively (software broker version 9.6+):
# Enable Prometheus telemetry on port 9087
solace> configure
solace(configure)> telemetry
solace(configure/telemetry)> receiver-acl-profile default
solace(configure/telemetry)> no shutdown
solace(configure/telemetry)> end
Your Prometheus endpoint will be at http://<broker>:9087/. To monitor it with Vigilmon, probe a Prometheus-to-HTTP adapter:
@app.route('/health/solace/prometheus')
def prometheus_health():
r = requests.get('http://localhost:9087/', timeout=5)
if r.status_code == 200 and 'solace_' in r.text:
return jsonify(status='ok', metrics_available=True), 200
return jsonify(status='down', reason='prometheus_endpoint_unavailable'), 503
Step 4: Configure Vigilmon HTTP Monitors
- Log in to vigilmon.online and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- Set the URL to your Solace health endpoint:
https://your-app.example.com/health/solace - Set the check interval to 1 minute
- Under Expected response, configure:
- Status code:
200 - Response body contains:
"status":"ok" - Response time threshold:
3000ms
- Status code:
- Under Alert channels, assign your Slack or PagerDuty integration
- Save the monitor
Add additional monitors for granular coverage:
| Monitor URL | Purpose | Interval |
|---|---|---|
| /health/solace | VPN health, queue depth, consumer bind | 1 min |
| /health/solace/throughput | Message and byte rate trends | 1 min |
| /health/solace/ha | HA pair sync status | 1 min |
| /health/solace/prometheus | Prometheus metrics availability | 5 min |
Step 5: Heartbeat Monitoring for Solace Consumer Applications
Solace broker health is only half the picture — a consumer application can silently stall while the broker reports healthy. Vigilmon heartbeat monitors detect silent consumer failures: your application pings Vigilmon after each successful message batch, and if pings stop, Vigilmon fires an alert.
Set Up the Heartbeat Monitor
- In Vigilmon, go to Monitors → New Monitor → Heartbeat
- Set the name:
solace-order-consumer - Set the expected interval: 5 minutes
- Set the grace period: 10 minutes
- Save — copy the heartbeat URL:
https://vigilmon.online/heartbeat/abc123xyz
Wire It Into Your Consumer (Java — Solace JCSMP)
// SolaceConsumer.java
import com.solacesystems.jcsmp.*;
import java.net.http.*;
import java.net.URI;
import java.util.concurrent.atomic.AtomicLong;
public class SolaceConsumer {
private static final String VIGILMON_HEARTBEAT =
System.getenv("VIGILMON_HEARTBEAT_URL");
private static final long HEARTBEAT_EVERY_N = 100;
private static AtomicLong msgCount = new AtomicLong(0);
private static final HttpClient http = HttpClient.newHttpClient();
public static void main(String[] args) throws Exception {
JCSMPProperties props = new JCSMPProperties();
props.setProperty(JCSMPProperties.HOST, System.getenv("SOLACE_HOST"));
props.setProperty(JCSMPProperties.VPN_NAME, System.getenv("SOLACE_VPN"));
props.setProperty(JCSMPProperties.USERNAME, System.getenv("SOLACE_USER"));
props.setProperty(JCSMPProperties.PASSWORD, System.getenv("SOLACE_PASS"));
JCSMPSession session = JCSMPFactory.onlyInstance().createSession(props);
session.connect();
ConsumerFlowProperties flowProps = new ConsumerFlowProperties();
flowProps.setEndpoint(
JCSMPFactory.onlyInstance().createQueue(System.getenv("SOLACE_QUEUE"))
);
FlowReceiver receiver = session.createFlow(
(BytesXMLMessage msg, JCSMPException ex) -> {
if (ex != null) return;
processMessage(msg);
msg.ackMessage();
if (msgCount.incrementAndGet() % HEARTBEAT_EVERY_N == 0) {
pingVigilmon();
}
},
flowProps
);
receiver.start();
// Keep-alive
Thread.currentThread().join();
}
private static void pingVigilmon() {
try {
http.send(
HttpRequest.newBuilder(URI.create(VIGILMON_HEARTBEAT)).GET().build(),
HttpResponse.BodyHandlers.discarding()
);
} catch (Exception ignored) {}
}
private static void processMessage(BytesXMLMessage msg) {
// your processing logic
}
}
Python Consumer (solace-pubsubplus)
# consumer.py
import solace.messaging.messaging_service as mms
import requests, os, time
VIGILMON_URL = os.environ['VIGILMON_HEARTBEAT_URL']
last_heartbeat = time.time()
HEARTBEAT_INTERVAL = 60 # seconds
messaging_service = (mms.MessagingService.builder()
.from_properties({
"solace.messaging.transport.host": os.environ["SOLACE_HOST"],
"solace.messaging.service.vpn-name": os.environ["SOLACE_VPN"],
"solace.messaging.authentication.basic.username": os.environ["SOLACE_USER"],
"solace.messaging.authentication.basic.password": os.environ["SOLACE_PASS"],
})
.build())
messaging_service.connect()
def message_handler(message):
global last_heartbeat
process(message)
if time.time() - last_heartbeat > HEARTBEAT_INTERVAL:
try:
requests.get(VIGILMON_URL, timeout=3)
except Exception:
pass
last_heartbeat = time.time()
persistent_receiver = (messaging_service
.create_persistent_message_receiver_builder()
.build(solace.messaging.resources.queue.Queue.durable_non_exclusive_queue(
os.environ["SOLACE_QUEUE"]
)))
persistent_receiver.start()
persistent_receiver.receive_async(message_handler)
Step 6: Alert Routing
Configure alert channels to match Solace failure severity:
| Monitor | Alert Channel | Priority |
|---|---|---|
| VPN health /health/solace | Slack + PagerDuty | P1 |
| HA pair /health/solace/ha | Slack + PagerDuty | P1 |
| Queue depth spike | Slack | P2 |
| Heartbeat: consumer app | Slack + email | P2 |
| Throughput drop | Email | P3 |
Set response time thresholds for early warning:
- Alert at
2000msfor SEMP health queries (slowness signals broker load pressure) - Alert at
5000msfor HA status (HA sync operations can be heavy during active replication)
Summary
Solace PubSub+ failures are silent: VPN degradation and queue backpressure accumulate before dashboards catch up. External monitoring with Vigilmon closes this gap:
| Monitor Type | What It Covers |
|---|---|
| HTTP monitor on /health/solace | VPN health, queue depth, consumer bind counts |
| HTTP monitor on /health/solace/ha | HA pair synchronization state |
| HTTP monitor on /health/solace/throughput | Message and byte rate trends |
| Heartbeat monitor | Consumer application liveness and processing health |
Get started free at vigilmon.online — your first Solace monitor is running in under two minutes.