You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/06/08 14:07:49 UTC
(camel) branch main updated: CAMEL-23709: Reduce span verbosity for endpoint-sending processors (#23843)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new d02d1120f94b CAMEL-23709: Reduce span verbosity for endpoint-sending processors (#23843)
d02d1120f94b is described below
commit d02d1120f94bc27a7682a9850ee45abcec1830c1
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Jun 8 16:07:42 2026 +0200
CAMEL-23709: Reduce span verbosity for endpoint-sending processors (#23843)
* CAMEL-23709: Reduce span verbosity for endpoint-sending processors
Introduce EndpointSending marker interface in camel-api. Processors
that send to endpoints (to, toD, wireTap, enrich) implement it to
signal they produce their own EVENT_SENT span via ExchangeSendingEvent.
The telemetry intercept strategy skips creating redundant EVENT_PROCESS
processor spans for these processors, reducing span tree depth.
Co-Authored-By: Claude <no...@anthropic.com>
Signed-off-by: Claus Ibsen <cl...@gmail.com>
* CAMEL-23709: Add upgrade guide note for reduced span verbosity
Co-Authored-By: Claude <no...@anthropic.com>
Signed-off-by: Claus Ibsen <cl...@gmail.com>
---------
Signed-off-by: Claus Ibsen <cl...@gmail.com>
Co-authored-by: Claude <no...@anthropic.com>
---
.../camel/opentelemetry2/BaggageInjectionTest.java | 5 +--
.../camel/opentelemetry2/BaggageSettingTest.java | 5 +--
.../camel/opentelemetry2/EnableProcessorsTest.java | 7 +--
.../apache/camel/opentelemetry2/SpanBeanTest.java | 9 ++--
.../opentelemetry2/SpanCustomizationTest.java | 7 +--
.../camel/opentelemetry2/SpanInjectionTest.java | 7 +--
.../camel/opentelemetry2/SpanToBeanTest.java | 23 ++++------
.../TraceProcessorsInterceptStrategy.java | 52 +++++++++++++++-------
.../camel/telemetry/EnableCoreProcessorsTest.java | 9 +---
.../org/apache/camel/telemetry/SpanBeanTest.java | 15 +++----
.../org/apache/camel/telemetry/SpanToBeanTest.java | 27 ++++-------
.../java/org/apache/camel/EndpointSending.java | 27 +++++++++++
.../java/org/apache/camel/processor/Enricher.java | 4 +-
.../camel/processor/SendDynamicProcessor.java | 3 +-
.../org/apache/camel/processor/SendProcessor.java | 3 +-
.../apache/camel/processor/WireTapProcessor.java | 3 +-
.../ROOT/pages/camel-4x-upgrade-guide-4_21.adoc | 19 ++++++++
17 files changed, 128 insertions(+), 97 deletions(-)
diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/BaggageInjectionTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/BaggageInjectionTest.java
index 23a20c2f12a7..d7be5cba8de1 100644
--- a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/BaggageInjectionTest.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/BaggageInjectionTest.java
@@ -74,7 +74,8 @@ public class BaggageInjectionTest extends OpenTelemetryTracerTestSupport {
private void checkTrace(OtelTrace trace) {
List<SpanData> spans = trace.getSpans();
- assertEquals(8, spans.size());
+ // to("log:info") no longer produces a processor span (SendProcessor implements EndpointSending)
+ assertEquals(7, spans.size());
SpanData testProducer = spans.get(0);
SpanData direct = spans.get(1);
SpanData innerProcessor1 = spans.get(2);
@@ -82,7 +83,6 @@ public class BaggageInjectionTest extends OpenTelemetryTracerTestSupport {
SpanData innerLog = spans.get(4);
SpanData innerProcessor2 = spans.get(5);
SpanData log = spans.get(6);
- SpanData innerToLog = spans.get(7);
// Validate span completion
assertTrue(testProducer.hasEnded());
@@ -92,7 +92,6 @@ public class BaggageInjectionTest extends OpenTelemetryTracerTestSupport {
assertTrue(innerLog.hasEnded());
assertTrue(innerProcessor2.hasEnded());
assertTrue(log.hasEnded());
- assertTrue(innerToLog.hasEnded());
}
@Override
diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/BaggageSettingTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/BaggageSettingTest.java
index f953f0098564..e161a6dc3630 100644
--- a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/BaggageSettingTest.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/BaggageSettingTest.java
@@ -61,14 +61,14 @@ public class BaggageSettingTest extends OpenTelemetryTracerTestSupport {
private void checkTrace(OtelTrace trace) {
List<SpanData> spans = trace.getSpans();
- assertEquals(7, spans.size());
+ // to("log:info") no longer produces a processor span (SendProcessor implements EndpointSending)
+ assertEquals(6, spans.size());
SpanData testProducer = spans.get(0);
SpanData direct = spans.get(1);
SpanData setHeaders = spans.get(2);
SpanData innerLog = spans.get(3);
SpanData innerProcessor = spans.get(4);
SpanData log = spans.get(5);
- SpanData innerToLog = spans.get(6);
// Validate span completion
assertTrue(testProducer.hasEnded());
@@ -77,7 +77,6 @@ public class BaggageSettingTest extends OpenTelemetryTracerTestSupport {
assertTrue(innerLog.hasEnded());
assertTrue(innerProcessor.hasEnded());
assertTrue(log.hasEnded());
- assertTrue(innerToLog.hasEnded());
}
@Override
diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/EnableProcessorsTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/EnableProcessorsTest.java
index 76f4abe6868f..e1470e581b5d 100644
--- a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/EnableProcessorsTest.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/EnableProcessorsTest.java
@@ -60,13 +60,13 @@ public class EnableProcessorsTest extends OpenTelemetryTracerTestSupport {
private void checkTrace(OtelTrace trace) {
List<SpanData> spans = trace.getSpans();
- assertEquals(6, spans.size());
+ // to("log:info") no longer produces a processor span (SendProcessor implements EndpointSending)
+ assertEquals(5, spans.size());
SpanData testProducer = spans.get(0);
SpanData direct = spans.get(1);
SpanData innerLog = spans.get(2);
SpanData innerProcessor = spans.get(3);
SpanData log = spans.get(4);
- SpanData innerToLog = spans.get(5);
// Validate span completion
assertTrue(testProducer.hasEnded());
@@ -74,14 +74,12 @@ public class EnableProcessorsTest extends OpenTelemetryTracerTestSupport {
assertTrue(innerLog.hasEnded());
assertTrue(innerProcessor.hasEnded());
assertTrue(log.hasEnded());
- assertTrue(innerToLog.hasEnded());
// Validate same trace
assertEquals(testProducer.getSpanContext().getTraceId(), direct.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), innerLog.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), innerProcessor.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), log.getSpanContext().getTraceId());
- assertEquals(testProducer.getSpanContext().getTraceId(), innerToLog.getSpanContext().getTraceId());
// Validate operations
assertEquals(Op.EVENT_RECEIVED.toString(), direct.getAttributes().get(AttributeKey.stringKey("op")));
@@ -93,7 +91,6 @@ public class EnableProcessorsTest extends OpenTelemetryTracerTestSupport {
assertEquals(direct.getSpanContext().getSpanId(), innerLog.getParentSpanContext().getSpanId());
assertEquals(direct.getSpanContext().getSpanId(), innerProcessor.getParentSpanContext().getSpanId());
assertEquals(direct.getSpanContext().getSpanId(), log.getParentSpanContext().getSpanId());
- assertEquals(log.getSpanContext().getSpanId(), innerToLog.getParentSpanContext().getSpanId());
}
@Override
diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanBeanTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanBeanTest.java
index a785de244f28..5952d2aca42c 100644
--- a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanBeanTest.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanBeanTest.java
@@ -63,14 +63,14 @@ public class SpanBeanTest extends OpenTelemetryTracerTestSupport {
private void checkTrace(OtelTrace trace) {
List<SpanData> spans = trace.getSpans();
- assertEquals(7, spans.size());
+ // to("log:info") no longer produces a processor span (SendProcessor implements EndpointSending)
+ assertEquals(6, spans.size());
SpanData testProducer = spans.get(0);
SpanData direct = spans.get(1);
SpanData innerLog = spans.get(2);
SpanData beanProcessor = spans.get(3);
SpanData beanMethod = spans.get(4);
SpanData log = spans.get(5);
- SpanData innerToLog = spans.get(6);
// Validate span completion
assertTrue(testProducer.hasEnded());
@@ -79,7 +79,6 @@ public class SpanBeanTest extends OpenTelemetryTracerTestSupport {
assertTrue(beanProcessor.hasEnded());
assertTrue(beanMethod.hasEnded());
assertTrue(log.hasEnded());
- assertTrue(innerToLog.hasEnded());
// Validate same trace
assertEquals(testProducer.getSpanContext().getTraceId(), direct.getSpanContext().getTraceId());
@@ -87,7 +86,6 @@ public class SpanBeanTest extends OpenTelemetryTracerTestSupport {
assertEquals(testProducer.getSpanContext().getTraceId(), beanProcessor.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), beanMethod.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), log.getSpanContext().getTraceId());
- assertEquals(testProducer.getSpanContext().getTraceId(), innerToLog.getSpanContext().getTraceId());
// Validate operations
assertEquals(Op.EVENT_RECEIVED.toString(), direct.getAttributes().get(AttributeKey.stringKey("op")));
@@ -101,13 +99,12 @@ public class SpanBeanTest extends OpenTelemetryTracerTestSupport {
assertEquals(direct.getSpanContext().getSpanId(), beanProcessor.getParentSpanContext().getSpanId());
assertEquals(beanProcessor.getSpanContext().getSpanId(), beanMethod.getParentSpanContext().getSpanId());
assertEquals(direct.getSpanContext().getSpanId(), log.getParentSpanContext().getSpanId());
- assertEquals(log.getSpanContext().getSpanId(), innerToLog.getParentSpanContext().getSpanId());
// Validate message logging
assertEquals("A message", innerLog.getEvents().get(0).getAttributes().get(AttributeKey.stringKey("message")));
assertEquals(
"Exchange[ExchangePattern: InOnly, BodyType: String, Body: my-body]",
- innerToLog.getEvents().get(0).getAttributes().get(AttributeKey.stringKey("message")));
+ log.getEvents().get(0).getAttributes().get(AttributeKey.stringKey("message")));
}
@Override
diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanCustomizationTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanCustomizationTest.java
index 2b1d6c4d91d3..cc4d61938125 100644
--- a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanCustomizationTest.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanCustomizationTest.java
@@ -64,14 +64,14 @@ public class SpanCustomizationTest extends OpenTelemetryTracerTestSupport {
private void checkTrace(OtelTrace trace) {
List<SpanData> spans = trace.getSpans();
- assertEquals(7, spans.size());
+ // to("log:info") no longer produces a processor span (SendProcessor implements EndpointSending)
+ assertEquals(6, spans.size());
SpanData testProducer = spans.get(0);
SpanData direct = spans.get(1);
SpanData innerLog = spans.get(2);
SpanData innerProcessor = spans.get(3);
SpanData customSpan = spans.get(4);
SpanData log = spans.get(5);
- SpanData innerToLog = spans.get(6);
// Validate span completion
assertTrue(testProducer.hasEnded());
@@ -80,16 +80,13 @@ public class SpanCustomizationTest extends OpenTelemetryTracerTestSupport {
assertTrue(innerProcessor.hasEnded());
assertTrue(customSpan.hasEnded());
assertTrue(log.hasEnded());
- assertTrue(innerToLog.hasEnded());
// Validate same trace
assertEquals(testProducer.getSpanContext().getTraceId(), direct.getSpanContext().getTraceId());
- assertEquals(testProducer.getSpanContext().getTraceId(), direct.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), innerLog.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), innerProcessor.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), customSpan.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), log.getSpanContext().getTraceId());
- assertEquals(testProducer.getSpanContext().getTraceId(), innerToLog.getSpanContext().getTraceId());
// Validate operations
assertEquals(Op.EVENT_RECEIVED.toString(), direct.getAttributes().get(AttributeKey.stringKey("op")));
diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanInjectionTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanInjectionTest.java
index fb72cd03684d..80182ad54569 100644
--- a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanInjectionTest.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanInjectionTest.java
@@ -102,13 +102,13 @@ public class SpanInjectionTest extends OpenTelemetryTracerTestSupport {
private void checkTrace(OtelTrace trace, String parentTrace, String parentSpan) {
List<SpanData> spans = trace.getSpans();
- assertEquals(6, spans.size());
+ // to("log:info") no longer produces a processor span (SendProcessor implements EndpointSending)
+ assertEquals(5, spans.size());
SpanData testProducer = spans.get(0);
SpanData direct = spans.get(1);
SpanData innerLog = spans.get(2);
SpanData innerProcessor = spans.get(3);
SpanData log = spans.get(4);
- SpanData innerToLog = spans.get(5);
// Validate span completion
assertTrue(testProducer.hasEnded());
@@ -116,7 +116,6 @@ public class SpanInjectionTest extends OpenTelemetryTracerTestSupport {
assertTrue(innerLog.hasEnded());
assertTrue(innerProcessor.hasEnded());
assertTrue(log.hasEnded());
- assertTrue(innerToLog.hasEnded());
// Validate same trace
assertEquals(parentTrace, testProducer.getSpanContext().getTraceId());
@@ -124,7 +123,6 @@ public class SpanInjectionTest extends OpenTelemetryTracerTestSupport {
assertEquals(testProducer.getSpanContext().getTraceId(), innerLog.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), innerProcessor.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), log.getSpanContext().getTraceId());
- assertEquals(testProducer.getSpanContext().getTraceId(), innerToLog.getSpanContext().getTraceId());
// Validate operations
assertEquals(Op.EVENT_RECEIVED.toString(), direct.getAttributes().get(AttributeKey.stringKey("op")));
@@ -139,7 +137,6 @@ public class SpanInjectionTest extends OpenTelemetryTracerTestSupport {
assertEquals(direct.getSpanContext().getSpanId(), innerLog.getParentSpanContext().getSpanId());
assertEquals(direct.getSpanContext().getSpanId(), innerProcessor.getParentSpanContext().getSpanId());
assertEquals(direct.getSpanContext().getSpanId(), log.getParentSpanContext().getSpanId());
- assertEquals(log.getSpanContext().getSpanId(), innerToLog.getParentSpanContext().getSpanId());
}
@Override
diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanToBeanTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanToBeanTest.java
index 64f0eb2d8c1c..d138c89fd8c9 100644
--- a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanToBeanTest.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanToBeanTest.java
@@ -61,46 +61,39 @@ public class SpanToBeanTest extends OpenTelemetryTracerTestSupport {
private void checkTrace(OtelTrace trace) {
List<SpanData> spans = trace.getSpans();
- assertEquals(8, spans.size());
+ // to("bean:myBean") and to("log:info") no longer produce processor spans (SendProcessor implements EndpointSending)
+ assertEquals(6, spans.size());
SpanData testProducer = spans.get(0);
SpanData direct = spans.get(1);
SpanData innerLog = spans.get(2);
- SpanData beanTo = spans.get(3);
- SpanData beanProcessor = spans.get(4);
- SpanData beanMethod = spans.get(5);
- SpanData log = spans.get(6);
- SpanData innerToLog = spans.get(7);
+ SpanData beanProcessor = spans.get(3);
+ SpanData beanMethod = spans.get(4);
+ SpanData log = spans.get(5);
// Validate span completion
assertTrue(testProducer.hasEnded());
assertTrue(direct.hasEnded());
assertTrue(innerLog.hasEnded());
- assertTrue(beanTo.hasEnded());
assertTrue(beanProcessor.hasEnded());
assertTrue(beanMethod.hasEnded());
assertTrue(log.hasEnded());
- assertTrue(innerToLog.hasEnded());
// Validate same trace
assertEquals(testProducer.getSpanContext().getTraceId(), direct.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), innerLog.getSpanContext().getTraceId());
- assertEquals(testProducer.getSpanContext().getTraceId(), beanTo.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), beanProcessor.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), beanMethod.getSpanContext().getTraceId());
assertEquals(testProducer.getSpanContext().getTraceId(), log.getSpanContext().getTraceId());
- assertEquals(testProducer.getSpanContext().getTraceId(), innerToLog.getSpanContext().getTraceId());
// Validate hierarchy
assertFalse(testProducer.getParentSpanContext().isValid());
assertEquals(testProducer.getSpanContext().getSpanId(), direct.getParentSpanContext().getSpanId());
assertEquals(direct.getSpanContext().getSpanId(), innerLog.getParentSpanContext().getSpanId());
- assertEquals(direct.getSpanContext().getSpanId(), beanTo.getParentSpanContext().getSpanId());
- assertEquals(beanTo.getSpanContext().getSpanId(), beanProcessor.getParentSpanContext().getSpanId());
- // NOTE: the bean method belongs to the component ("to") node.
- assertEquals(beanTo.getSpanContext().getSpanId(), beanMethod.getParentSpanContext().getSpanId());
+ assertEquals(direct.getSpanContext().getSpanId(), beanProcessor.getParentSpanContext().getSpanId());
+ // beanMethod's parent is direct (OTel context set by scope wrapper, not the event span)
+ assertEquals(direct.getSpanContext().getSpanId(), beanMethod.getParentSpanContext().getSpanId());
assertEquals(direct.getSpanContext().getSpanId(), log.getParentSpanContext().getSpanId());
- assertEquals(log.getSpanContext().getSpanId(), innerToLog.getParentSpanContext().getSpanId());
}
@Override
diff --git a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/TraceProcessorsInterceptStrategy.java b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/TraceProcessorsInterceptStrategy.java
index 5369cba95192..2185881e7077 100644
--- a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/TraceProcessorsInterceptStrategy.java
+++ b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/TraceProcessorsInterceptStrategy.java
@@ -20,6 +20,8 @@ import java.util.concurrent.CompletableFuture;
import org.apache.camel.AsyncCallback;
import org.apache.camel.CamelContext;
+import org.apache.camel.DelegateProcessor;
+import org.apache.camel.EndpointSending;
import org.apache.camel.Exchange;
import org.apache.camel.NamedNode;
import org.apache.camel.Processor;
@@ -57,9 +59,7 @@ public class TraceProcessorsInterceptStrategy implements InterceptStrategy {
@Override
public void process(Exchange exchange) throws Exception {
String processorName = processorDefinition.getId() + "-" + processorDefinition.getShortName();
- if ((isCoreProcessEnabled(tracer.isDisableCoreProcessors(), processorDefinition.getShortName()) ||
- isCustomProcessEnabled(tracer.isTraceProcessors(), processorDefinition.getShortName()))
- && tracer.match(processorName, exchange.getContext())) {
+ if (shouldTrace(processorName, exchange)) {
tracer.beginProcessorSpan(exchange, processorName);
try {
processor.process(exchange);
@@ -67,7 +67,6 @@ public class TraceProcessorsInterceptStrategy implements InterceptStrategy {
tracer.endProcessorSpan(exchange, processorName);
}
} else {
- // We must always execute this
processor.process(exchange);
}
}
@@ -75,11 +74,8 @@ public class TraceProcessorsInterceptStrategy implements InterceptStrategy {
@Override
public boolean process(Exchange exchange, AsyncCallback callback) {
String processorName = processorDefinition.getId() + "-" + processorDefinition.getShortName();
- boolean isTraceProcessor
- = (isCoreProcessEnabled(tracer.isDisableCoreProcessors(), processorDefinition.getShortName()) ||
- isCustomProcessEnabled(tracer.isTraceProcessors(), processorDefinition.getShortName()))
- && tracer.match(processorName, exchange.getContext());
- if (isTraceProcessor) {
+ boolean trace = shouldTrace(processorName, exchange);
+ if (trace) {
try {
tracer.beginProcessorSpan(exchange, processorName);
} catch (Exception e) {
@@ -90,7 +86,7 @@ public class TraceProcessorsInterceptStrategy implements InterceptStrategy {
try {
callback.done(doneSync);
} finally {
- if (isTraceProcessor) {
+ if (trace) {
try {
tracer.endProcessorSpan(exchange, processorName);
} catch (Exception e) {
@@ -108,15 +104,39 @@ public class TraceProcessorsInterceptStrategy implements InterceptStrategy {
process(exchange, callback);
return callback.getFuture();
}
+
+ private boolean shouldTrace(String processorName, Exchange exchange) {
+ // skip processors that send to an endpoint (to, toD, wireTap, enrich)
+ // unwrap through any delegate chain (e.g. otel scope wrapper) to find the real processor
+ if (isEndpointSending(processor)) {
+ return false;
+ }
+ String shortName = processorDefinition.getShortName();
+ boolean enabled = isCoreProcessEnabled(shortName) || isCustomProcessEnabled(shortName);
+ return enabled && tracer.match(processorName, exchange.getContext());
+ }
+
+ private boolean isEndpointSending(Processor target) {
+ Processor p = target;
+ while (p != null) {
+ if (p instanceof EndpointSending) {
+ return true;
+ }
+ if (p instanceof DelegateProcessor dp) {
+ p = dp.getProcessor();
+ } else {
+ break;
+ }
+ }
+ return false;
+ }
}
- private boolean isCoreProcessEnabled(boolean isDisableCoreProcessors, String processDefinitionShortName) {
- // Any enabled core process which is not a "custom" processor.
- return !isDisableCoreProcessors && !processDefinitionShortName.equals("process");
+ private boolean isCoreProcessEnabled(String shortName) {
+ return !tracer.isDisableCoreProcessors() && !"process".equals(shortName);
}
- private boolean isCustomProcessEnabled(boolean isTraceProcessors, String processDefinitionShortName) {
- // Any custom core process.
- return isTraceProcessors && processDefinitionShortName.equals("process");
+ private boolean isCustomProcessEnabled(String shortName) {
+ return tracer.isTraceProcessors() && "process".equals(shortName);
}
}
diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/EnableCoreProcessorsTest.java b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/EnableCoreProcessorsTest.java
index 62c50a27899d..04b873507a30 100644
--- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/EnableCoreProcessorsTest.java
+++ b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/EnableCoreProcessorsTest.java
@@ -57,27 +57,23 @@ public class EnableCoreProcessorsTest extends ExchangeTestSupport {
private void checkTrace(MockTrace trace) {
List<Span> spans = trace.spans();
- assertEquals(5, spans.size());
- // Cast to implementation object to be able to
- // inspect the status of the Span.
+ // to("log:info") no longer produces a processor span (SendProcessor implements EndpointSending)
+ assertEquals(4, spans.size());
MockSpanAdapter testProducer = (MockSpanAdapter) spans.get(0);
MockSpanAdapter direct = (MockSpanAdapter) spans.get(1);
MockSpanAdapter innerLog = (MockSpanAdapter) spans.get(2);
MockSpanAdapter log = (MockSpanAdapter) spans.get(3);
- MockSpanAdapter innerToLog = (MockSpanAdapter) spans.get(4);
// Validate span completion
assertEquals("true", testProducer.getTag("isDone"));
assertEquals("true", direct.getTag("isDone"));
assertEquals("true", innerLog.getTag("isDone"));
assertEquals("true", log.getTag("isDone"));
- assertEquals("true", innerToLog.getTag("isDone"));
// Validate same trace
assertEquals(testProducer.getTag("traceid"), direct.getTag("traceid"));
assertEquals(testProducer.getTag("traceid"), innerLog.getTag("traceid"));
assertEquals(testProducer.getTag("traceid"), log.getTag("traceid"));
- assertEquals(testProducer.getTag("traceid"), innerToLog.getTag("traceid"));
// Validate op
assertEquals(Op.EVENT_RECEIVED.toString(), direct.getTag("op"));
@@ -87,7 +83,6 @@ public class EnableCoreProcessorsTest extends ExchangeTestSupport {
assertEquals(testProducer.getTag("spanid"), direct.getTag("parentSpan"));
assertEquals(direct.getTag("spanid"), innerLog.getTag("parentSpan"));
assertEquals(direct.getTag("spanid"), log.getTag("parentSpan"));
- assertEquals(log.getTag("spanid"), innerToLog.getTag("parentSpan"));
}
@Override
diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanBeanTest.java b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanBeanTest.java
index ed2cba75226d..0adafe69ce01 100644
--- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanBeanTest.java
+++ b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanBeanTest.java
@@ -57,16 +57,14 @@ public class SpanBeanTest extends ExchangeTestSupport {
private void checkTrace(MockTrace trace) {
List<Span> spans = trace.spans();
- assertEquals(7, spans.size());
- // Cast to implementation object to be able to
- // inspect the status of the Span.
+ // to("log:info") no longer produces a processor span (SendProcessor implements EndpointSending)
+ assertEquals(6, spans.size());
MockSpanAdapter testProducer = (MockSpanAdapter) spans.get(0);
MockSpanAdapter direct = (MockSpanAdapter) spans.get(1);
MockSpanAdapter innerLog = (MockSpanAdapter) spans.get(2);
MockSpanAdapter bean = (MockSpanAdapter) spans.get(3);
MockSpanAdapter beanMethod = (MockSpanAdapter) spans.get(4);
MockSpanAdapter log = (MockSpanAdapter) spans.get(5);
- MockSpanAdapter innerToLog = (MockSpanAdapter) spans.get(6);
// Validate span completion
assertEquals("true", testProducer.getTag("isDone"));
@@ -75,15 +73,13 @@ public class SpanBeanTest extends ExchangeTestSupport {
assertEquals("true", bean.getTag("isDone"));
assertEquals("true", beanMethod.getTag("isDone"));
assertEquals("true", log.getTag("isDone"));
- assertEquals("true", innerToLog.getTag("isDone"));
// Validate same trace
assertEquals(testProducer.getTag("traceid"), direct.getTag("traceid"));
assertEquals(testProducer.getTag("traceid"), innerLog.getTag("traceid"));
- assertEquals(testProducer.getTag("traceid"), log.getTag("traceid"));
assertEquals(testProducer.getTag("traceid"), bean.getTag("traceid"));
assertEquals(testProducer.getTag("traceid"), beanMethod.getTag("traceid"));
- assertEquals(testProducer.getTag("traceid"), innerToLog.getTag("traceid"));
+ assertEquals(testProducer.getTag("traceid"), log.getTag("traceid"));
// Validate op
assertEquals(Op.EVENT_RECEIVED.toString(), direct.getTag("op"));
@@ -92,10 +88,9 @@ public class SpanBeanTest extends ExchangeTestSupport {
assertNull(testProducer.getTag("parentSpan"));
assertEquals(testProducer.getTag("spanid"), direct.getTag("parentSpan"));
assertEquals(direct.getTag("spanid"), innerLog.getTag("parentSpan"));
- assertEquals(direct.getTag("spanid"), log.getTag("parentSpan"));
assertEquals(direct.getTag("spanid"), bean.getTag("parentSpan"));
assertEquals(bean.getTag("spanid"), beanMethod.getTag("parentSpan"));
- assertEquals(log.getTag("spanid"), innerToLog.getTag("parentSpan"));
+ assertEquals(direct.getTag("spanid"), log.getTag("parentSpan"));
// Validate operations
assertEquals(Op.EVENT_SENT.toString(), testProducer.getTag("op"));
@@ -105,7 +100,7 @@ public class SpanBeanTest extends ExchangeTestSupport {
assertEquals("A message", innerLog.logEntries().get(0).fields().get("message"));
assertEquals(
"Exchange[ExchangePattern: InOnly, BodyType: String, Body: my-body]",
- innerToLog.logEntries().get(0).fields().get("message"));
+ log.logEntries().get(0).fields().get("message"));
}
@Override
diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanToBeanTest.java b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanToBeanTest.java
index 0e4433ea4654..8debbdfb4632 100644
--- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanToBeanTest.java
+++ b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanToBeanTest.java
@@ -57,36 +57,29 @@ public class SpanToBeanTest extends ExchangeTestSupport {
private void checkTrace(MockTrace trace) {
List<Span> spans = trace.spans();
- assertEquals(8, spans.size());
- // Cast to implementation object to be able to
- // inspect the status of the Span.
+ // to("bean:myBean") and to("log:info") no longer produce processor spans (SendProcessor implements EndpointSending)
+ assertEquals(6, spans.size());
MockSpanAdapter testProducer = (MockSpanAdapter) spans.get(0);
MockSpanAdapter direct = (MockSpanAdapter) spans.get(1);
MockSpanAdapter innerLog = (MockSpanAdapter) spans.get(2);
- MockSpanAdapter toBean = (MockSpanAdapter) spans.get(3);
- MockSpanAdapter bean = (MockSpanAdapter) spans.get(4);
- MockSpanAdapter beanMethod = (MockSpanAdapter) spans.get(5);
- MockSpanAdapter log = (MockSpanAdapter) spans.get(6);
- MockSpanAdapter innerToLog = (MockSpanAdapter) spans.get(7);
+ MockSpanAdapter bean = (MockSpanAdapter) spans.get(3);
+ MockSpanAdapter beanMethod = (MockSpanAdapter) spans.get(4);
+ MockSpanAdapter log = (MockSpanAdapter) spans.get(5);
// Validate span completion
assertEquals("true", testProducer.getTag("isDone"));
assertEquals("true", direct.getTag("isDone"));
assertEquals("true", innerLog.getTag("isDone"));
- assertEquals("true", toBean.getTag("isDone"));
assertEquals("true", bean.getTag("isDone"));
assertEquals("true", beanMethod.getTag("isDone"));
assertEquals("true", log.getTag("isDone"));
- assertEquals("true", innerToLog.getTag("isDone"));
// Validate same trace
assertEquals(testProducer.getTag("traceid"), direct.getTag("traceid"));
assertEquals(testProducer.getTag("traceid"), innerLog.getTag("traceid"));
- assertEquals(testProducer.getTag("traceid"), log.getTag("traceid"));
- assertEquals(testProducer.getTag("traceid"), toBean.getTag("traceid"));
assertEquals(testProducer.getTag("traceid"), bean.getTag("traceid"));
assertEquals(testProducer.getTag("traceid"), beanMethod.getTag("traceid"));
- assertEquals(testProducer.getTag("traceid"), innerToLog.getTag("traceid"));
+ assertEquals(testProducer.getTag("traceid"), log.getTag("traceid"));
// Validate op
assertEquals(Op.EVENT_RECEIVED.toString(), direct.getTag("op"));
@@ -95,11 +88,9 @@ public class SpanToBeanTest extends ExchangeTestSupport {
assertNull(testProducer.getTag("parentSpan"));
assertEquals(testProducer.getTag("spanid"), direct.getTag("parentSpan"));
assertEquals(direct.getTag("spanid"), innerLog.getTag("parentSpan"));
- assertEquals(direct.getTag("spanid"), log.getTag("parentSpan"));
- assertEquals(direct.getTag("spanid"), toBean.getTag("parentSpan"));
- assertEquals(toBean.getTag("spanid"), bean.getTag("parentSpan"));
+ assertEquals(direct.getTag("spanid"), bean.getTag("parentSpan"));
assertEquals(bean.getTag("spanid"), beanMethod.getTag("parentSpan"));
- assertEquals(log.getTag("spanid"), innerToLog.getTag("parentSpan"));
+ assertEquals(direct.getTag("spanid"), log.getTag("parentSpan"));
// Validate operations
assertEquals(Op.EVENT_SENT.toString(), testProducer.getTag("op"));
@@ -109,7 +100,7 @@ public class SpanToBeanTest extends ExchangeTestSupport {
assertEquals("A message", innerLog.logEntries().get(0).fields().get("message"));
assertEquals(
"Exchange[ExchangePattern: InOnly, BodyType: String, Body: my-body]",
- innerToLog.logEntries().get(0).fields().get("message"));
+ log.logEntries().get(0).fields().get("message"));
}
@Override
diff --git a/core/camel-api/src/main/java/org/apache/camel/EndpointSending.java b/core/camel-api/src/main/java/org/apache/camel/EndpointSending.java
new file mode 100644
index 000000000000..e56002ed660e
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/EndpointSending.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel;
+
+/**
+ * Marker interface for {@link Processor} implementations that send an {@link org.apache.camel.Exchange} to an
+ * {@link Endpoint} as part of their processing. This applies to both static endpoints (resolved at route build time)
+ * and dynamic endpoints (computed at runtime from an expression).
+ *
+ * @since 4.21
+ */
+public interface EndpointSending {
+}
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/Enricher.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/Enricher.java
index 54dd1e7ca154..fd8a0053061f 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/Enricher.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/Enricher.java
@@ -23,6 +23,7 @@ import org.apache.camel.AsyncCallback;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.CamelExchangeException;
+import org.apache.camel.EndpointSending;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ExchangePropertyKey;
@@ -50,7 +51,8 @@ import static org.apache.camel.support.ExchangeHelper.copyResultsPreservePattern
*
* @see PollEnricher
*/
-public class Enricher extends BaseProcessorSupport implements IdAware, RouteIdAware, StepIdAware, CamelContextAware {
+public class Enricher extends BaseProcessorSupport
+ implements EndpointSending, IdAware, RouteIdAware, StepIdAware, CamelContextAware {
private CamelContext camelContext;
private String id;
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendDynamicProcessor.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendDynamicProcessor.java
index 86ff6459d115..543c8f04c730 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendDynamicProcessor.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendDynamicProcessor.java
@@ -23,6 +23,7 @@ import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.Component;
import org.apache.camel.Endpoint;
+import org.apache.camel.EndpointSending;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ExchangePropertyKey;
@@ -54,7 +55,7 @@ import org.slf4j.LoggerFactory;
* @see org.apache.camel.processor.SendProcessor
*/
public class SendDynamicProcessor extends BaseProcessorSupport
- implements IdAware, RouteIdAware, StepIdAware, CamelContextAware {
+ implements EndpointSending, IdAware, RouteIdAware, StepIdAware, CamelContextAware {
private static final Logger LOG = LoggerFactory.getLogger(SendDynamicProcessor.class);
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java
index 5745d5ec39ff..48f135653234 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java
@@ -24,6 +24,7 @@ import org.apache.camel.AsyncProducer;
import org.apache.camel.CamelContext;
import org.apache.camel.Endpoint;
import org.apache.camel.EndpointAware;
+import org.apache.camel.EndpointSending;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ExchangePropertyKey;
@@ -52,7 +53,7 @@ import org.slf4j.LoggerFactory;
* @see SendDynamicProcessor
*/
public class SendProcessor extends BaseProcessorSupport
- implements Traceable, EndpointAware, IdAware, RouteIdAware, StepIdAware {
+ implements Traceable, EndpointAware, EndpointSending, IdAware, RouteIdAware, StepIdAware {
private static final Logger LOG = LoggerFactory.getLogger(SendProcessor.class);
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/WireTapProcessor.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/WireTapProcessor.java
index bda30103fb71..67a6132fa8a9 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/WireTapProcessor.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/WireTapProcessor.java
@@ -24,6 +24,7 @@ import org.apache.camel.AsyncCallback;
import org.apache.camel.AsyncProcessor;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
+import org.apache.camel.EndpointSending;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ExchangePropertyKey;
@@ -49,7 +50,7 @@ import org.slf4j.LoggerFactory;
* Processor for wire tapping exchanges to an endpoint destination.
*/
public class WireTapProcessor extends BaseProcessorSupport
- implements Traceable, ShutdownAware, IdAware, RouteIdAware, StepIdAware, CamelContextAware {
+ implements Traceable, ShutdownAware, EndpointSending, IdAware, RouteIdAware, StepIdAware, CamelContextAware {
private static final Logger LOG = LoggerFactory.getLogger(WireTapProcessor.class);
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
index 346a9aa8e2ad..b02c23dcc37a 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
@@ -485,6 +485,25 @@ extend from:
- `AbstractHttpSpanDecorator` (returns `CLIENT` for `EVENT_SENT`, `SERVER` for `EVENT_RECEIVED`)
- `AbstractMessagingSpanDecorator` (returns `PRODUCER` for `EVENT_SENT`, `CONSUMER` for `EVENT_RECEIVED`)
+==== Reduced span verbosity for endpoint-sending processors
+
+Processors that send to an endpoint (`to`, `toD`, `wireTap`, `enrich`) no longer emit a redundant
+processor span (`EVENT_PROCESS`) wrapping the endpoint event span (`EVENT_SENT`). Previously these
+EIPs produced two spans in a parent-child relationship — the processor span added no value because
+the event span already captures the destination, timing, and status.
+
+A new `org.apache.camel.EndpointSending` marker interface in `camel-api` identifies processors that
+will produce their own endpoint span via `ExchangeSendingEvent`. The telemetry intercept strategy
+skips creating a processor span for these processors.
+
+This reduces span tree depth and total span count for routes that use `to`, `toD`, `wireTap`, or
+`enrich`. Non-send EIPs (`log`, `bean`, `split`, `aggregate`, etc.) continue to produce processor
+spans as before.
+
+If you have custom telemetry code that relies on the processor span existing as a parent of the
+endpoint span for `to`, `toD`, `wireTap`, or `enrich`, you will need to adjust your span hierarchy
+expectations.
+
=== camel-opentelemetry2
In order to prevent a potential leak when running asynchronous components we need to rethink the implementation details of `camel-opentelemetry2` and remove the `Scope` wrapping that, when asynchronous, was opening the `Scope` in a thread and closing in another (what we had called "dirty" context). We are now removing this wrapping and moving this part exclusively in the custom Camel `Processors`. Here Camel will take care to open the Opentelemetry scope and close it within the same thread.