tutorial

How to Monitor Apache Camel Health and Route Availability with Vigilmon

Camel route failures and dead letter queue backlogs silently stall your integration pipelines. Learn how to monitor Camel route health, detect stalled consumers, and track integration application liveness with Vigilmon HTTP probes and heartbeat monitors.

Apache Camel is the enterprise integration framework that wires together REST APIs, message queues, databases, FTP servers, and hundreds of other systems through a uniform routing DSL — but when a route's consumer loses its broker connection, a processor throws an unhandled exception, or a dead letter channel fills up silently, your integration pipeline stops moving data without any visible error. Unlike web services, Camel route failures often manifest as missing records downstream, not as HTTP 5xx errors, making external monitoring essential.

Vigilmon gives you external visibility into Camel-powered integration services through HTTP probe monitoring (via Camel's built-in health check API or Jolokia) and heartbeat monitoring for your route processing loops. This tutorial covers both.


Why Camel Needs External Monitoring

Camel's built-in tooling (Hawtio, JMX, Actuator endpoints for Spring Boot) provides rich internal metrics — but only if someone is watching. External monitoring with Vigilmon adds:

  • Proactive alerting when the Camel application becomes unreachable (early sign of OOM crash or stuck thread pool)
  • Route health detection via Camel's Health Check API that aggregates component, consumer, and producer readiness
  • Heartbeat monitoring so you know immediately when a route stops successfully processing and completing exchanges
  • Dead letter queue detection by surfacing Camel's inflight and failed exchange counters to Vigilmon via a custom endpoint

These layers work together: the JVM can be alive while a route is suspended due to a failed consumer reconnect, and all routes can show as STARTED while a critical processor is silently catching and swallowing exceptions into a dead letter channel.


Step 1: Build a Camel Health Endpoint

Camel provides health check infrastructure out of the box. The approach depends on whether you're running Spring Boot, Quarkus, or standalone Camel.

Spring Boot + Camel (camel-spring-boot-starter)

Add the health check dependency:

<!-- pom.xml -->
<dependency>
    <groupId>org.apache.camel.springboot</groupId>
    <artifactId>camel-health-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Configure the health endpoint:

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health, info
  endpoint:
    health:
      show-details: always
camel:
  health:
    enabled: true
    routes-enabled: true
    consumers-enabled: true

Camel then exposes /actuator/health with route-level detail. Add a custom check for dead letter queue depth:

// DeadLetterHealthIndicator.java
@Component
public class DeadLetterHealthIndicator extends AbstractHealthIndicator {

    @Autowired
    private CamelContext camelContext;

    @Override
    protected void doHealthCheck(Health.Builder builder) {
        ManagedRouteMBean dlqRoute = (ManagedRouteMBean) camelContext
            .getManagementStrategy()
            .getManagementObjectStrategy()
            .getManagedObjectForRoute(camelContext, camelContext.getRoute("dead-letter-handler"));

        long failedExchanges = dlqRoute != null ? dlqRoute.getExchangesFailed() : 0;

        if (failedExchanges > 100) {
            builder.down()
                .withDetail("dead_letter_count", failedExchanges)
                .withDetail("reason", "dlq_threshold_exceeded");
        } else {
            builder.up()
                .withDetail("dead_letter_count", failedExchanges);
        }
    }
}

Custom Health Endpoint (Standalone Camel)

// CamelHealthController.java
@RestController
public class CamelHealthController {

    @Autowired
    private CamelContext camelContext;

    @GetMapping("/health/camel")
    public ResponseEntity<Map<String, Object>> checkHealth() {
        Map<String, Object> status = new LinkedHashMap<>();
        try {
            // Check overall Camel context state
            ServiceStatus ctxStatus = camelContext.getStatus();
            if (ctxStatus != ServiceStatus.Started) {
                status.put("status", "down");
                status.put("camel_status", ctxStatus.name());
                return ResponseEntity.status(503).body(status);
            }

            // Check for stopped or suspended routes
            List<String> problemRoutes = camelContext.getRoutes().stream()
                .filter(r -> {
                    ServiceStatus rs = camelContext.getRouteStatus(r.getId());
                    return rs != ServiceStatus.Started;
                })
                .map(Route::getId)
                .collect(Collectors.toList());

            if (!problemRoutes.isEmpty()) {
                status.put("status", "degraded");
                status.put("stopped_routes", problemRoutes);
                return ResponseEntity.status(503).body(status);
            }

            status.put("status", "ok");
            status.put("routes_started", camelContext.getRoutes().size());
            return ResponseEntity.ok(status);

        } catch (Exception e) {
            status.put("status", "down");
            status.put("error", e.getMessage());
            return ResponseEntity.status(503).body(status);
        }
    }
}

Python Health Sidecar (via Camel JMX/Jolokia REST)

# camel_health.py
from flask import Flask, jsonify
import requests
import os

app = Flask(__name__)

JOLOKIA_URL = os.environ.get('JOLOKIA_URL', 'http://localhost:8778/jolokia')

@app.route('/health/camel')
def camel_health():
    try:
        # Read CamelContext status via Jolokia
        resp = requests.post(JOLOKIA_URL, json={
            'type': 'read',
            'mbean': 'org.apache.camel:context=*,type=context,name=*',
            'attribute': ['State', 'ExchangesTotal', 'ExchangesFailed']
        }, timeout=5)
        data = resp.json()

        if data.get('status') != 200:
            return jsonify({'status': 'down', 'reason': 'jolokia_error'}), 503

        ctx = data.get('value', {})
        # Flatten if multiple contexts
        if isinstance(ctx, dict):
            ctx = list(ctx.values())[0] if ctx else {}

        state = ctx.get('State', 'UNKNOWN')
        failed = ctx.get('ExchangesFailed', 0)

        if state != 'Started':
            return jsonify({'status': 'down', 'camel_state': state}), 503
        if failed > 1000:
            return jsonify({'status': 'degraded', 'exchanges_failed': failed}), 503

        return jsonify({
            'status': 'ok',
            'camel_state': state,
            'exchanges_total': ctx.get('ExchangesTotal'),
            'exchanges_failed': failed,
        }), 200

    except Exception as e:
        return jsonify({'status': 'down', 'error': str(e)}), 503

if __name__ == '__main__':
    app.run(port=8093)

Step 2: Configure Vigilmon HTTP Monitor for Camel

  1. Log in to vigilmon.online and go to Monitors → New Monitor
  2. Choose HTTP / HTTPS
  3. Set the URL to your Camel health endpoint: https://your-app.example.com/health/camel (or /actuator/health for Spring Boot)
  4. Set the check interval to 1 minute
  5. Under Expected response, configure:
    • Status code: 200
    • Response body contains: "status":"ok" (or "status":"UP" for Spring Actuator)
    • Response time threshold: 5000ms
  6. Under Alert channels, assign your Slack or PagerDuty channel
  7. Save the monitor

Add a second monitor for route-level detail if you use Spring Actuator:

  • URL: https://your-app.example.com/actuator/health/camel
  • Expected: 200, body contains "status":"UP"
  • Interval: 2 minutes
  • Alert channel: on-call channel

Vigilmon's multi-region probe consensus prevents false positives from transient network blips between probe nodes and your integration service.


Step 3: Heartbeat Monitoring for Camel Route Processing

Route health checks are necessary — but not sufficient. A Camel route can be STARTED while it is processing zero messages per hour due to a backed-up upstream queue, a stalled timer trigger, or a consumer that reconnected but is not receiving messages. Heartbeat monitoring catches these silent throughput failures.

Vigilmon heartbeat monitors detect silent processing stalls: your route pings Vigilmon after successfully completing each exchange batch. If pings stop, Vigilmon fires an alert.

Set Up the Heartbeat Monitor

  1. In Vigilmon, go to Monitors → New Monitor → Heartbeat
  2. Set the name: camel-integration-route
  3. Set the expected interval: 5 minutes (adjust to your message volume)
  4. Set the grace period: 15 minutes
  5. Save — copy the heartbeat URL, e.g. https://vigilmon.online/heartbeat/abc123xyz

Wire It Into Your Camel Route (Java DSL)

// IntegrationRouteBuilder.java
@Component
public class IntegrationRouteBuilder extends RouteBuilder {

    @Value("${vigilmon.heartbeat.url}")
    private String heartbeatUrl;

    @Override
    public void configure() {
        from("activemq:queue:orders")
            .routeId("order-processing")
            .process(this::processOrder)
            .to("jpa:OrderEntity")
            .process(exchange -> {
                // Ping Vigilmon after each successful exchange
                pingVigilmon();
            })
            .log("Processed order ${header.orderId}");
    }

    private void processOrder(Exchange exchange) {
        // Business logic here
    }

    private final java.net.http.HttpClient httpClient =
        java.net.http.HttpClient.newHttpClient();

    private void pingVigilmon() {
        try {
            httpClient.sendAsync(
                java.net.http.HttpRequest.newBuilder()
                    .uri(java.net.URI.create(heartbeatUrl))
                    .GET().build(),
                java.net.http.HttpResponse.BodyHandlers.discarding()
            );
        } catch (Exception e) {
            // Non-fatal: don't let heartbeat failure interrupt route processing
        }
    }
}

Camel Route with Throttled Heartbeat (Java DSL)

For high-volume routes, ping Vigilmon on a timer rather than per-message:

// TimedHeartbeatRouteBuilder.java
@Component
public class TimedHeartbeatRouteBuilder extends RouteBuilder {

    @Value("${vigilmon.heartbeat.url}")
    private String heartbeatUrl;

    @Override
    public void configure() {
        // Heartbeat route fires every 4 minutes if the main route is alive
        from("timer:vigilmon-heartbeat?period=240000")
            .routeId("vigilmon-heartbeat")
            .to(heartbeatUrl + "?httpMethod=GET")
            .log("Vigilmon heartbeat sent");

        // Main processing route
        from("file:{{inbound.directory}}?delete=true")
            .routeId("file-processor")
            .unmarshal().csv()
            .split(body())
            .process(this::processRecord)
            .to("direct:output");
    }
}

Python Camel Client with Heartbeat

# camel_worker.py
import requests
import os
import time

HEARTBEAT_URL = os.environ['VIGILMON_HEARTBEAT_URL']
HEARTBEAT_INTERVAL = 60  # seconds

last_heartbeat = time.time()
messages_processed = 0

for message in consume_from_queue():
    process_message(message)
    messages_processed += 1

    if time.time() - last_heartbeat > HEARTBEAT_INTERVAL:
        try:
            requests.get(HEARTBEAT_URL, timeout=5)
        except Exception:
            pass
        last_heartbeat = time.time()

print(f"Processed {messages_processed} messages")

Step 4: Alert Routing for Camel Failures

Camel failures cascade through integration chains: a consumer losing its broker connection causes that route to suspend, which causes upstream producers to back up, which causes upstream services to time out waiting for acknowledgements, which surfaces as cascading failures across all systems in the integration mesh. Alert routing should reflect this cascade order.

Configure alert routing in Vigilmon:

| Monitor | Alert Channel | Priority | |---|---|---| | Camel health /health/camel | Slack + PagerDuty | P1 | | Spring Actuator /actuator/health/camel | Slack | P2 | | Heartbeat: order processing route | Slack + email | P2 | | Heartbeat: batch file integration job | Email | P3 |

Set response time thresholds for early warning:

  • Alert at 3000ms for the health endpoint (slow route status aggregation signals JMX pressure)
  • Alert at 8000ms for Actuator health (slow health detail signals GC pressure or thread pool exhaustion)

For integration-critical routes, enable two consecutive failures before alerting — Camel's automatic consumer reconnect on broker restart can briefly cause route status to show as SUSPENDED before the reconnect succeeds.


Summary

Camel route failures are invisible at the business logic layer. External monitoring catches consumer disconnections, route suspensions, dead letter queue buildups, and processing stalls before they become integration outages:

| Monitor Type | What It Covers | |---|---| | HTTP monitor on /health/camel | Camel context state, route health, failed exchange count | | HTTP monitor on Spring Actuator | Component readiness, consumer connectivity | | Heartbeat monitor | Route liveness, message processing throughput |

Get started free at vigilmon.online — your first Camel monitor is running in under two minutes.

Monitor your app with Vigilmon

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

Start free →