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 2025/11/11 06:29:37 UTC

(camel) branch main updated: chore(components): telemetry strenghten context propagation tests (#19845)

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 50689321c122 chore(components): telemetry strenghten context propagation tests (#19845)
50689321c122 is described below

commit 50689321c1223973f592b142c0528b01fbb15c94
Author: Pasquale Congiusti <pa...@gmail.com>
AuthorDate: Tue Nov 11 07:29:30 2025 +0100

    chore(components): telemetry strenghten context propagation tests (#19845)
    
    Closes CAMEL-22648
---
 .../MicrometerObservabilityTracer.java             | 12 +--
 .../micrometer/observability/AsyncDirectTest.java  | 89 +++++++++++---------
 .../camel/micrometer/observability/AsyncTest.java  | 96 ++++++++++++----------
 .../micrometer/observability/AsyncWiretapTest.java | 89 +++++++++++---------
 .../observability/CamelOpenTelemetryExtension.java | 39 ---------
 .../observability/DisableEndpointTest.java         | 27 +++---
 .../observability/EnableProcessorsTest.java        | 55 +++++++------
 .../micrometer/observability/HeadersTraceTest.java |  5 +-
 ...rObservabilityTracerPropagationTestSupport.java |  2 +-
 .../MicrometerObservabilityTracerTest.java         | 48 +++++------
 ...est.java => SpanPropagationDownstreamTest.java} | 42 ++++------
 ...nTest.java => SpanPropagationUpstreamTest.java} | 14 ++--
 .../camel/opentelemetry2/OpenTelemetryTracer.java  | 12 +--
 .../CamelOpenTelemetryExtension.java               | 39 +--------
 .../SpanPropagationDownstreamTest.java}            | 44 +++++-----
 ...nTest.java => SpanPropagationUpstreamTest.java} |  2 +-
 .../src/test/resources/log4j2.properties           | 25 +++---
 .../SpanPropagationDownstreamTest.java}            | 43 +++++-----
 ...nTest.java => SpanPropagationUpstreamTest.java} |  2 +-
 .../telemetry/SpanPropagationDownstreamTest.java}  | 45 +++++-----
 ...nTest.java => SpanPropagationUpstreamTest.java} |  2 +-
 .../apache/camel/telemetry/mock/MockTracer.java    |  4 +-
 proposals/tracing.adoc                             | 24 +++++-
 23 files changed, 348 insertions(+), 412 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 fa239a258558..76b18dd6fb4c 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
@@ -131,23 +131,13 @@ public class MicrometerObservabilityTracer extends org.apache.camel.telemetry.Tr
             if (parent != null) {
                 MicrometerObservabilitySpanAdapter microObsParentSpan = (MicrometerObservabilitySpanAdapter) parent;
                 span = tracer.nextSpan(microObsParentSpan.getSpan());
-            } else if (extractor.get("traceparent") != null || extractor.get("X-B3-TraceId") != null) {
-                /*
-                 * This part is a bit tricky. We need to verify if the extractor
-                 * (ie, the Camel Exchange) holds a propagated parent.
-                 * As the micrometer-observability is technology agnostic, we need to check against
-                 * the available implementations (Opentelemetry and Zipkin at the moment of writing this comment).
-                 * TODO: we could do this configurable if it is required.
-                 */
+            } else {
                 Builder builder = propagator.extract(extractor, (carrier, key) -> {
                     return extractor.get(key) == null ? null : (String) extractor.get(key);
                 });
 
                 span = builder.start();
-            } else {
-                span = tracer.nextSpan();
             }
-
             span.name(spanName);
 
             return new MicrometerObservabilitySpanAdapter(span);
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncDirectTest.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncDirectTest.java
index 816a7de03a31..b1be54cf1848 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncDirectTest.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncDirectTest.java
@@ -17,22 +17,23 @@
 package org.apache.camel.micrometer.observability;
 
 import java.io.IOException;
-import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 
-import io.micrometer.tracing.test.simple.SimpleSpan;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.trace.SpanId;
+import io.opentelemetry.sdk.trace.data.SpanData;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.micrometer.observability.CamelOpenTelemetryExtension.OtelTrace;
 import org.apache.camel.telemetry.Op;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class AsyncDirectTest extends MicrometerObservabilityTracerTestSupport {
+public class AsyncDirectTest extends MicrometerObservabilityTracerPropagationTestSupport {
 
     @Test
     void testRouteMultipleRequests() throws InterruptedException, IOException {
@@ -44,37 +45,37 @@ public class AsyncDirectTest extends MicrometerObservabilityTracerTestSupport {
             context.createProducerTemplate().sendBody("direct:start", "Hello!");
         }
         mock.assertIsSatisfied(1000);
-        Map<String, MicrometerObservabilityTrace> traces = traces();
+        Map<String, OtelTrace> traces = otelExtension.getTraces();
         // Each trace should have a unique trace id. It is enough to assert that
         // the number of elements in the map is the same of the requests to prove
         // all traces have been generated uniquely.
         assertEquals(j, traces.size());
         // Each trace should have the same structure
-        for (MicrometerObservabilityTrace trace : traces.values()) {
+        for (OtelTrace trace : traces.values()) {
             checkTrace(trace, "Hello!");
         }
 
     }
 
-    private void checkTrace(MicrometerObservabilityTrace trace, String expectedBody) {
-        List<SimpleSpan> spans = trace.getSpans();
+    private void checkTrace(OtelTrace trace, String expectedBody) {
+        List<SpanData> spans = trace.getSpans();
         assertEquals(7, spans.size());
-        SimpleSpan testProducer = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_SENT);
-        SimpleSpan direct = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_RECEIVED);
-        SimpleSpan newDirectTo = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://new", Op.EVENT_SENT);
-        SimpleSpan log = MicrometerObservabilityTracerTestSupport.getSpan(spans, "log://info", Op.EVENT_SENT);
-        SimpleSpan newDirectFrom = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://new", Op.EVENT_RECEIVED);
-        SimpleSpan newLog = MicrometerObservabilityTracerTestSupport.getSpan(spans, "log://new", Op.EVENT_SENT);
-        SimpleSpan newMock = MicrometerObservabilityTracerTestSupport.getSpan(spans, "mock://end", Op.EVENT_SENT);
+        SpanData testProducer = getSpan(spans, "direct://start", Op.EVENT_SENT);
+        SpanData direct = getSpan(spans, "direct://start", Op.EVENT_RECEIVED);
+        SpanData newDirectTo = getSpan(spans, "direct://new", Op.EVENT_SENT);
+        SpanData log = getSpan(spans, "log://info", Op.EVENT_SENT);
+        SpanData newDirectFrom = getSpan(spans, "direct://new", Op.EVENT_RECEIVED);
+        SpanData newLog = getSpan(spans, "log://new", Op.EVENT_SENT);
+        SpanData newMock = getSpan(spans, "mock://end", Op.EVENT_SENT);
 
         // Validate span completion
-        assertNotEquals(Instant.EPOCH, testProducer.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, direct.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, newDirectTo.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, log.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, newDirectFrom.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, newLog.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, newMock.getEndTimestamp());
+        assertTrue(testProducer.hasEnded());
+        assertTrue(direct.hasEnded());
+        assertTrue(newDirectTo.hasEnded());
+        assertTrue(log.hasEnded());
+        assertTrue(newDirectFrom.hasEnded());
+        assertTrue(newLog.hasEnded());
+        assertTrue(newMock.hasEnded());
 
         // Validate same trace
         assertEquals(testProducer.getTraceId(), direct.getTraceId());
@@ -87,40 +88,46 @@ public class AsyncDirectTest extends MicrometerObservabilityTracerTestSupport {
         // Validate same Exchange ID
         // As it's a "direct" component, we expect the logic to happen within the same
         // Exchange boundary
-        assertEquals(testProducer.getTags().get("exchangeId"), direct.getTags().get("exchangeId"));
-        assertEquals(testProducer.getTags().get("exchangeId"), newDirectTo.getTags().get("exchangeId"));
-        assertEquals(testProducer.getTags().get("exchangeId"), newDirectFrom.getTags().get("exchangeId"));
-        assertEquals(testProducer.getTags().get("exchangeId"), log.getTags().get("exchangeId"));
-        assertEquals(testProducer.getTags().get("exchangeId"), newLog.getTags().get("exchangeId"));
-        assertEquals(testProducer.getTags().get("exchangeId"), newMock.getTags().get("exchangeId"));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                direct.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                newDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                newDirectFrom.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                log.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                newLog.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                newMock.getAttributes().get(AttributeKey.stringKey("exchangeId")));
 
         // // Validate hierarchy
-        assertTrue(testProducer.getParentId().isEmpty());
-        assertEquals(testProducer.getSpanId(), direct.getParentId());
-        assertEquals(direct.getSpanId(), newDirectTo.getParentId());
-        assertEquals(direct.getSpanId(), log.getParentId());
-        assertEquals(newDirectTo.getSpanId(), newDirectFrom.getParentId());
-        assertEquals(newDirectFrom.getSpanId(), newLog.getParentId());
-        assertEquals(newDirectFrom.getSpanId(), newMock.getParentId());
+        assertEquals(SpanId.getInvalid(), testProducer.getParentSpanId());
+        assertEquals(testProducer.getSpanId(), direct.getParentSpanId());
+        assertEquals(direct.getSpanId(), newDirectTo.getParentSpanId());
+        assertEquals(direct.getSpanId(), log.getParentSpanId());
+        assertEquals(newDirectTo.getSpanId(), newDirectFrom.getParentSpanId());
+        assertEquals(newDirectFrom.getSpanId(), newLog.getParentSpanId());
+        assertEquals(newDirectFrom.getSpanId(), newMock.getParentSpanId());
 
         // Validate message logging
-        assertEquals("message=A direct message", direct.getEvents().iterator().next().getValue());
-        assertEquals("message=A new message", newDirectFrom.getEvents().iterator().next().getValue());
+        assertEquals("message=A direct message", direct.getEvents().get(0).getName());
+        assertEquals("message=A new message", newDirectFrom.getEvents().get(0).getName());
 
         if (expectedBody == null) {
             assertEquals(
                     "message=Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]",
-                    log.getEvents().iterator().next().getValue());
+                    log.getEvents().get(0).getName());
             assertEquals(
                     "message=Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]",
-                    newLog.getEvents().iterator().next().getValue());
+                    newLog.getEvents().get(0).getName());
         } else {
             assertEquals(
                     "message=Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]",
-                    log.getEvents().iterator().next().getValue());
+                    log.getEvents().get(0).getName());
             assertEquals(
                     "message=Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]",
-                    newLog.getEvents().iterator().next().getValue());
+                    newLog.getEvents().get(0).getName());
         }
     }
 
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncTest.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncTest.java
index 3713a3a1cdc0..e47ab60ba663 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncTest.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncTest.java
@@ -17,14 +17,16 @@
 package org.apache.camel.micrometer.observability;
 
 import java.io.IOException;
