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 10ae2cedb594 CAMEL-23312: Add SpanKind support to OpenTelemetry2
component (#22653)
10ae2cedb594 is described below
commit 10ae2cedb59480c109a42956ec3c8f497a829455
Author: JiriOndrusek <[email protected]>
AuthorDate: Thu Apr 30 16:00:20 2026 +0200
CAMEL-23312: Add SpanKind support to OpenTelemetry2 component (#22653)
---
.../MicrometerObservabilityTracer.java | 2 +-
.../camel/opentelemetry2/OpenTelemetryTracer.java | 7 +-
.../apache/camel/opentelemetry2/SpanKindTest.java | 155 +++++++++++++++++++++
.../opentelemetry2/mock/MockHttpComponent.java} | 25 ++--
.../opentelemetry2/mock/MockHttpEndpoint.java} | 25 ++--
.../opentelemetry2/mock/MockHttpProducer.java} | 23 ++-
.../mock/MockHttpSpanDecorator.java} | 23 ++-
.../opentelemetry2/mock/MockKafkaComponent.java} | 25 ++--
.../opentelemetry2/mock/MockKafkaEndpoint.java} | 32 ++---
.../opentelemetry2/mock/MockKafkaProducer.java} | 38 +++--
.../mock/MockKafkaSpanDecorator.java} | 24 ++--
.../org.apache.camel.telemetry.SpanDecorator | 21 +++
.../camel/telemetrydev/TelemetryDevTracer.java | 2 +-
.../camel-telemetry/src/main/docs/telemetry.adoc | 2 +-
.../org/apache/camel/telemetry/SpanDecorator.java | 1 +
.../camel/telemetry/SpanLifecycleManager.java | 2 +-
.../java/org/apache/camel/telemetry/Tracer.java | 8 +-
.../decorators/AbstractHttpSpanDecorator.java | 12 ++
.../decorators/AbstractMessagingSpanDecorator.java | 11 ++
.../decorators/AbstractSpanDecorator.java | 5 +
.../apache/camel/telemetry/mock/MockTracer.java | 2 +-
.../ROOT/pages/camel-4x-upgrade-guide-4_21.adoc | 34 +++++
22 files changed, 359 insertions(+), 120 deletions(-)
diff --git
a/components/camel-micrometer-observability/src/main/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracer.java
b/components/camel-micrometer-observability/src/main/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracer.java
index 4de010e86553..1244b976b279 100644
---
a/components/camel-micrometer-observability/src/main/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracer.java
+++
b/components/camel-micrometer-observability/src/main/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracer.java
@@ -126,7 +126,7 @@ public class MicrometerObservabilityTracer extends
org.apache.camel.telemetry.Tr
}
@Override
- public Span create(String spanName, Span parent,
SpanContextPropagationExtractor extractor) {
+ public Span create(String spanName, String spanKind, Span parent,
SpanContextPropagationExtractor extractor) {
io.micrometer.tracing.Span span;
if (parent != null) {
MicrometerObservabilitySpanAdapter microObsParentSpan =
(MicrometerObservabilitySpanAdapter) parent;
diff --git
a/components/camel-opentelemetry2/src/main/java/org/apache/camel/opentelemetry2/OpenTelemetryTracer.java
b/components/camel-opentelemetry2/src/main/java/org/apache/camel/opentelemetry2/OpenTelemetryTracer.java
index b1a3ed47daa7..257e5afd250b 100644
---
a/components/camel-opentelemetry2/src/main/java/org/apache/camel/opentelemetry2/OpenTelemetryTracer.java
+++
b/components/camel-opentelemetry2/src/main/java/org/apache/camel/opentelemetry2/OpenTelemetryTracer.java
@@ -22,6 +22,7 @@ import java.util.Map;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.ContextPropagators;
@@ -101,7 +102,7 @@ public class OpenTelemetryTracer extends
org.apache.camel.telemetry.Tracer {
}
@Override
- public Span create(String spanName, Span parent,
SpanContextPropagationExtractor extractor) {
+ public Span create(String spanName, String spanKind, Span parent,
SpanContextPropagationExtractor extractor) {
SpanBuilder builder = tracer.spanBuilder(spanName);
Baggage baggage = Baggage.current();
@@ -143,6 +144,10 @@ public class OpenTelemetryTracer extends
org.apache.camel.telemetry.Tracer {
}
baggage = getBaggageFromHeaders(baggage, extractor);
+ if (spanKind != null) {
+ builder.setSpanKind(SpanKind.valueOf(spanKind));
+ }
+
return new OpenTelemetrySpanAdapter(builder.startSpan(), baggage);
}
diff --git
a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanKindTest.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanKindTest.java
new file mode 100644
index 000000000000..41e0d021d6d5
--- /dev/null
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanKindTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.opentelemetry2;
+
+import java.util.List;
+import java.util.Map;
+
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.sdk.trace.data.SpanData;
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.opentelemetry2.CamelOpenTelemetryExtension.OtelTrace;
+import org.apache.camel.opentelemetry2.mock.MockHttpComponent;
+import org.apache.camel.opentelemetry2.mock.MockKafkaComponent;
+import org.apache.camel.telemetry.Op;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test that verifies SpanKind is set correctly for different component types.
+ */
+public class SpanKindTest extends OpenTelemetryTracerTestSupport {
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ OpenTelemetryTracer tst = new OpenTelemetryTracer();
+
tst.setTracer(otelExtension.getOpenTelemetry().getTracer("spanKindTest"));
+
tst.setContextPropagators(otelExtension.getOpenTelemetry().getPropagators());
+ CamelContext context = super.createCamelContext();
+
+ // Register mock HTTP component for testing
+ context.addComponent("mock-http", new MockHttpComponent());
+ // Register mock Kafka component for testing
+ context.addComponent("mock-kafka", new MockKafkaComponent());
+
+ CamelContextAware.trySetCamelContext(tst, context);
+ tst.init(context);
+ return context;
+ }
+
+ @Test
+ void testDirectComponentHasInternalSpanKind() {
+ template.sendBody("direct:start", "test");
+
+ List<OtelTrace> traces =
List.copyOf(otelExtension.getTraces().values());
+ assertEquals(1, traces.size());
+
+ List<SpanData> spans = traces.get(0).getSpans();
+
+ // Find the direct:start EVENT_SENT span
+ SpanData directSentSpan = getSpan(spans, "direct://start",
Op.EVENT_SENT);
+ assertEquals(SpanKind.INTERNAL, directSentSpan.getKind(),
+ "direct:start EVENT_SENT should have INTERNAL SpanKind");
+
+ // Find the direct:start EVENT_RECEIVED span
+ SpanData directReceivedSpan = getSpan(spans, "direct://start",
Op.EVENT_RECEIVED);
+ assertEquals(SpanKind.INTERNAL, directReceivedSpan.getKind(),
+ "direct:start EVENT_RECEIVED should have INTERNAL SpanKind");
+ }
+
+ @Test
+ void testHttpComponentHasClientServerSpanKind() throws Exception {
+ MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+ mockEndpoint.expectedMessageCount(1);
+
+ template.sendBody("direct:httpClient", "test message");
+
+ mockEndpoint.assertIsSatisfied();
+
+ List<OtelTrace> traces =
List.copyOf(otelExtension.getTraces().values());
+ assertEquals(1, traces.size());
+
+ List<SpanData> spans = traces.get(0).getSpans();
+
+ // Find the mock-http EVENT_SENT span (client side)
+ SpanData httpClientSpan = getSpan(spans, "mock-http://testEndpoint",
Op.EVENT_SENT);
+ assertEquals(SpanKind.CLIENT, httpClientSpan.getKind(),
+ "HTTP EVENT_SENT should have CLIENT SpanKind");
+ }
+
+ @Test
+ void testKafkaComponentHasProducerSpanKindAndInheritedProperties() throws
Exception {
+ MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+ mockEndpoint.expectedMessageCount(1);
+
+ // Send with Kafka headers that would normally be set before/during
sending
+ template.sendBodyAndHeaders("direct:kafkaProducer", "test message",
+ Map.of("kafka.KEY", "test-key",
+ "kafka.PARTITION", 0,
+ "kafka.OFFSET", "12345"));
+
+ mockEndpoint.assertIsSatisfied();
+
+ List<OtelTrace> traces =
List.copyOf(otelExtension.getTraces().values());
+ assertEquals(1, traces.size());
+
+ List<SpanData> spans = traces.get(0).getSpans();
+
+ // Find the mock-kafka EVENT_SENT span
+ SpanData kafkaSpan = getSpan(spans, "mock-kafka://testTopic",
Op.EVENT_SENT);
+
+ // Verify OpenTelemetry-specific SpanKind
+ assertEquals(SpanKind.PRODUCER, kafkaSpan.getKind(),
+ "Kafka EVENT_SENT should have PRODUCER SpanKind");
+
+ // Verify inherited properties from camel-telemetry KafkaSpanDecorator
+ assertEquals("0",
kafkaSpan.getAttributes().get(AttributeKey.stringKey("kafka.partition")),
+ "Should have kafka.partition tag from inherited decorator");
+ assertEquals("12345",
kafkaSpan.getAttributes().get(AttributeKey.stringKey("kafka.offset")),
+ "Should have kafka.offset tag from inherited decorator");
+ assertEquals("test-key",
kafkaSpan.getAttributes().get(AttributeKey.stringKey("kafka.key")),
+ "Should have kafka.key tag from inherited decorator");
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .log("Processing message");
+
+ // Mock HTTP client route - tests CLIENT SpanKind
+ from("direct:httpClient")
+ .to("mock-http://testEndpoint")
+ .to("mock:result");
+
+ // Mock Kafka producer route - tests PRODUCER SpanKind and
inherited properties
+ from("direct:kafkaProducer")
+ .to("mock-kafka://testTopic")
+ .to("mock:result");
+ }
+ };
+ }
+
+}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpComponent.java
similarity index 58%
copy from
components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
copy to
components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpComponent.java
index 501dd2430f20..9d74ab627166 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpComponent.java
@@ -14,21 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.telemetry;
+package org.apache.camel.opentelemetry2.mock;
+
+import java.util.Map;
/**
- * This interface is used to manage the lifecycle of a Span.
+ * Mock HTTP component for testing SpanKind. This component is recognized by
HttpSpanDecorator based on its class name.
*/
-public interface SpanLifecycleManager {
-
- Span create(String spanName, Span parent, SpanContextPropagationExtractor
extractor);
-
- void activate(Span span);
-
- void deactivate(Span span);
-
- void close(Span span);
-
- void inject(Span span, SpanContextPropagationInjector injector, boolean
includeTracing);
+public class MockHttpComponent extends
org.apache.camel.support.DefaultComponent {
+ @Override
+ protected org.apache.camel.Endpoint createEndpoint(String uri, String
remaining, Map<String, Object> parameters)
+ throws Exception {
+ MockHttpEndpoint endpoint = new MockHttpEndpoint(uri, this);
+ setProperties(endpoint, parameters);
+ return endpoint;
+ }
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpEndpoint.java
similarity index 56%
copy from
components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
copy to
components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpEndpoint.java
index 501dd2430f20..ee1d5e061862 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpEndpoint.java
@@ -14,21 +14,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.telemetry;
+package org.apache.camel.opentelemetry2.mock;
/**
- * This interface is used to manage the lifecycle of a Span.
+ * Mock HTTP endpoint for testing SpanKind.
*/
-public interface SpanLifecycleManager {
+class MockHttpEndpoint extends org.apache.camel.support.DefaultEndpoint {
- Span create(String spanName, Span parent, SpanContextPropagationExtractor
extractor);
+ public MockHttpEndpoint(String endpointUri, org.apache.camel.Component
component) {
+ super(endpointUri, component);
+ }
- void activate(Span span);
-
- void deactivate(Span span);
-
- void close(Span span);
-
- void inject(Span span, SpanContextPropagationInjector injector, boolean
includeTracing);
+ @Override
+ public org.apache.camel.Producer createProducer() throws Exception {
+ return new MockHttpProducer(this);
+ }
+ @Override
+ public org.apache.camel.Consumer createConsumer(org.apache.camel.Processor
processor) throws Exception {
+ throw new IllegalArgumentException("Not used in MockHttpEndpoint");
+ }
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpProducer.java
similarity index 61%
copy from
components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
copy to
components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpProducer.java
index 501dd2430f20..11530a443255 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpProducer.java
@@ -14,21 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.telemetry;
+package org.apache.camel.opentelemetry2.mock;
/**
- * This interface is used to manage the lifecycle of a Span.
+ * Mock HTTP producer that just echoes the input.
*/
-public interface SpanLifecycleManager {
+class MockHttpProducer extends org.apache.camel.support.DefaultProducer {
- Span create(String spanName, Span parent, SpanContextPropagationExtractor
extractor);
-
- void activate(Span span);
-
- void deactivate(Span span);
-
- void close(Span span);
-
- void inject(Span span, SpanContextPropagationInjector injector, boolean
includeTracing);
+ public MockHttpProducer(org.apache.camel.Endpoint endpoint) {
+ super(endpoint);
+ }
+ @Override
+ public void process(org.apache.camel.Exchange exchange) throws Exception {
+ // Simple echo - set response body to request body
+ exchange.getMessage().setBody("HTTP Response: " +
exchange.getIn().getBody());
+ }
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpSpanDecorator.java
similarity index 66%
copy from
components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
copy to
components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpSpanDecorator.java
index 501dd2430f20..e8597fbfca97 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpSpanDecorator.java
@@ -14,21 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.telemetry;
+package org.apache.camel.opentelemetry2.mock;
/**
- * This interface is used to manage the lifecycle of a Span.
+ * Span decorator for mock HTTP component used in tests.
*/
-public interface SpanLifecycleManager {
+public class MockHttpSpanDecorator extends
org.apache.camel.telemetry.decorators.AbstractHttpSpanDecorator {
- Span create(String spanName, Span parent, SpanContextPropagationExtractor
extractor);
-
- void activate(Span span);
-
- void deactivate(Span span);
-
- void close(Span span);
-
- void inject(Span span, SpanContextPropagationInjector injector, boolean
includeTracing);
+ @Override
+ public String getComponent() {
+ return "mock-http";
+ }
+ @Override
+ public String getComponentClassName() {
+ return MockHttpComponent.class.getName();
+ }
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaComponent.java
similarity index 60%
copy from
components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
copy to
components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaComponent.java
index 501dd2430f20..8ee1a0725ecd 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaComponent.java
@@ -14,21 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.telemetry;
+package org.apache.camel.opentelemetry2.mock;
+
+import java.util.Map;
/**
- * This interface is used to manage the lifecycle of a Span.
+ * Mock Kafka component for testing SpanKind and inherited properties.
*/
-public interface SpanLifecycleManager {
-
- Span create(String spanName, Span parent, SpanContextPropagationExtractor
extractor);
-
- void activate(Span span);
-
- void deactivate(Span span);
-
- void close(Span span);
-
- void inject(Span span, SpanContextPropagationInjector injector, boolean
includeTracing);
+public class MockKafkaComponent extends
org.apache.camel.support.DefaultComponent {
+ @Override
+ protected org.apache.camel.Endpoint createEndpoint(String uri, String
remaining, Map<String, Object> parameters)
+ throws Exception {
+ MockKafkaEndpoint endpoint = new MockKafkaEndpoint(uri, this);
+ setProperties(endpoint, parameters);
+ return endpoint;
+ }
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaEndpoint.java
similarity index 54%
copy from
components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
copy to
components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaEndpoint.java
index 7b019e38fd16..c9fcd1d58a7e 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaEndpoint.java
@@ -14,28 +14,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.telemetry;
-
-import org.apache.camel.Endpoint;
-import org.apache.camel.Exchange;
+package org.apache.camel.opentelemetry2.mock;
/**
- * This interface represents a decorator specific to the component/endpoint
being instrumented.
+ * Mock Kafka endpoint for testing SpanKind and inherited properties.
*/
-public interface SpanDecorator {
-
- void beforeTracingEvent(Span span, Exchange exchange, Endpoint endpoint);
-
- void afterTracingEvent(Span span, Exchange exchange);
-
- String getComponent();
-
- String getComponentClassName();
-
- String getOperationName(Exchange exchange, Endpoint endpoint);
+class MockKafkaEndpoint extends org.apache.camel.support.DefaultEndpoint {
- SpanContextPropagationExtractor getExtractor(Exchange exchange);
+ public MockKafkaEndpoint(String endpointUri, org.apache.camel.Component
component) {
+ super(endpointUri, component);
+ }
- SpanContextPropagationInjector getInjector(Exchange exchange);
+ @Override
+ public org.apache.camel.Producer createProducer() throws Exception {
+ return new MockKafkaProducer(this);
+ }
+ @Override
+ public org.apache.camel.Consumer createConsumer(org.apache.camel.Processor
processor) throws Exception {
+ throw new UnsupportedOperationException("Consumer not implemented for
mock Kafka");
+ }
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaProducer.java
similarity index 50%
copy from
components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
copy to
components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaProducer.java
index 7b019e38fd16..033993a5d226 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaProducer.java
@@ -14,28 +14,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.telemetry;
-
-import org.apache.camel.Endpoint;
-import org.apache.camel.Exchange;
+package org.apache.camel.opentelemetry2.mock;
/**
- * This interface represents a decorator specific to the component/endpoint
being instrumented.
+ * Mock Kafka producer that simulates Kafka headers.
*/
-public interface SpanDecorator {
-
- void beforeTracingEvent(Span span, Exchange exchange, Endpoint endpoint);
-
- void afterTracingEvent(Span span, Exchange exchange);
-
- String getComponent();
-
- String getComponentClassName();
-
- String getOperationName(Exchange exchange, Endpoint endpoint);
-
- SpanContextPropagationExtractor getExtractor(Exchange exchange);
-
- SpanContextPropagationInjector getInjector(Exchange exchange);
-
+class MockKafkaProducer extends org.apache.camel.support.DefaultProducer {
+
+ public MockKafkaProducer(org.apache.camel.Endpoint endpoint) {
+ super(endpoint);
+ }
+
+ @Override
+ public void process(org.apache.camel.Exchange exchange) throws Exception {
+ // Simulate Kafka response with partition, offset, and key
+ // These headers would normally be set by the real Kafka producer
+ exchange.getMessage().setHeader("kafka.PARTITION", 0);
+ exchange.getMessage().setHeader("kafka.OFFSET", "12345");
+ exchange.getMessage().setHeader("kafka.KEY", "test-key");
+ exchange.getMessage().setBody("Kafka Response: " +
exchange.getIn().getBody());
+ }
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaSpanDecorator.java
similarity index 60%
copy from
components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
copy to
components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaSpanDecorator.java
index 501dd2430f20..6d0cc534c437 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaSpanDecorator.java
@@ -14,21 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.telemetry;
+package org.apache.camel.opentelemetry2.mock;
/**
- * This interface is used to manage the lifecycle of a Span.
+ * Span decorator for mock Kafka component used in tests. Extends the real
KafkaSpanDecorator to inherit all
+ * Kafka-specific behavior (partition, offset, key tags) and adds SpanKind.
*/
-public interface SpanLifecycleManager {
+public class MockKafkaSpanDecorator extends
org.apache.camel.telemetry.decorators.KafkaSpanDecorator {
- Span create(String spanName, Span parent, SpanContextPropagationExtractor
extractor);
-
- void activate(Span span);
-
- void deactivate(Span span);
-
- void close(Span span);
-
- void inject(Span span, SpanContextPropagationInjector injector, boolean
includeTracing);
+ @Override
+ public String getComponent() {
+ return "mock-kafka";
+ }
+ @Override
+ public String getComponentClassName() {
+ return MockKafkaComponent.class.getName();
+ }
}
diff --git
a/components/camel-opentelemetry2/src/test/resources/META-INF/services/org.apache.camel.telemetry.SpanDecorator
b/components/camel-opentelemetry2/src/test/resources/META-INF/services/org.apache.camel.telemetry.SpanDecorator
new file mode 100644
index 000000000000..3a7f879efce0
--- /dev/null
+++
b/components/camel-opentelemetry2/src/test/resources/META-INF/services/org.apache.camel.telemetry.SpanDecorator
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+
+# Mock HTTP decorator for testing
+org.apache.camel.opentelemetry2.mock.MockHttpSpanDecorator
+# Mock Kafka decorator for testing
+org.apache.camel.opentelemetry2.mock.MockKafkaSpanDecorator
diff --git
a/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java
b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java
index f4d0cdee05a8..c57b5e133ec7 100644
---
a/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java
+++
b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java
@@ -87,7 +87,7 @@ public class TelemetryDevTracer extends Tracer {
}
@Override
- public Span create(String spanName, Span parent,
SpanContextPropagationExtractor extractor) {
+ public Span create(String spanName, String spanKind, Span parent,
SpanContextPropagationExtractor extractor) {
Span span = DevSpanAdapter.buildSpan(spanName);
String traceId = UUID.randomUUID().toString().replaceAll("-", "");
if (parent != null) {
diff --git a/components/camel-telemetry/src/main/docs/telemetry.adoc
b/components/camel-telemetry/src/main/docs/telemetry.adoc
index 6daad9776f25..148d7a9cfe67 100644
--- a/components/camel-telemetry/src/main/docs/telemetry.adoc
+++ b/components/camel-telemetry/src/main/docs/telemetry.adoc
@@ -147,7 +147,7 @@ The `initTracer()` is in charge to inject a concrete
implementation of `SpanLife
```java
public interface SpanLifecycleManager {
- Span create(String spanName, Span parent, SpanContextPropagationExtractor
extractor);
+ Span create(String spanName, String spanKind, Span parent,
SpanContextPropagationExtractor extractor);
void activate(Span span);
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
index 7b019e38fd16..95492f127ee6 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
+++
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java
@@ -38,4 +38,5 @@ public interface SpanDecorator {
SpanContextPropagationInjector getInjector(Exchange exchange);
+ String getSpanKind(String operation);
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
index 501dd2430f20..837cc269657d 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
+++
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java
@@ -21,7 +21,7 @@ package org.apache.camel.telemetry;
*/
public interface SpanLifecycleManager {
- Span create(String spanName, Span parent, SpanContextPropagationExtractor
extractor);
+ Span create(String spanName, String spanKind, Span parent,
SpanContextPropagationExtractor extractor);
void activate(Span span);
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java
index 514fd2044565..0c5ada29eb23 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java
+++
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java
@@ -270,7 +270,9 @@ public abstract class Tracer extends ServiceSupport
implements CamelTracingServi
SpanDecorator spanDecorator = spanDecoratorManager.get(endpoint);
Span parentSpan = spanStorageManager.peek(exchange);
String spanName = spanDecorator.getOperationName(exchange, endpoint);
- Span span = spanLifecycleManager.create(spanName, parentSpan,
spanDecorator.getExtractor(exchange));
+ String spanKind = spanDecorator.getSpanKind(op.toString());
+ Span span = spanLifecycleManager.create(spanName, spanKind, parentSpan,
+ spanDecorator.getExtractor(exchange));
span.setTag(TagConstants.OP, op.toString());
spanDecorator.beforeTracingEvent(span, exchange, endpoint);
spanLifecycleManager.activate(span);
@@ -286,7 +288,9 @@ public abstract class Tracer extends ServiceSupport
implements CamelTracingServi
// there is some inconsistency
LOG.warn("Processor tracing parent should not be null!");
}
- Span span = spanLifecycleManager.create(processorName, parentSpan,
spanDecorator.getExtractor(exchange));
+ String spanKind =
spanDecorator.getSpanKind(Op.EVENT_PROCESS.toString());
+ Span span = spanLifecycleManager.create(processorName, spanKind,
+ parentSpan, spanDecorator.getExtractor(exchange));
span.setTag(TagConstants.OP, Op.EVENT_PROCESS.toString());
spanDecorator.beforeTracingEvent(span, exchange, null);
spanLifecycleManager.activate(span);
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractHttpSpanDecorator.java
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractHttpSpanDecorator.java
index e0ae5b91a376..24a1216e796c 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractHttpSpanDecorator.java
+++
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractHttpSpanDecorator.java
@@ -19,6 +19,7 @@ package org.apache.camel.telemetry.decorators;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
+import org.apache.camel.telemetry.Op;
import org.apache.camel.telemetry.Span;
import org.apache.camel.telemetry.TagConstants;
@@ -105,4 +106,15 @@ public abstract class AbstractHttpSpanDecorator extends
AbstractSpanDecorator {
}
}
}
+
+ @Override
+ public String getSpanKind(String operationName) {
+ if (Op.EVENT_RECEIVED.name().equals(operationName)) {
+ return "SERVER";
+ } else if (Op.EVENT_SENT.name().equals(operationName)) {
+ return "CLIENT";
+ } else {
+ return "INTERNAL";
+ }
+ }
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractMessagingSpanDecorator.java
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractMessagingSpanDecorator.java
index bd4852ffaea2..e592fe2c3b18 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractMessagingSpanDecorator.java
+++
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractMessagingSpanDecorator.java
@@ -18,6 +18,7 @@ package org.apache.camel.telemetry.decorators;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
+import org.apache.camel.telemetry.Op;
import org.apache.camel.telemetry.Span;
import org.apache.camel.telemetry.TagConstants;
@@ -60,4 +61,14 @@ public abstract class AbstractMessagingSpanDecorator extends
AbstractSpanDecorat
return null;
}
+ @Override
+ public String getSpanKind(String operationName) {
+ if (Op.EVENT_RECEIVED.name().equals(operationName)) {
+ return "CONSUMER";
+ } else if (Op.EVENT_SENT.name().equals(operationName)) {
+ return "PRODUCER";
+ } else {
+ return "INTERNAL";
+ }
+ }
}
diff --git
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractSpanDecorator.java
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractSpanDecorator.java
index ee7e8002e2e2..5995ad3ed32b 100644
---
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractSpanDecorator.java
+++
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractSpanDecorator.java
@@ -175,4 +175,9 @@ public abstract class AbstractSpanDecorator implements
SpanDecorator {
public SpanContextPropagationInjector getInjector(Exchange exchange) {
return new
CamelHeadersSpanContextPropagationInjector(exchange.getIn().getHeaders());
}
+
+ @Override
+ public String getSpanKind(String operationName) {
+ return "INTERNAL";
+ }
}
diff --git
a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/mock/MockTracer.java
b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/mock/MockTracer.java
index 48e0467b9612..6479e8ebf213 100644
---
a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/mock/MockTracer.java
+++
b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/mock/MockTracer.java
@@ -53,7 +53,7 @@ public class MockTracer extends Tracer {
Map<String, Span> inMemoryStorageMap = new HashMap<>();
@Override
- public Span create(String spanName, Span parentSpan,
SpanContextPropagationExtractor extractor) {
+ public Span create(String spanName, String spanKind, Span parentSpan,
SpanContextPropagationExtractor extractor) {
Span span = MockSpanAdapter.buildSpan(spanName);
String traceId = UUID.randomUUID().toString().replaceAll("-", "");
if (parentSpan != null) {
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 c9536d9da7be..517cc570bfc8 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
@@ -125,3 +125,37 @@ Camel stomp was deprecated with Camel 4.17. The stomp
library didn't have any ac
=== camel-aws-xray removal
Camel AWS X-Ray was deprecated with Camel 4.17. Amazon Web Services X-Ray
service is in maintenance mode since February 2026. The component is now
removed.
+
+=== camel-telemetry
+
+If you have custom telemetry implementations, the following interfaces have
changed:
+
+==== `org.apache.camel.telemetry.SpanLifecycleManager`
+
+The `create` method signature has changed:
+
+[source,java]
+----
+// Old signature
+Span create(String spanName, Span parent, SpanContextPropagationExtractor
extractor)
+
+// New signature
+Span create(String spanName, String spanKind, Span parent,
SpanContextPropagationExtractor
+extractor)
+----
+
+==== `org.apache.camel.telemetry.SpanDecorator`
+
+A new method must be implemented:
+
+[source,java]
+----
+String getSpanKind(String operation)
+----
+
+This method should return the appropriate SpanKind based on the operation.
Most implementations can
+extend from:
+
+- `AbstractSpanDecorator` (returns `INTERNAL` for all operations)
+- `AbstractHttpSpanDecorator` (returns `CLIENT` for `EVENT_SENT`, `SERVER` for
`EVENT_RECEIVED`)
+- `AbstractMessagingSpanDecorator` (returns `PRODUCER` for `EVENT_SENT`,
`CONSUMER` for `EVENT_RECEIVED`)
\ No newline at end of file