Camel with Micrometer: Route Metrics, Tracing & Observability

Production Camel deployments need visibility into route throughput, processing times, error rates, and in-flight exchanges. Apache Camel's camel-micrometer component automatically instruments every route with Micrometer metrics. Add camel-opentelemetry for distributed tracing and you get end-to-end observability from the first consumer to the last producer: all exposed via Prometheus and visualised in Grafana.

Contents

Dependencies & Auto-Configuration

Add the following to your pom.xml (Maven) or build.gradle (Gradle). Spring Boot manages compatible versions automatically when you use the Spring Boot BOM.

XML
<!-- camel-micrometer — auto-instruments all routes -->
<dependency>
  <groupId>org.apache.camel.springboot</groupId>
  <artifactId>camel-micrometer-starter</artifactId>
</dependency>

<!-- Actuator + Prometheus registry -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

<!-- OpenTelemetry tracing -->
<dependency>
  <groupId>org.apache.camel.springboot</groupId>
  <artifactId>camel-opentelemetry-starter</artifactId>
</dependency>
YAML
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,prometheus,camel
  metrics:
    tags:
      application: ${spring.application.name}

camel:
  springboot:
    name: order-integration-service

Auto-Collected Route Metrics

Once camel-micrometer-starter is on the classpath, every route is automatically instrumented. No code changes required.

MetricTypeDescription
camel.exchanges.totalCounterTotal exchanges processed per route
camel.exchanges.succeeded.totalCounterSuccessful exchanges per route
camel.exchanges.failed.totalCounterFailed exchanges per route
camel.exchanges.inflightGaugeCurrently in-flight exchanges
camel.exchange.event.notifierTimerExchange processing time per route
camel.route.policyTimerRoute execution time (requires RoutePolicyFactory)
Shell
# Verify auto-collected metrics at the Prometheus endpoint
curl http://localhost:8080/actuator/prometheus | grep camel_exchanges
# camel_exchanges_total{camelContext="order-integration-service",routeId="route1",...} 42.0
# camel_exchanges_succeeded_total{...} 40.0
# camel_exchanges_failed_total{...} 2.0

Custom Counters in Routes

The class below shows the implementation. Key points are highlighted in the inline comments.

Java
@Component
public class OrderRoute extends RouteBuilder {

    @Override
    public void configure() {
        from("kafka:order.created")
            .routeId("order-processor")
            .choice()
                .when(simple("${body.totalAmount} > 1000"))
                    // Increment a custom counter for high-value orders
                    .process(exchange -> {
                        MeterRegistry registry = exchange.getContext()
                            .getRegistry().findByType(MeterRegistry.class)
                            .iterator().next();
                        Counter.builder("order.high.value")
                            .tag("currency", exchange.getIn().getHeader("currency", String.class))
                            .description("High-value orders processed")
                            .register(registry)
                            .increment();
                    })
                    .to("direct:processHighValueOrder")
                .otherwise()
                    .to("direct:processStandardOrder");
    }
}

Custom Timers

The class below shows the implementation. Key points are highlighted in the inline comments.

Java
@Component
public class EnrichmentRoute extends RouteBuilder {

    @Autowired
    private MeterRegistry meterRegistry;

    @Override
    public void configure() {
        Timer enrichmentTimer = Timer.builder("camel.order.enrichment.duration")
            .description("Time to enrich an order from external service")
            .register(meterRegistry);

        from("direct:enrichOrder")
            .process(exchange -> {
                Timer.Sample sample = Timer.start(meterRegistry);
                try {
                    // Perform enrichment
                    Order enriched = enrichmentService.enrich(
                        exchange.getIn().getBody(Order.class));
                    exchange.getIn().setBody(enriched);
                } finally {
                    sample.stop(enrichmentTimer);
                }
            });
    }
}

Micrometer DSL in Routes

The micrometer DSL lets you increment counters and record timers directly in the route definition without writing processor boilerplate:

