Most Camel estates we review are observable in the narrow sense: there are logs, and someone knows which server to tail. Then a partner asks why order 88213 took four hours, the message crossed three routes, a JMS queue, a Kafka topic and a REST call, and the answer takes two engineers a full afternoon of grepping. That is the gap tracing closes. Metrics close a different one: whether the estate is healthy right now, before anyone asks.
Camel 4 has decent answers for both. They are not the same answer, and wiring both badly is a common way to spend money on telemetry nobody reads.
Tracing: camel-opentelemetry, and the part everyone gets wrong
Add the starter and Camel instruments each route: a span per exchange, child spans per processing step, endpoint URIs as span attributes.
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-opentelemetry-starter</artifactId>
</dependency>
The easy part is the spans inside one JVM. The part that decides whether tracing is useful is context propagation across the broker. A trace that stops at to("jms:queue:orders") and restarts as an unrelated trace in the consumer is two traces, and two traces answer nothing. Camel's OpenTelemetry component injects and extracts W3C traceparent as message headers for the components that support header propagation — JMS and Kafka included — but only if the headers survive the trip:
- JMS silently drops headers with names containing hyphens or dots under the JMS 1.1 identifier rules. Camel's JMS binding maps them, but a hand-rolled
MessageConverteror a third-party producer in the middle will not. Verify with a real end-to-end message, not a unit test. - Kafka carries them as record headers. Fine — unless a legacy producer writes the topic without them, in which case the consumer-side span becomes a root span and you have a forest, not a trace.
- File and FTP hops carry no headers at all. If a flow lands a file and another route picks it up, the trace ends there unless you propagate the trace id inside the payload or the filename, which is usually not worth it. Accept the break and name it in your runbook.
Sample deliberately. Head-based sampling at 100% on a route doing 5,000 messages a second will cost more than the integration. Percentage sampling plus an always-sample rule for errors gets you the traces you actually open.
Metrics: camel-micrometer and the four numbers that matter
Tracing is for one message. Metrics are for the estate. camel-micrometer exposes per-route timers and counters — exchanges completed, exchanges failed, processing time, inflight exchanges — and ships them to whatever registry you already run.
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-micrometer-starter</artifactId>
</dependency>
Most teams then build a dashboard with forty panels and alert on none of them. The short list we recommend alerting on:
- Failure rate per route. Not total errors — rate, per route, compared to that route's own baseline. A route that always fails 0.2% and now fails 3% is the signal.
- Inflight exchanges. The cleanest early warning of a downstream slowdown. It rises before latency alerts fire and long before the queue depth alarm.
- Dead letter channel arrivals. Any message reaching the DLQ is, by definition, one your error handler gave up on. Alert on the first one, not on a threshold. If that is too noisy, your redelivery policy is wrong, not your alert.
- Consumer lag / queue depth, from the broker rather than from Camel. Camel cannot tell you about the messages it has not received yet.
Add one business-level metric per critical flow — files processed before the 6 a.m. cutoff, invoices posted per hour — because "all routes green" and "the business got its data" are different claims, and only the second one matters to the person who calls you.
Cardinality is the bill
The fastest way to a surprising observability invoice is a tag whose value is unbounded: order id, customer id, filename, correlation id. Tag by route id, endpoint, and outcome. Put the high-cardinality identifiers in span attributes and structured logs, where you look them up one at a time, and keep them out of metric labels, where every distinct value is a new time series forever.
Logs still earn their place
Drop the trace id into the MDC and log structurally, and the three pillars connect: an alert on a failure rate points at a route, the route's error logs carry trace ids, the trace shows the hop that broke. That chain — alert to route to trace to hop — is the entire point. If your telemetry cannot walk it, adding more exporters will not help.
What this does not solve
Observability tells you what happened. It does not tell you what should have happened, and it will not reconcile a shadow output against a legacy one during a migration — that is a comparison harness, a different tool. It also will not rescue a route whose error handling never distinguished retryable from poisoned messages; you will simply watch the same failure in higher resolution.
Wire tracing for the cross-system questions, metrics for the health questions, keep cardinality honest, and connect the two with a trace id in your logs. That is a weekend of work on most estates and it retires an entire category of afternoon-long investigations.