-import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 
-import io.micrometer.tracing.test.simple.SimpleSpan;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.trace.SpanId;
+import io.opentelemetry.sdk.trace.data.SpanData;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.micrometer.observability.CamelOpenTelemetryExtension.OtelTrace;
 import org.apache.camel.telemetry.Op;
 import org.junit.jupiter.api.Test;
 
@@ -35,7 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 /*
  * AsyncTest tests the execution of a new spin off async components.
  */
-public class AsyncTest extends MicrometerObservabilityTracerTestSupport {
+public class AsyncTest extends MicrometerObservabilityTracerPropagationTestSupport {
 
     @Test
     void testRouteMultipleRequests() throws InterruptedException, IOException {
@@ -47,40 +49,39 @@ public class AsyncTest extends MicrometerObservabilityTracerTestSupport {
             context.createProducerTemplate().sendBody("direct:start", "Hello!");
         }
         mock.assertIsSatisfied(1000);
-        Map<String, MicrometerObservabilityTrace> traces = traces();
+        Map<String, OtelTrace> traces = otelExtension.getTraces();
         // Each trace should have a unique trace id. It is enough to assert that
         // the number of elements in the map is the same of the requests to prove
         // all traces have been generated uniquely.
         assertEquals(j, traces.size());
         // Each trace should have the same structure
-        for (MicrometerObservabilityTrace trace : traces.values()) {
+        for (OtelTrace trace : traces.values()) {
             checkTrace(trace, "Hello!");
         }
 
     }
 
-    private void checkTrace(MicrometerObservabilityTrace trace, String expectedBody) {
-        List<SimpleSpan> spans = trace.getSpans();
+    private void checkTrace(OtelTrace trace, String expectedBody) {
+        List<SpanData> spans = trace.getSpans();
         assertEquals(8, spans.size());
-        SimpleSpan testProducer = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_SENT);
-        SimpleSpan direct = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_RECEIVED);
-        SimpleSpan asyncDirectTo = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://async", Op.EVENT_SENT);
-        SimpleSpan asyncDirectFrom
-                = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://async", Op.EVENT_RECEIVED);
-        SimpleSpan async = MicrometerObservabilityTracerTestSupport.getSpan(spans, "async://bye:camel", Op.EVENT_SENT);
-        SimpleSpan log = MicrometerObservabilityTracerTestSupport.getSpan(spans, "log://info", Op.EVENT_SENT);
-        SimpleSpan asyncLog = MicrometerObservabilityTracerTestSupport.getSpan(spans, "log://tapped", Op.EVENT_SENT);
-        SimpleSpan asyncMock = MicrometerObservabilityTracerTestSupport.getSpan(spans, "mock://end", Op.EVENT_SENT);
+        SpanData testProducer = getSpan(spans, "direct://start", Op.EVENT_SENT);
+        SpanData direct = getSpan(spans, "direct://start", Op.EVENT_RECEIVED);
+        SpanData asyncDirectTo = getSpan(spans, "direct://async", Op.EVENT_SENT);
+        SpanData asyncDirectFrom = getSpan(spans, "direct://async", Op.EVENT_RECEIVED);
+        SpanData async = getSpan(spans, "async://bye:camel", Op.EVENT_SENT);
+        SpanData log = getSpan(spans, "log://info", Op.EVENT_SENT);
+        SpanData asyncLog = getSpan(spans, "log://tapped", Op.EVENT_SENT);
+        SpanData asyncMock = getSpan(spans, "mock://end", Op.EVENT_SENT);
 
         // Validate span completion
-        assertNotEquals(Instant.EPOCH, testProducer.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, direct.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, asyncDirectTo.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, log.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, asyncDirectFrom.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, async.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, asyncLog.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, asyncMock.getEndTimestamp());
+        assertTrue(testProducer.hasEnded());
+        assertTrue(direct.hasEnded());
+        assertTrue(asyncDirectTo.hasEnded());
+        assertTrue(log.hasEnded());
+        assertTrue(asyncDirectFrom.hasEnded());
+        assertTrue(async.hasEnded());
+        assertTrue(asyncLog.hasEnded());
+        assertTrue(asyncMock.hasEnded());
 
         // Validate same trace
         assertEquals(testProducer.getTraceId(), direct.getTraceId());
@@ -92,40 +93,47 @@ public class AsyncTest extends MicrometerObservabilityTracerTestSupport {
         assertEquals(testProducer.getTraceId(), asyncMock.getTraceId());
 
         // Validate different Exchange ID
-        assertNotEquals(testProducer.getTags().get("exchangeId"), asyncDirectTo.getTags().get("exchangeId"));
-        assertEquals(testProducer.getTags().get("exchangeId"), direct.getTags().get("exchangeId"));
-        assertEquals(testProducer.getTags().get("exchangeId"), log.getTags().get("exchangeId"));
-        assertEquals(asyncDirectTo.getTags().get("exchangeId"), asyncDirectFrom.getTags().get("exchangeId"));
-        assertEquals(asyncDirectTo.getTags().get("exchangeId"), async.getTags().get("exchangeId"));
-        assertEquals(asyncDirectTo.getTags().get("exchangeId"), asyncLog.getTags().get("exchangeId"));
-        assertEquals(asyncDirectTo.getTags().get("exchangeId"), asyncMock.getTags().get("exchangeId"));
+        assertNotEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                asyncDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                direct.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                log.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(asyncDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                asyncDirectFrom.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(asyncDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                async.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(asyncDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                asyncLog.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(asyncDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                asyncMock.getAttributes().get(AttributeKey.stringKey("exchangeId")));
 
         // Validate hierarchy
-        assertTrue(testProducer.getParentId().isEmpty());
-        assertEquals(testProducer.getSpanId(), direct.getParentId());
-        assertEquals(direct.getSpanId(), asyncDirectTo.getParentId());
-        assertEquals(direct.getSpanId(), log.getParentId());
-        assertEquals(asyncDirectTo.getSpanId(), asyncDirectFrom.getParentId());
-        assertEquals(asyncDirectFrom.getSpanId(), async.getParentId());
-        assertEquals(asyncDirectFrom.getSpanId(), asyncLog.getParentId());
-        assertEquals(asyncDirectFrom.getSpanId(), asyncMock.getParentId());
+        assertEquals(SpanId.getInvalid(), testProducer.getParentSpanId());
+        assertEquals(testProducer.getSpanId(), direct.getParentSpanId());
+        assertEquals(direct.getSpanId(), asyncDirectTo.getParentSpanId());
+        assertEquals(direct.getSpanId(), log.getParentSpanId());
+        assertEquals(asyncDirectTo.getSpanId(), asyncDirectFrom.getParentSpanId());
+        assertEquals(asyncDirectFrom.getSpanId(), async.getParentSpanId());
+        assertEquals(asyncDirectFrom.getSpanId(), asyncLog.getParentSpanId());
+        assertEquals(asyncDirectFrom.getSpanId(), asyncMock.getParentSpanId());
 
         // Validate message logging
-        assertEquals("message=A direct message", direct.getEvents().iterator().next().getValue());
-        assertEquals("message=An async message", asyncDirectFrom.getEvents().iterator().next().getValue());
+        assertEquals("message=A direct message", direct.getEvents().get(0).getName());
+        assertEquals("message=An async message", asyncDirectFrom.getEvents().get(0).getName());
         String expectedBodyAsync = "Bye Camel";
         if (expectedBody == null) {
             assertEquals(
                     "message=Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]",
-                    log.getEvents().iterator().next().getValue());
+                    log.getEvents().get(0).getName());
         } else {
             assertEquals(
                     "message=Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]",
-                    log.getEvents().iterator().next().getValue());
+                    log.getEvents().get(0).getName());
         }
         assertEquals(
                 "message=Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBodyAsync + "]",
-                asyncLog.getEvents().iterator().next().getValue());
+                asyncLog.getEvents().get(0).getName());
     }
 
     @Override
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncWiretapTest.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncWiretapTest.java
index 4c9210cab47f..225e9d109840 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncWiretapTest.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/AsyncWiretapTest.java
@@ -17,14 +17,16 @@
 package org.apache.camel.micrometer.observability;
 
 import java.io.IOException;
-import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 
-import io.micrometer.tracing.test.simple.SimpleSpan;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.trace.SpanId;
+import io.opentelemetry.sdk.trace.data.SpanData;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.micrometer.observability.CamelOpenTelemetryExtension.OtelTrace;
 import org.apache.camel.telemetry.Op;
 import org.junit.jupiter.api.Test;
 
@@ -36,7 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
  * WiretappedRouteTest tests the execution of a new spin off component which would create a new exchange,
  * for example, using the wiretap component.
  */
-public class AsyncWiretapTest extends MicrometerObservabilityTracerTestSupport {
+public class AsyncWiretapTest extends MicrometerObservabilityTracerPropagationTestSupport {
 
     @Test
     void testRouteMultipleRequests() throws InterruptedException, IOException {
@@ -48,38 +50,37 @@ public class AsyncWiretapTest extends MicrometerObservabilityTracerTestSupport {
             context.createProducerTemplate().sendBody("direct:start", "Hello!");
         }
         mock.assertIsSatisfied(1000);
-        Map<String, MicrometerObservabilityTrace> traces = traces();
+        Map<String, OtelTrace> traces = otelExtension.getTraces();
         // Each trace should have a unique trace id. It is enough to assert that
         // the number of elements in the map is the same of the requests to prove
         // all traces have been generated uniquely.
         assertEquals(j, traces.size());
         // Each trace should have the same structure
-        for (MicrometerObservabilityTrace trace : traces.values()) {
+        for (OtelTrace trace : traces.values()) {
             checkTrace(trace, "Hello!");
         }
 
     }
 
-    private void checkTrace(MicrometerObservabilityTrace trace, String expectedBody) {
-        List<SimpleSpan> spans = trace.getSpans();
+    private void checkTrace(OtelTrace trace, String expectedBody) {
+        List<SpanData> spans = trace.getSpans();
         assertEquals(7, spans.size());
-        SimpleSpan testProducer = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_SENT);
-        SimpleSpan direct = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://start", Op.EVENT_RECEIVED);
-        SimpleSpan wiretapDirectTo = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://tap", Op.EVENT_SENT);
-        SimpleSpan wiretapDirectFrom
-                = MicrometerObservabilityTracerTestSupport.getSpan(spans, "direct://tap", Op.EVENT_RECEIVED);
-        SimpleSpan log = MicrometerObservabilityTracerTestSupport.getSpan(spans, "log://info", Op.EVENT_SENT);
-        SimpleSpan wiretapLog = MicrometerObservabilityTracerTestSupport.getSpan(spans, "log://tapped", Op.EVENT_SENT);
-        SimpleSpan wiretapMock = MicrometerObservabilityTracerTestSupport.getSpan(spans, "mock://end", Op.EVENT_SENT);
+        SpanData testProducer = getSpan(spans, "direct://start", Op.EVENT_SENT);
+        SpanData direct = getSpan(spans, "direct://start", Op.EVENT_RECEIVED);
+        SpanData wiretapDirectTo = getSpan(spans, "direct://tap", Op.EVENT_SENT);
+        SpanData wiretapDirectFrom = getSpan(spans, "direct://tap", Op.EVENT_RECEIVED);
+        SpanData log = getSpan(spans, "log://info", Op.EVENT_SENT);
+        SpanData wiretapLog = getSpan(spans, "log://tapped", Op.EVENT_SENT);
+        SpanData wiretapMock = getSpan(spans, "mock://end", Op.EVENT_SENT);
 
         // Validate span completion
-        assertNotEquals(Instant.EPOCH, testProducer.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, direct.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, wiretapDirectTo.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, log.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, wiretapDirectFrom.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, wiretapLog.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, wiretapMock.getEndTimestamp());
+        assertTrue(testProducer.hasEnded());
+        assertTrue(direct.hasEnded());
+        assertTrue(wiretapDirectTo.hasEnded());
+        assertTrue(log.hasEnded());
+        assertTrue(wiretapDirectFrom.hasEnded());
+        assertTrue(wiretapLog.hasEnded());
+        assertTrue(wiretapMock.hasEnded());
 
         // Validate same trace
         assertEquals(testProducer.getTraceId(), direct.getTraceId());
@@ -90,40 +91,46 @@ public class AsyncWiretapTest extends MicrometerObservabilityTracerTestSupport {
         assertEquals(testProducer.getTraceId(), wiretapMock.getTraceId());
 
         // Validate different Exchange ID
-        assertNotEquals(testProducer.getTags().get("exchangeId"), wiretapDirectTo.getTags().get("exchangeId"));
-        assertEquals(testProducer.getTags().get("exchangeId"), direct.getTags().get("exchangeId"));
-        assertEquals(testProducer.getTags().get("exchangeId"), log.getTags().get("exchangeId"));
-        assertEquals(wiretapDirectTo.getTags().get("exchangeId"), wiretapDirectFrom.getTags().get("exchangeId"));
-        assertEquals(wiretapDirectTo.getTags().get("exchangeId"), wiretapLog.getTags().get("exchangeId"));
-        assertEquals(wiretapDirectTo.getTags().get("exchangeId"), wiretapMock.getTags().get("exchangeId"));
+        assertNotEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                wiretapDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                direct.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(testProducer.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                log.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(wiretapDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                wiretapDirectFrom.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(wiretapDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                wiretapLog.getAttributes().get(AttributeKey.stringKey("exchangeId")));
+        assertEquals(wiretapDirectTo.getAttributes().get(AttributeKey.stringKey("exchangeId")),
+                wiretapMock.getAttributes().get(AttributeKey.stringKey("exchangeId")));
 
         // Validate hierarchy
-        assertTrue(testProducer.getParentId().isEmpty());
-        assertEquals(testProducer.getSpanId(), direct.getParentId());
-        assertEquals(direct.getSpanId(), wiretapDirectTo.getParentId());
-        assertEquals(direct.getSpanId(), log.getParentId());
-        assertEquals(wiretapDirectTo.getSpanId(), wiretapDirectFrom.getParentId());
-        assertEquals(wiretapDirectFrom.getSpanId(), wiretapLog.getParentId());
-        assertEquals(wiretapDirectFrom.getSpanId(), wiretapMock.getParentId());
+        assertEquals(SpanId.getInvalid(), testProducer.getParentSpanId());
+        assertEquals(testProducer.getSpanId(), direct.getParentSpanId());
+        assertEquals(direct.getSpanId(), wiretapDirectTo.getParentSpanId());
+        assertEquals(direct.getSpanId(), log.getParentSpanId());
+        assertEquals(wiretapDirectTo.getSpanId(), wiretapDirectFrom.getParentSpanId());
+        assertEquals(wiretapDirectFrom.getSpanId(), wiretapLog.getParentSpanId());
+        assertEquals(wiretapDirectFrom.getSpanId(), wiretapMock.getParentSpanId());
 
         // Validate message logging
-        assertEquals("message=A direct message", direct.getEvents().iterator().next().getValue());
-        assertEquals("message=A tapped message", wiretapDirectFrom.getEvents().iterator().next().getValue());
+        assertEquals("message=A direct message", direct.getEvents().get(0).getName());
+        assertEquals("message=A tapped message", wiretapDirectFrom.getEvents().get(0).getName());
 
         if (expectedBody == null) {
             assertEquals(
                     "message=Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]",
-                    log.getEvents().iterator().next().getValue());
+                    log.getEvents().get(0).getName());
             assertEquals(
                     "message=Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]",
-                    wiretapLog.getEvents().iterator().next().getValue());
+                    wiretapLog.getEvents().get(0).getName());
         } else {
             assertEquals(
                     "message=Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]",
-                    log.getEvents().iterator().next().getValue());
+                    log.getEvents().get(0).getName());
             assertEquals(
                     "message=Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]",
-                    wiretapLog.getEvents().iterator().next().getValue());
+                    wiretapLog.getEvents().get(0).getName());
         }
 
     }
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/CamelOpenTelemetryExtension.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/CamelOpenTelemetryExtension.java
index e54393fa3937..a5cdd9ea2a93 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/CamelOpenTelemetryExtension.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/CamelOpenTelemetryExtension.java
@@ -25,7 +25,6 @@ import java.util.Map;
 import io.opentelemetry.api.GlobalOpenTelemetry;
 import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
-import io.opentelemetry.context.Context;
 import io.opentelemetry.context.propagation.ContextPropagators;
 import io.opentelemetry.sdk.OpenTelemetrySdk;
 //import io.opentelemetry.sdk.extension.incubator.trace.LeakDetectingSpanProcessor;
@@ -39,19 +38,12 @@ import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
 import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
 import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
 import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
-import io.opentelemetry.sdk.trace.ReadWriteSpan;
-import io.opentelemetry.sdk.trace.ReadableSpan;
 import io.opentelemetry.sdk.trace.SdkTracerProvider;
-import io.opentelemetry.sdk.trace.SpanProcessor;
 import io.opentelemetry.sdk.trace.data.SpanData;
 import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
 import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.slf4j.Marker;
-import org.slf4j.MarkerFactory;
 
 /**
  * Adapted from
@@ -66,8 +58,6 @@ final class CamelOpenTelemetryExtension implements BeforeEachCallback, AfterEach
     static CamelOpenTelemetryExtension create() {
         InMemorySpanExporter spanExporter = InMemorySpanExporter.create();
         SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
-                //.addSpanProcessor(LeakDetectingSpanProcessor.create())
-                .addSpanProcessor(new LoggingSpanProcessor())
                 .addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
                 .build();
 
@@ -175,35 +165,6 @@ final class CamelOpenTelemetryExtension implements BeforeEachCallback, AfterEach
         openTelemetry.close();
     }
 
-    static class LoggingSpanProcessor implements SpanProcessor {
-        private static final Logger LOG = LoggerFactory.getLogger(LoggingSpanProcessor.class);
-        private static final Marker OTEL_MARKER = MarkerFactory.getMarker("OTEL");
-
-        @Override
-        public void onStart(Context context, ReadWriteSpan readWriteSpan) {
-            LOG.info(OTEL_MARKER, "Span started: name - '{}', kind - '{}', id - '{}-{}", readWriteSpan.getName(),
-                    readWriteSpan.getKind(),
-                    readWriteSpan.getSpanContext().getTraceId(), readWriteSpan.getSpanContext().getSpanId());
-        }
-
-        @Override
-        public boolean isStartRequired() {
-            return true;
-        }
-
-        @Override
-        public void onEnd(ReadableSpan readableSpan) {
-            LOG.info(OTEL_MARKER, "Span ended: name - '{}', kind - '{}', id - '{}-{}", readableSpan.getName(),
-                    readableSpan.getKind(),
-                    readableSpan.getSpanContext().getTraceId(), readableSpan.getSpanContext().getSpanId());
-        }
-
-        @Override
-        public boolean isEndRequired() {
-            return true;
-        }
-    }
-
     Map<String, OtelTrace> getTraces() {
         Map<String, OtelTrace> answer = new HashMap<>();
         for (SpanData span : this.getSpans()) {
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/DisableEndpointTest.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/DisableEndpointTest.java
index 7dcc77c4da4f..76fdd637dfb7 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/DisableEndpointTest.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/DisableEndpointTest.java
@@ -17,23 +17,24 @@
 package org.apache.camel.micrometer.observability;
 
 import java.io.IOException;
-import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 
-import io.micrometer.tracing.test.simple.SimpleSpan;
+import io.opentelemetry.api.trace.SpanId;
+import io.opentelemetry.sdk.trace.data.SpanData;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.micrometer.observability.CamelOpenTelemetryExtension.OtelTrace;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class DisableEndpointTest extends MicrometerObservabilityTracerTestSupport {
+public class DisableEndpointTest extends MicrometerObservabilityTracerPropagationTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
@@ -45,7 +46,7 @@ public class DisableEndpointTest extends MicrometerObservabilityTracerTestSuppor
     @Test
     void testProcessorsTraceRequest() throws IOException {
         template.sendBody("direct:start", "my-body");
-        Map<String, MicrometerObservabilityTrace> traces = traces();
+        Map<String, OtelTrace> traces = otelExtension.getTraces();
         assertEquals(1, traces.size());
         checkTrace(traces.values().iterator().next());
     }
@@ -62,22 +63,22 @@ public class DisableEndpointTest extends MicrometerObservabilityTracerTestSuppor
         Assertions.assertEquals("testValue", myVar);
     }
 
-    private void checkTrace(MicrometerObservabilityTrace trace) {
-        List<SimpleSpan> spans = trace.getSpans();
+    private void checkTrace(OtelTrace trace) {
+        List<SpanData> spans = trace.getSpans();
         assertEquals(2, spans.size());
-        SimpleSpan testProducer = spans.get(0);
-        SimpleSpan direct = spans.get(1);
+        SpanData testProducer = spans.get(0);
+        SpanData direct = spans.get(1);
 
         // Validate span completion
-        assertNotEquals(Instant.EPOCH, testProducer.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, direct.getEndTimestamp());
+        assertTrue(testProducer.hasEnded());
+        assertTrue(direct.hasEnded());
 
         // Validate same trace
         assertEquals(testProducer.getTraceId(), direct.getTraceId());
 
         // Validate hierarchy
-        assertEquals("", testProducer.getParentId());
-        assertEquals(testProducer.getSpanId(), direct.getParentId());
+        assertEquals(SpanId.getInvalid(), testProducer.getParentSpanId());
+        assertEquals(testProducer.getSpanId(), direct.getParentSpanId());
     }
 
     @Override
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/EnableProcessorsTest.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/EnableProcessorsTest.java
index 5431cddfcd42..84395878723b 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/EnableProcessorsTest.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/EnableProcessorsTest.java
@@ -17,24 +17,25 @@
 package org.apache.camel.micrometer.observability;
 
 import java.io.IOException;
-import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 
-import io.micrometer.tracing.test.simple.SimpleSpan;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.trace.SpanId;
+import io.opentelemetry.sdk.trace.data.SpanData;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.micrometer.observability.CamelOpenTelemetryExtension.OtelTrace;
 import org.apache.camel.telemetry.Op;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class EnableProcessorsTest extends MicrometerObservabilityTracerTestSupport {
+public class EnableProcessorsTest extends MicrometerObservabilityTracerPropagationTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
@@ -45,29 +46,29 @@ public class EnableProcessorsTest extends MicrometerObservabilityTracerTestSuppo
     @Test
     void testProcessorsTraceRequest() throws IOException {
         template.sendBody("direct:start", "my-body");
-        Map<String, MicrometerObservabilityTrace> traces = traces();
+        Map<String, OtelTrace> traces = otelExtension.getTraces();
         assertEquals(1, traces.size());
         checkTrace(traces.values().iterator().next());
     }
 
-    private void checkTrace(MicrometerObservabilityTrace trace) {
-        List<SimpleSpan> spans = trace.getSpans();
+    private void checkTrace(OtelTrace trace) {
+        List<SpanData> spans = trace.getSpans();
         assertEquals(6, spans.size());
 
-        SimpleSpan testProducer = spans.get(0);
-        SimpleSpan direct = spans.get(1);
-        SimpleSpan innerLog = spans.get(2);
-        SimpleSpan innerProcessor = spans.get(3);
-        SimpleSpan log = spans.get(4);
-        SimpleSpan innerToLog = spans.get(5);
+        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
-        assertNotEquals(Instant.EPOCH, testProducer.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, direct.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, innerLog.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, innerProcessor.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, log.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, innerToLog.getEndTimestamp());
+        assertTrue(testProducer.hasEnded());
+        assertTrue(direct.hasEnded());
+        assertTrue(innerLog.hasEnded());
+        assertTrue(innerProcessor.hasEnded());
+        assertTrue(log.hasEnded());
+        assertTrue(innerToLog.hasEnded());
 
         // Validate same trace
         assertEquals(testProducer.getTraceId(), direct.getTraceId());
@@ -77,16 +78,16 @@ public class EnableProcessorsTest extends MicrometerObservabilityTracerTestSuppo
         assertEquals(testProducer.getTraceId(), innerToLog.getTraceId());
 
         // Validate op
-        assertEquals(Op.EVENT_RECEIVED.toString(), direct.getTags().get("op"));
-        assertEquals(Op.EVENT_PROCESS.toString(), innerProcessor.getTags().get("op"));
+        assertEquals(Op.EVENT_RECEIVED.toString(), direct.getAttributes().get(AttributeKey.stringKey("op")));
+        assertEquals(Op.EVENT_PROCESS.toString(), innerProcessor.getAttributes().get(AttributeKey.stringKey("op")));
 
         // Validate hierarchy
-        assertTrue(testProducer.getParentId().isEmpty());
-        assertEquals(testProducer.getSpanId(), direct.getParentId());
-        assertEquals(direct.getSpanId(), innerLog.getParentId());
-        assertEquals(direct.getSpanId(), innerProcessor.getParentId());
-        assertEquals(direct.getSpanId(), log.getParentId());
-        assertEquals(log.getSpanId(), innerToLog.getParentId());
+        assertEquals(SpanId.getInvalid(), testProducer.getParentSpanId());
+        assertEquals(testProducer.getSpanId(), direct.getParentSpanId());
+        assertEquals(direct.getSpanId(), innerLog.getParentSpanId());
+        assertEquals(direct.getSpanId(), innerProcessor.getParentSpanId());
+        assertEquals(direct.getSpanId(), log.getParentSpanId());
+        assertEquals(log.getSpanId(), innerToLog.getParentSpanId());
     }
 
     @Override
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
index 194e35e78e07..89c89b35d514 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
@@ -23,13 +23,14 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.micrometer.observability.CamelOpenTelemetryExtension.OtelTrace;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
-public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
+public class HeadersTraceTest extends MicrometerObservabilityTracerPropagationTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
@@ -42,7 +43,7 @@ public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
         MockEndpoint mock = getMockEndpoint("mock:result");
         mock.expectedMessageCount(1);
         template.sendBody("direct:start", "my-body");
-        Map<String, MicrometerObservabilityTrace> traces = traces();
+        Map<String, OtelTrace> traces = otelExtension.getTraces();
         assertEquals(1, traces.size());
         mock.assertIsSatisfied();
         Map<String, Object> headers = mock.getExchanges().get(0).getIn().getHeaders();
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerPropagationTestSupport.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerPropagationTestSupport.java
index 35d320d52637..9a8eb6f5e439 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerPropagationTestSupport.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerPropagationTestSupport.java
@@ -36,10 +36,10 @@ import org.apache.camel.test.junit5.ExchangeTestSupport;
 public class MicrometerObservabilityTracerPropagationTestSupport extends ExchangeTestSupport {
 
     protected CamelOpenTelemetryExtension otelExtension = CamelOpenTelemetryExtension.create();
+    protected MicrometerObservabilityTracer tst = new MicrometerObservabilityTracer();
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
-        MicrometerObservabilityTracer tst = new MicrometerObservabilityTracer();
         CamelContext context = super.createCamelContext();
 
         ContextPropagators propagators = otelExtension.getPropagators();
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerTest.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerTest.java
index f427c3f05bf6..0cd6766ba0b8 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerTest.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerTest.java
@@ -17,26 +17,27 @@
 package org.apache.camel.micrometer.observability;
 
 import java.io.IOException;
-import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 
-import io.micrometer.tracing.test.simple.SimpleSpan;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.trace.SpanId;
+import io.opentelemetry.sdk.trace.data.SpanData;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.micrometer.observability.CamelOpenTelemetryExtension.OtelTrace;
 import org.apache.camel.telemetry.Op;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class MicrometerObservabilityTracerTest extends MicrometerObservabilityTracerTestSupport {
+public class MicrometerObservabilityTracerTest extends MicrometerObservabilityTracerPropagationTestSupport {
 
     @Test
     void testRouteSingleRequest() throws IOException {
         template.request("direct:start", null);
-        Map<String, MicrometerObservabilityTrace> traces = traces();
+        Map<String, OtelTrace> traces = otelExtension.getTraces();
         assertEquals(1, traces.size());
         checkTrace(traces.values().iterator().next(), null);
     }
@@ -46,54 +47,53 @@ public class MicrometerObservabilityTracerTest extends MicrometerObservabilityTr
         for (int i = 1; i <= 10; i++) {
             context.createProducerTemplate().sendBody("direct:start", "Hello!");
         }
-        Map<String, MicrometerObservabilityTrace> traces = traces();
+        Map<String, OtelTrace> traces = otelExtension.getTraces();
         // Each trace should have a unique trace id. It is enough to assert that
         // the number of elements in the map is the same of the requests to prove
         // all traces have been generated uniquely.
         assertEquals(10, traces.size());
         // Each trace should have the same structure
-        for (MicrometerObservabilityTrace trace : traces.values()) {
+        for (OtelTrace trace : traces.values()) {
             checkTrace(trace, "Hello!");
         }
     }
 
-    private void checkTrace(MicrometerObservabilityTrace trace, String expectedBody) {
-        List<SimpleSpan> spans = trace.getSpans();
+    private void checkTrace(OtelTrace trace, String expectedBody) {
+        List<SpanData> spans = trace.getSpans();
         assertEquals(3, spans.size());
-        SimpleSpan testProducer = spans.get(0);
-        SimpleSpan direct = spans.get(1);
-        SimpleSpan log = spans.get(2);
+        SpanData testProducer = spans.get(0);
+        SpanData direct = spans.get(1);
+        SpanData log = spans.get(2);
 
         // Validate span completion
-        assertNotEquals(Instant.EPOCH, testProducer.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, direct.getEndTimestamp());
-        assertNotEquals(Instant.EPOCH, log.getEndTimestamp());
+        assertTrue(testProducer.hasEnded());
+        assertTrue(direct.hasEnded());
+        assertTrue(log.hasEnded());
 
         // Validate same trace
         assertEquals(testProducer.getTraceId(), direct.getTraceId());
         assertEquals(direct.getTraceId(), log.getTraceId());
 
         // Validate hierarchy
-        assertTrue(testProducer.getParentId().isEmpty());
-        assertEquals(testProducer.getSpanId(), direct.getParentId());
-        assertEquals(direct.getSpanId(), log.getParentId());
+        assertEquals(SpanId.getInvalid(), testProducer.getParentSpanContext().getSpanId());
+        assertEquals(testProducer.getSpanContext().getSpanId(), direct.getParentSpanContext().getSpanId());
+        assertEquals(direct.getSpanContext().getSpanId(), log.getParentSpanContext().getSpanId());
 
         // Validate operations
-        assertEquals(Op.EVENT_SENT.toString(), testProducer.getTags().get("op"));
-        assertEquals(Op.EVENT_RECEIVED.toString(), direct.getTags().get("op"));
+        assertEquals(Op.EVENT_SENT.toString(), testProducer.getAttributes().get(AttributeKey.stringKey("op")));
+        assertEquals(Op.EVENT_RECEIVED.toString(), direct.getAttributes().get(AttributeKey.stringKey("op")));
 
         // Validate message logging
-        assertEquals("message=A message", direct.getEvents().iterator().next().getValue());
+        assertEquals("message=A message", direct.getEvents().get(0).getName());
         if (expectedBody == null) {
             assertEquals(
                     "message=Exchange[ExchangePattern: InOut, BodyType: null, Body: [Body is null]]",
-                    log.getEvents().iterator().next().getValue());
+                    log.getEvents().get(0).getName());
         } else {
             assertEquals(
                     "message=Exchange[ExchangePattern: InOnly, BodyType: String, Body: " + expectedBody + "]",
-                    log.getEvents().iterator().next().getValue());
+                    log.getEvents().get(0).getName());
         }
-
     }
 
     @Override
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/SpanPropagationDownstreamTest.java
similarity index 52%
copy from components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
copy to components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/SpanPropagationDownstreamTest.java
index 194e35e78e07..a3bcec45fc2e 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/SpanPropagationDownstreamTest.java
@@ -16,44 +16,30 @@
  */
 package org.apache.camel.micrometer.observability;
 
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.camel.CamelContext;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-    @Override
-    protected CamelContext createCamelContext() throws Exception {
-        tst.setTraceHeadersInclusion(true);
-        return super.createCamelContext();
-    }
+/**
+ * This test is special as it requires a different setting to inherit the Opentelemetry propagation mechanism.
+ */
+public class SpanPropagationDownstreamTest extends MicrometerObservabilityTracerPropagationTestSupport {
 
     @Test
-    void testProcessorsTraceRequest() throws InterruptedException, IOException {
+    void testPropagateDownstreamTraceRequest() throws InterruptedException {
         MockEndpoint mock = getMockEndpoint("mock:result");
         mock.expectedMessageCount(1);
-        template.sendBody("direct:start", "my-body");
-        Map<String, MicrometerObservabilityTrace> traces = traces();
-        assertEquals(1, traces.size());
+        template.sendBody("direct:start", "Test");
         mock.assertIsSatisfied();
-        Map<String, Object> headers = mock.getExchanges().get(0).getIn().getHeaders();
-
-        // NOTE: the check on TRACE_ID and SPAN_ID instead of the related constant is on purpose
-        // We want to fail if there is any change in the constant by any chance and report into the
-        // documentation.
-        assertNotNull(headers.get("CAMEL_TRACE_ID"));
-        assertNotNull(headers.get("CAMEL_SPAN_ID"));
-        assertNotEquals("", headers.get("CAMEL_TRACE_ID"));
-        assertNotEquals("", headers.get("CAMEL_SPAN_ID"));
+        mock.getExchanges().forEach(exchange -> {
+            assertTrue(
+                    exchange.getIn().getHeader("traceparent", String.class)
+                            .matches("^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$"),
+                    "The traceparent header does not match with the expected format <version>-<traceid>-<spanid>-<flags>");
+        });
     }
 
     @Override
@@ -62,6 +48,8 @@ public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
             @Override
             public void configure() {
                 from("direct:start")
+                        .routeId("start")
+                        .log("A message")
                         .to("mock:result");
             }
         };
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/SpanPropagationTest.java b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/SpanPropagationUpstreamTest.java
similarity index 83%
rename from components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/SpanPropagationTest.java
rename to components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/SpanPropagationUpstreamTest.java
index 70c4c7191870..9264531554a9 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/SpanPropagationTest.java
+++ b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/SpanPropagationUpstreamTest.java
@@ -32,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 /**
  * This test is special as it requires a different setting to inherit the Opentelemetry propagation mechanism.
  */
-public class SpanPropagationTest extends MicrometerObservabilityTracerPropagationTestSupport {
+public class SpanPropagationUpstreamTest extends MicrometerObservabilityTracerPropagationTestSupport {
 
     @Test
     void testPropagateUpstreamTraceRequest() throws IOException {
@@ -56,14 +56,14 @@ public class SpanPropagationTest extends MicrometerObservabilityTracerPropagatio
         assertTrue(log.hasEnded());
 
         // Validate same trace
-        assertEquals("0af044aea5c127fd5ab5f839de2b8ae2", testProducer.getSpanContext().getTraceId());
-        assertEquals(testProducer.getSpanContext().getTraceId(), direct.getSpanContext().getTraceId());
-        assertEquals(direct.getSpanContext().getTraceId(), log.getSpanContext().getTraceId());
+        assertEquals("0af044aea5c127fd5ab5f839de2b8ae2", testProducer.getTraceId());
+        assertEquals(testProducer.getTraceId(), direct.getTraceId());
+        assertEquals(direct.getTraceId(), log.getTraceId());
 
         // Validate hierarchy
-        assertEquals("d362a8a943c2b289", testProducer.getParentSpanContext().getSpanId());
-        assertEquals(testProducer.getSpanContext().getSpanId(), direct.getParentSpanContext().getSpanId());
-        assertEquals(direct.getSpanContext().getSpanId(), log.getParentSpanContext().getSpanId());
+        assertEquals("d362a8a943c2b289", testProducer.getParentSpanId());
+        assertEquals(testProducer.getSpanId(), direct.getParentSpanId());
+        assertEquals(direct.getSpanId(), log.getParentSpanId());
     }
 
     @Override
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 e1d0cefd52fd..071425324c6a 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
@@ -105,16 +105,8 @@ public class OpenTelemetryTracer extends org.apache.camel.telemetry.Tracer {
                 builder = builder.setParent(Context.current().with(otelParentSpan.getSpan()));
                 baggage = otelParentSpan.getBaggage();
             } else {
-                /*
-                 * This part is a bit tricky in Opentelemetry. We need to verify if the extractor
-                 * (ie, the Camel Exchange) holds a propagated parent. If it doesn't, then, we must use a null Context.
-                 */
-                Context current = null;
-                if (extractor.get("traceparent") != null) {
-                    current = Context.current();
-                }
                 // Try to get parent from context propagation (upstream traces)
-                Context ctx = contextPropagators.getTextMapPropagator().extract(current, extractor,
+                Context ctx = contextPropagators.getTextMapPropagator().extract(Context.root(), extractor,
                         new TextMapGetter<SpanContextPropagationExtractor>() {
                             @Override
                             public Iterable<String> keys(SpanContextPropagationExtractor carrier) {
@@ -129,7 +121,9 @@ public class OpenTelemetryTracer extends org.apache.camel.telemetry.Tracer {
                                 return carrier.get(key).toString();
                             }
                         });
+
                 builder = builder.setParent(ctx);
+                baggage = Baggage.fromContext(ctx);
             }
 
             return new OpenTelemetrySpanAdapter(builder.startSpan(), baggage);
diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/CamelOpenTelemetryExtension.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/CamelOpenTelemetryExtension.java
index 544018fa27fd..beea4f154bfe 100644
--- a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/CamelOpenTelemetryExtension.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/CamelOpenTelemetryExtension.java
@@ -25,7 +25,6 @@ import java.util.Map;
 import io.opentelemetry.api.GlobalOpenTelemetry;
 import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
-import io.opentelemetry.context.Context;
 import io.opentelemetry.context.propagation.ContextPropagators;
 import io.opentelemetry.instrumentation.log4j.appender.v2_17.OpenTelemetryAppender;
 import io.opentelemetry.sdk.OpenTelemetrySdk;
@@ -40,19 +39,12 @@ import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
 import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
 import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
 import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
-import io.opentelemetry.sdk.trace.ReadWriteSpan;
-import io.opentelemetry.sdk.trace.ReadableSpan;
 import io.opentelemetry.sdk.trace.SdkTracerProvider;
-import io.opentelemetry.sdk.trace.SpanProcessor;
 import io.opentelemetry.sdk.trace.data.SpanData;
 import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
 import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.slf4j.Marker;
-import org.slf4j.MarkerFactory;
 
 /**
  * Adapted from
@@ -68,7 +60,7 @@ final class CamelOpenTelemetryExtension implements BeforeEachCallback, AfterEach
         InMemorySpanExporter spanExporter = InMemorySpanExporter.create();
         SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
                 //.addSpanProcessor(LeakDetectingSpanProcessor.create())
-                .addSpanProcessor(new LoggingSpanProcessor())
+                //.addSpanProcessor(new LoggingSpanProcessor())
                 .addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
                 .build();
 
@@ -167,35 +159,6 @@ final class CamelOpenTelemetryExtension implements BeforeEachCallback, AfterEach
         openTelemetry.close();
     }
 
-    static class LoggingSpanProcessor implements SpanProcessor {
-        private static final Logger LOG = LoggerFactory.getLogger(LoggingSpanProcessor.class);
-        private static final Marker OTEL_MARKER = MarkerFactory.getMarker("OTEL");
-
-        @Override
-        public void onStart(Context context, ReadWriteSpan readWriteSpan) {
-            LOG.info(OTEL_MARKER, "Span started: name - '{}', kind - '{}', id - '{}-{}", readWriteSpan.getName(),
-                    readWriteSpan.getKind(),
-                    readWriteSpan.getSpanContext().getTraceId(), readWriteSpan.getSpanContext().getSpanId());
-        }
-
-        @Override
-        public boolean isStartRequired() {
-            return true;
-        }
-
-        @Override
-        public void onEnd(ReadableSpan readableSpan) {
-            LOG.info(OTEL_MARKER, "Span ended: name - '{}', kind - '{}', id - '{}-{}", readableSpan.getName(),
-                    readableSpan.getKind(),
-                    readableSpan.getSpanContext().getTraceId(), readableSpan.getSpanContext().getSpanId());
-        }
-
-        @Override
-        public boolean isEndRequired() {
-            return true;
-        }
-    }
-
     Map<String, OtelTrace> getTraces() {
         Map<String, OtelTrace> answer = new HashMap<>();
         for (SpanData span : this.getSpans()) {
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanPropagationDownstreamTest.java
similarity index 55%
copy from components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
copy to components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanPropagationDownstreamTest.java
index 194e35e78e07..0c28617a1ffc 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanPropagationDownstreamTest.java
@@ -14,46 +14,42 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.micrometer.observability;
-
-import java.io.IOException;
-import java.util.Map;
+package org.apache.camel.opentelemetry2;
 
 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.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
+public class SpanPropagationDownstreamTest extends OpenTelemetryTracerTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
-        tst.setTraceHeadersInclusion(true);
-        return super.createCamelContext();
+        OpenTelemetryTracer tst = new OpenTelemetryTracer();
+        tst.setTracer(otelExtension.getOpenTelemetry().getTracer("traceTest"));
+        tst.setContextPropagators(otelExtension.getOpenTelemetry().getPropagators());
+        CamelContext context = super.createCamelContext();
+        CamelContextAware.trySetCamelContext(tst, context);
+        tst.init(context);
+        return context;
     }
 
     @Test
-    void testProcessorsTraceRequest() throws InterruptedException, IOException {
+    void testPropagateDownstreamTraceRequest() throws InterruptedException {
         MockEndpoint mock = getMockEndpoint("mock:result");
         mock.expectedMessageCount(1);
-        template.sendBody("direct:start", "my-body");
-        Map<String, MicrometerObservabilityTrace> traces = traces();
-        assertEquals(1, traces.size());
+        template.sendBody("direct:start", "Test");
         mock.assertIsSatisfied();
-        Map<String, Object> headers = mock.getExchanges().get(0).getIn().getHeaders();
-
-        // NOTE: the check on TRACE_ID and SPAN_ID instead of the related constant is on purpose
-        // We want to fail if there is any change in the constant by any chance and report into the
-        // documentation.
-        assertNotNull(headers.get("CAMEL_TRACE_ID"));
-        assertNotNull(headers.get("CAMEL_SPAN_ID"));
-        assertNotEquals("", headers.get("CAMEL_TRACE_ID"));
-        assertNotEquals("", headers.get("CAMEL_SPAN_ID"));
+        mock.getExchanges().forEach(exchange -> {
+            assertTrue(
+                    exchange.getIn().getHeader("traceparent", String.class)
+                            .matches("^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$"),
+                    "The traceparent header does not match with the expected format <version>-<traceid>-<spanid>-<flags>");
+        });
     }
 
     @Override
@@ -62,6 +58,8 @@ public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
             @Override
             public void configure() {
                 from("direct:start")
+                        .routeId("start")
+                        .log("A message")
                         .to("mock:result");
             }
         };
diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanPropagationTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanPropagationUpstreamTest.java
similarity index 97%
rename from components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanPropagationTest.java
rename to components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanPropagationUpstreamTest.java
index 8e45472bf657..3938d39a3662 100644
--- a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanPropagationTest.java
+++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanPropagationUpstreamTest.java
@@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class SpanPropagationTest extends OpenTelemetryTracerTestSupport {
+public class SpanPropagationUpstreamTest extends OpenTelemetryTracerTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
diff --git a/components/camel-opentelemetry2/src/test/resources/log4j2.properties b/components/camel-opentelemetry2/src/test/resources/log4j2.properties
index c75cd8e27dd5..5167c94b70fd 100644
--- a/components/camel-opentelemetry2/src/test/resources/log4j2.properties
+++ b/components/camel-opentelemetry2/src/test/resources/log4j2.properties
@@ -29,17 +29,18 @@ appender.out.layout.pattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
 # to trace.
 # If the rootLogger level is changed, the logging calls on `CamelOpenTelemetryExtension.LoggingSpanProcessor`
 # may need to be adjusted.
-appender.opentelemetry.type=OpenTelemetry
-appender.opentelemetry.name=opentelemetry
-appender.opentelemetry.filter.marker.type=MarkerFilter
-appender.opentelemetry.filter.marker.onMatch=ACCEPT
-appender.opentelemetry.filter.marker.onMismatch=DENY
-appender.opentelemetry.filter.marker.marker=OTEL
-appender.opentelemetry.layout.type=PatternLayout
-appender.opentelemetry.layout.pattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+# appender.opentelemetry.type=OpenTelemetry
+# appender.opentelemetry.name=opentelemetry
+# appender.opentelemetry.filter.marker.type=MarkerFilter
+# appender.opentelemetry.filter.marker.onMatch=ACCEPT
+# appender.opentelemetry.filter.marker.onMismatch=DENY
+# appender.opentelemetry.filter.marker.marker=OTEL
+# appender.opentelemetry.layout.type=PatternLayout
+# appender.opentelemetry.layout.pattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
 
 rootLogger.level=INFO
-rootLogger.appenderRefs=file,out,opentelemetry
-rootLogger.appenderRef.file.ref=file
-#rootLogger.appenderRef.out.ref=out
-rootLogger.appenderRef.opentelemetry.ref=opentelemetry
+rootLogger.appenderRef.file.ref = file
+# rootLogger.appenderRefs=file,out,opentelemetry
+# rootLogger.appenderRef.file.ref=file
+# #rootLogger.appenderRef.out.ref=out
+# rootLogger.appenderRef.opentelemetry.ref=opentelemetry
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationDownstreamTest.java
similarity index 55%
copy from components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
copy to components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationDownstreamTest.java
index 194e35e78e07..897aceaba760 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
+++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationDownstreamTest.java
@@ -14,46 +14,41 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.micrometer.observability;
-
-import java.io.IOException;
-import java.util.Map;
+package org.apache.camel.telemetrydev;
 
 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.test.junit5.ExchangeTestSupport;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
+public class SpanPropagationDownstreamTest extends ExchangeTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
-        tst.setTraceHeadersInclusion(true);
-        return super.createCamelContext();
+        TelemetryDevTracer tst = new TelemetryDevTracer();
+        tst.setTraceFormat("json");
+        CamelContext context = super.createCamelContext();
+        CamelContextAware.trySetCamelContext(tst, context);
+        tst.init(context);
+        return context;
     }
 
     @Test
-    void testProcessorsTraceRequest() throws InterruptedException, IOException {
+    void testPropagateDownstreamTraceRequest() throws InterruptedException {
         MockEndpoint mock = getMockEndpoint("mock:result");
         mock.expectedMessageCount(1);
-        template.sendBody("direct:start", "my-body");
-        Map<String, MicrometerObservabilityTrace> traces = traces();
-        assertEquals(1, traces.size());
+        template.sendBody("direct:start", "Test");
         mock.assertIsSatisfied();
-        Map<String, Object> headers = mock.getExchanges().get(0).getIn().getHeaders();
-
-        // NOTE: the check on TRACE_ID and SPAN_ID instead of the related constant is on purpose
-        // We want to fail if there is any change in the constant by any chance and report into the
-        // documentation.
-        assertNotNull(headers.get("CAMEL_TRACE_ID"));
-        assertNotNull(headers.get("CAMEL_SPAN_ID"));
-        assertNotEquals("", headers.get("CAMEL_TRACE_ID"));
-        assertNotEquals("", headers.get("CAMEL_SPAN_ID"));
+        mock.getExchanges().forEach(exchange -> {
+            assertTrue(
+                    exchange.getIn().getHeader("traceparent", String.class).matches("^[a-z0-9]+-[a-z0-9]+$"),
+                    "The traceparent header does not match with the expected format <traceid>-<spanid>");
+        });
     }
 
     @Override
@@ -62,6 +57,8 @@ public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
             @Override
             public void configure() {
                 from("direct:start")
+                        .routeId("start")
+                        .log("A message")
                         .to("mock:result");
             }
         };
diff --git a/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationTest.java b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationUpstreamTest.java
similarity index 97%
rename from components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationTest.java
rename to components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationUpstreamTest.java
index 4df5bbb70f98..19af81965cc1 100644
--- a/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationTest.java
+++ b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationUpstreamTest.java
@@ -28,7 +28,7 @@ import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-public class SpanPropagationTest extends TelemetryDevTracerTestSupport {
+public class SpanPropagationUpstreamTest extends TelemetryDevTracerTestSupport {
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
diff --git a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanPropagationDownstreamTest.java
similarity index 55%
copy from components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
copy to components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanPropagationDownstreamTest.java
index 194e35e78e07..fdafde816f5c 100644
--- a/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
+++ b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanPropagationDownstreamTest.java
@@ -14,46 +14,43 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.micrometer.observability;
-
-import java.io.IOException;
-import java.util.Map;
+package org.apache.camel.telemetry;
 
 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.telemetry.mock.MockTracer;
+import org.apache.camel.test.junit5.ExchangeTestSupport;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SpanPropagationDownstreamTest extends ExchangeTestSupport {
 
-public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
+    MockTracer mockTracer;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
-        tst.setTraceHeadersInclusion(true);
-        return super.createCamelContext();
+        CamelContext context = super.createCamelContext();
+        this.mockTracer = new MockTracer();
+        CamelContextAware.trySetCamelContext(mockTracer, context);
+        mockTracer.init(context);
+        return context;
     }
 
     @Test
-    void testProcessorsTraceRequest() throws InterruptedException, IOException {
+    void testPropagateDownstreamTraceRequest() throws InterruptedException {
         MockEndpoint mock = getMockEndpoint("mock:result");
         mock.expectedMessageCount(1);
-        template.sendBody("direct:start", "my-body");
-        Map<String, MicrometerObservabilityTrace> traces = traces();
-        assertEquals(1, traces.size());
+        template.sendBody("direct:start", "Test");
         mock.assertIsSatisfied();
-        Map<String, Object> headers = mock.getExchanges().get(0).getIn().getHeaders();
-
-        // NOTE: the check on TRACE_ID and SPAN_ID instead of the related constant is on purpose
-        // We want to fail if there is any change in the constant by any chance and report into the
-        // documentation.
-        assertNotNull(headers.get("CAMEL_TRACE_ID"));
-        assertNotNull(headers.get("CAMEL_SPAN_ID"));
-        assertNotEquals("", headers.get("CAMEL_TRACE_ID"));
-        assertNotEquals("", headers.get("CAMEL_SPAN_ID"));
+        mock.getExchanges().forEach(exchange -> {
+            assertTrue(
+                    exchange.getIn().getHeader("traceparent", String.class).matches("^[a-z0-9]+-[a-z0-9]+$"),
+                    "The traceparent header does not match with the expected format <traceid>-<spanid>");
+        });
     }
 
     @Override
@@ -62,6 +59,8 @@ public class HeadersTraceTest extends MicrometerObservabilityTracerTestSupport {
             @Override
             public void configure() {
                 from("direct:start")
+                        .routeId("start")
+                        .log("A message")
                         .to("mock:result");
             }
         };
diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanPropagationTest.java b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanPropagationUpstreamTest.java
similarity index 98%
rename from components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanPropagationTest.java
rename to components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanPropagationUpstreamTest.java
index 0863dddde00e..0543668f48ad 100644
--- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanPropagationTest.java
+++ b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/SpanPropagationUpstreamTest.java
@@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-public class SpanPropagationTest extends ExchangeTestSupport {
+public class SpanPropagationUpstreamTest extends ExchangeTestSupport {
 
     MockTracer mockTracer;
 
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 c1ea641fd9b9..48e0467b9612 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
@@ -90,9 +90,9 @@ public class MockTracer extends Tracer {
 
         @Override
         public void inject(Span span, SpanContextPropagationInjector injector, boolean includeTracing) {
-            injector.put("traceparent", span.toString());
+            MockSpanAdapter msa = (MockSpanAdapter) span;
+            injector.put("traceparent", msa.getTag("traceid") + "-" + msa.getTag("spanid"));
             if (includeTracing) {
-                MockSpanAdapter msa = (MockSpanAdapter) span;
                 injector.put(Tracer.TRACE_HEADER, msa.getTag("traceid"));
                 injector.put(Tracer.SPAN_HEADER, msa.getTag("spanid"));
             }
diff --git a/proposals/tracing.adoc b/proposals/tracing.adoc
index 3001fa16496b..5a45ce113f57 100644
--- a/proposals/tracing.adoc
+++ b/proposals/tracing.adoc
@@ -9,7 +9,7 @@ approvers:
   - "@zbendhiba"
   - "@davsclaus"
 creation-date: 2025-01-08
-last-updated: 2025-03-21
+last-updated: 2025-11-07
 status: implemented
 see-also: []
 replaces: []
@@ -92,7 +92,7 @@ Beside the span lifecycle we will need to consider a few more aspects:
 
 The **span decoration** is a Camel specific way of decorating the different components we handle with specific traces information. As an example, when you're using Kafka component, you will get automatically in the trace useful configuration as the offset or the partition. We already have this mechanism in place and we should make sure to have a clear documentation stating about this particular feature.
 
-The **Context propagation** is a way to correlate distributed traces between each other. It works reading a `traceparent` header on the Exchange and using it to correlate to a chain of distributed requests. It's important to notice that the specific propagation mechanism belong to the implementation, so we will need to provide in the component the required level of abstraction.
+The **Context propagation** is a way to correlate distributed traces between each other. It works reading a `traceparent` header on the Exchange and using it to correlate to a chain of distributed requests. It's important to notice that the specific propagation mechanism belong to the implementation, so we will need to provide in the component the required level of abstraction (see Context Propagation chapter).
 
 === Tracing storage
 
@@ -100,6 +100,22 @@ The Exchange stack storage already exists and it may suffice to this proposal go
 
 In order to clarify this aspect, let's take `camel-opentelemetry` as an example. When we call the *activation* method, then, we must make sure that the span passed is correctly activated, calling therefore the `span.makeCurrent()` method. The generated scope has therefore to be kept in the same span wrapper in order to be later closed when the *closure* method is called via `scope.close()`. As each span wrapper is stored in the Exchange, then we can use this approach to maintain the stat [...]
 
+=== Context propagation
+
+Each telemetry implementations may provide a different way of managing context propagation. However, from Camel perspective, this is managed consistently across the different implementations by the consumption of upstream headers and the generation of downstream headers accordingly.
+
+```
+┌───────────────────────┐     ┌───────────────────────┐     ┌───────────────────────┐
+│   Upstream Component  │ ──> │        Camel          │ ──> │   Third-Party System  │
+│ (sends traceparent)   │     │ consumes & injects    │     │ (receives traceparent)│
+└───────────────────────┘     └───────────────────────┘     └───────────────────────┘
+            traceparent ───────────────────────────────────────────────────────────▶
+```
+
+For this reason, each telemetry implementations will need to **extract the expected header** before generating a new trace. This is typically happening during the Trace/Span creation (ie, method `create()`) if no parent span still exists. In a similar fashion, the telemetry component must implements an `inject()` method in order to pass to any downstream system the origin of the trace. This mechanism will also serve to make different Camel systems properly exchange the traces among them.
+
+It's important therefore that any upstream component (java libraries, agents, ...) implements properly the given context propagation technology specification. The de-facto standard is the https://www.w3.org/TR/trace-context/[W3C Trace Context specification] which is the one used in Opentelemetry based telemetry components. Camel Opentelemetry telemetry components must be compliant to the standard.
+
 === Tracing simple implementation (mock)
 
 If we move most of the logic into the abstraction, the implementation of a simple implementation should be straightforward. We can expect this implementation in charge to implement the abstraction methods provided in the "tracing lifecycle" section, which can be some simple UUID generation and the tracing into MDC variables in order to simply log them in the application log. No push/pull to any collector is expected and this implementation would serve more as a way to debug the abstracti [...]
@@ -133,3 +149,7 @@ Developed concrete Micrometer Observability component implementing the `camel-te
 === Inclusion of generated traces headers (2025-10-21)
 
 A new feature is available to add `TRACE_ID` and `SPAN_ID` Exchange headers. This is very useful in conjunction with MDC header usage. From now on we have a consistent setting of generated telemetry traces consumable from MDC mechanism.
+
+=== Design clarification about the adoption of W3C trace context (2025-11-07)
+
+Added a chapter to clarify the adoption and compliancy of W3C trace context of Camel for trace context propagation.