Java
from("direct:processPayment")
    .routeId("payment-processor")
    // Increment a counter for every payment attempt
    .to("micrometer:counter:payment.attempts?increment=1")

    .to("direct:callPaymentGateway")

    .choice()
        .when(header("paymentStatus").isEqualTo("SUCCESS"))
            // Counter with a tag from a header
            .to("micrometer:counter:payment.results?increment=1&tags=result=success")
        .otherwise()
            .to("micrometer:counter:payment.results?increment=1&tags=result=failure")
    .end()

    // Record processing time as a distribution summary
    .to("micrometer:summary:payment.amount?value=${body.amount}");

Prometheus & Actuator Exposure

The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.

YAML
# Prometheus scrape config
scrape_configs:
  - job_name: camel-integration
    static_configs:
      - targets: ["order-service:8080"]
    metrics_path: /actuator/prometheus
    scrape_interval: 15s
YAML
# Prometheus alert — high failure rate on a Camel route
groups:
  - name: camel-routes
    rules:
      - alert: CamelRouteHighFailureRate
        expr: |
          rate(camel_exchanges_failed_total[5m])
          /
          rate(camel_exchanges_total[5m]) > 0.05
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Camel route {{ $labels.routeId }} failure rate above 5%"

      - alert: CamelRouteExchangeBacklog
        expr: camel_exchanges_inflight > 500
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Camel route {{ $labels.routeId }} has {{ $value }} in-flight exchanges"

Key Grafana Queries

The PromQL queries below can be pasted directly into Grafana or the Prometheus expression browser.

PromQL
# Exchange throughput per route (exchanges per second)
rate(camel_exchanges_total{application="order-service"}[1m])

# Error rate per route
rate(camel_exchanges_failed_total[5m])
/
rate(camel_exchanges_total[5m])

# Average processing time per route (seconds)
rate(camel_exchange_event_notifier_seconds_sum[5m])
/
rate(camel_exchange_event_notifier_seconds_count[5m])

# In-flight exchanges (current backlog)
camel_exchanges_inflight{application="order-service"}

# Custom metric — high-value order rate
rate(order_high_value_total[1m])

OpenTelemetry Distributed Tracing

Add camel-opentelemetry-starter to automatically propagate trace context across all Camel endpoints: HTTP, Kafka, database, and between routes.

YAML
# application.yml — OpenTelemetry config
management:
  tracing:
    sampling:
      probability: 1.0    # sample 100% in dev; use 0.1 in production

spring:
  application:
    name: order-integration-service

# Send traces to Zipkin or Grafana Tempo
otel:
  exporter:
    otlp:
      endpoint: http://tempo:4317
Java
// Camel automatically creates spans for each route and propagates
// W3C traceparent headers across HTTP calls. No code changes needed.

// Optionally add custom span attributes in a processor:
from("direct:enrichOrder")
    .process(exchange -> {
        Span span = Span.current();
        span.setAttribute("order.id", exchange.getIn().getHeader("orderId", String.class));
        span.setAttribute("order.amount", exchange.getIn().getBody(Order.class).getAmount().toString());
    })
    .to("http:enrichment-service/api/enrich");

JMX & Camel Management

The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.

YAML
# application.yml — enable JMX for Camel management
camel:
  springboot:
    jmx-enabled: true
Java
// Programmatic route management via CamelContext
@Service
public class RouteManagementService {

    private final CamelContext camelContext;

    // Suspend a route (stop consuming, in-flight exchanges complete)
    public void suspendRoute(String routeId) throws Exception {
        camelContext.getRouteController().suspendRoute(routeId);
    }

    // Resume a suspended route
    public void resumeRoute(String routeId) throws Exception {
        camelContext.getRouteController().resumeRoute(routeId);
    }

    // Get route statistics
    public ManagedRouteMBean getRouteStats(String routeId) throws Exception {
        return camelContext.getExtension(ManagedCamelContext.class)
            .getManagedRoute(routeId, ManagedRouteMBean.class);
    }
}