This is an automated email from the ASF dual-hosted git repository.
pcongiusti 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 b176cf9a4fb2 feat(components): telemetry tracing info in headers
b176cf9a4fb2 is described below
commit b176cf9a4fb28ff61f63aa24952936934e511238
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Tue Oct 21 12:47:11 2025 +0200
feat(components): telemetry tracing info in headers
This feature will simplify the exposure of the generated traces information
giving a consisten solution across any Camel telemetry component.
Closes CAMEL-22478
---
.../MicrometerObservabilityTracerConfigurer.java | 6 ++
.../src/main/docs/micrometer-observability.adoc | 7 ++
.../MicrometerObservabilityTracer.java | 6 +-
.../micrometer/observability/HeadersTraceTest.java | 70 +++++++++++++++++++
.../OpenTelemetryTracerConfigurer.java | 6 ++
.../src/main/docs/opentelemetry2.adoc | 8 +++
.../camel/opentelemetry2/OpenTelemetryTracer.java | 6 +-
.../camel/opentelemetry2/MDCHeadersTraceTest.java | 78 +++++++++++++++++++++
.../telemetrydev/TelemetryDevTracerConfigurer.java | 6 ++
.../src/main/docs/telemetry-dev.adoc | 13 ++--
.../camel/telemetrydev/TelemetryDevTracer.java | 7 +-
.../camel/telemetrydev/MDCHeadersTraceTest.java | 76 +++++++++++++++++++++
.../camel-telemetry/src/main/docs/telemetry.adoc | 9 ++-
.../camel/telemetry/SpanLifecycleManager.java | 2 +-
.../java/org/apache/camel/telemetry/Tracer.java | 15 +++-
.../apache/camel/telemetry/HeadersTraceTest.java | 79 ++++++++++++++++++++++
.../apache/camel/telemetry/mock/MockTracer.java | 7 +-
proposals/tracing.adoc | 10 +--
18 files changed, 394 insertions(+), 17 deletions(-)
diff --git
a/components/camel-micrometer-observability/src/generated/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerConfigurer.java
b/components/camel-micrometer-observability/src/generated/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerConfigurer.java
index b8aba43c8641..81a5e30fb4b1 100644
---
a/components/camel-micrometer-observability/src/generated/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerConfigurer.java
+++
b/components/camel-micrometer-observability/src/generated/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracerConfigurer.java
@@ -32,6 +32,8 @@ public class MicrometerObservabilityTracerConfigurer extends
org.apache.camel.su
case "propagator": target.setPropagator(property(camelContext,
io.micrometer.tracing.propagation.Propagator.class, value)); return true;
case "spanlifecyclemanager":
case "spanLifecycleManager":
target.setSpanLifecycleManager(property(camelContext,
org.apache.camel.telemetry.SpanLifecycleManager.class, value)); return true;
+ case "traceheadersinclusion":
+ case "traceHeadersInclusion":
target.setTraceHeadersInclusion(property(camelContext, boolean.class, value));
return true;
case "traceprocessors":
case "traceProcessors":
target.setTraceProcessors(property(camelContext, boolean.class, value)); return
true;
case "tracer": target.setTracer(property(camelContext,
io.micrometer.tracing.Tracer.class, value)); return true;
@@ -51,6 +53,8 @@ public class MicrometerObservabilityTracerConfigurer extends
org.apache.camel.su
case "propagator": return
io.micrometer.tracing.propagation.Propagator.class;
case "spanlifecyclemanager":
case "spanLifecycleManager": return
org.apache.camel.telemetry.SpanLifecycleManager.class;
+ case "traceheadersinclusion":
+ case "traceHeadersInclusion": return boolean.class;
case "traceprocessors":
case "traceProcessors": return boolean.class;
case "tracer": return io.micrometer.tracing.Tracer.class;
@@ -71,6 +75,8 @@ public class MicrometerObservabilityTracerConfigurer extends
org.apache.camel.su
case "propagator": return target.getPropagator();
case "spanlifecyclemanager":
case "spanLifecycleManager": return target.getSpanLifecycleManager();
+ case "traceheadersinclusion":
+ case "traceHeadersInclusion": return target.isTraceHeadersInclusion();
case "traceprocessors":
case "traceProcessors": return target.isTraceProcessors();
case "tracer": return target.getTracer();
diff --git
a/components/camel-micrometer-observability/src/main/docs/micrometer-observability.adoc
b/components/camel-micrometer-observability/src/main/docs/micrometer-observability.adoc
index a3fde2f5d5b7..e502d493ee54 100644
---
a/components/camel-micrometer-observability/src/main/docs/micrometer-observability.adoc
+++
b/components/camel-micrometer-observability/src/main/docs/micrometer-observability.adoc
@@ -31,6 +31,7 @@ The configuration properties for the component are:
|`excludePatterns` | | Sets exclude pattern that will disable tracing for
those spans that matches the pattern. The variable is a comma separated values
of filters to execute (eg, `log*,direct*,setBody*`, ...)
|`traceProcessors` | `false` | Setting this to true will create new spans for
each Camel Processors.
Use the excludePattern property to filter out Processors
+|`traceHeadersInclusion`| false | Add the generated telemetry `CAMEL_TRACE_ID`
and `CAMEL_SPAN_ID` Exchange headers.
|=======================================================================
include::spring-boot:partial$starter.adoc[]
@@ -133,3 +134,9 @@ Once the application is instrumented and configured, you
can observe the traces
=== Java Agents
Your application may require a Java agent in order to get the traces generated
by the Camel application and push to the tracing server (ie, Opentelemetry
based instrumentation). You may need to configure such agent (or any other
tool) directly via Java parameters.
+
+=== MDC logging
+
+You can leverage the `traceHeadersInclusion` to include the generated
`CAMEL_TRACE_ID` and `CAMEL_SPAN_ID` into the Camel Exchange and together with
`camel-mdc` you can make those headers available in the MDC context (via
`camel.mdc.customHeaders=CAMEL_TRACE_ID,CAMEL_SPAN_ID` configuration). This is
the idiomatic way in Camel.
+
+As an alternative, you can add Mapped Diagnostic Context tracing information
(ie, `trace_id` and `span_id`) adding the specific
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/logger-mdc-instrumentation.md[Opentelemetry
Logger MDC auto instrumentation]. This would be available if you configure the
Opentelemetry. The logging configuration depends on the logging framework
you're using.
\ No newline at end of file
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 a581849815af..fa239a258558 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
@@ -172,12 +172,16 @@ public class MicrometerObservabilityTracer extends
org.apache.camel.telemetry.Tr
}
@Override
- public void inject(Span span, SpanContextPropagationInjector injector)
{
+ public void inject(Span span, SpanContextPropagationInjector injector,
boolean includeTracing) {
MicrometerObservabilitySpanAdapter microObsSpan =
(MicrometerObservabilitySpanAdapter) span;
propagator.inject(
microObsSpan.getSpan().context(),
injector,
(carrier, key, value) -> carrier.put(key, value));
+ if (includeTracing) {
+ injector.put(org.apache.camel.telemetry.Tracer.TRACE_HEADER,
microObsSpan.getSpan().context().traceId());
+ injector.put(org.apache.camel.telemetry.Tracer.SPAN_HEADER,
microObsSpan.getSpan().context().spanId());
+ }
}
}
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
new file mode 100644
index 000000000000..194e35e78e07
--- /dev/null
+++
b/components/camel-micrometer-observability/src/test/java/org/apache/camel/micrometer/observability/HeadersTraceTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.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
{
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ tst.setTraceHeadersInclusion(true);
+ return super.createCamelContext();
+ }
+
+ @Test
+ void testProcessorsTraceRequest() throws InterruptedException, IOException
{
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(1);
+ template.sendBody("direct:start", "my-body");
+ Map<String, MicrometerObservabilityTrace> traces = traces();
+ assertEquals(1, traces.size());
+ 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"));
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("mock:result");
+ }
+ };
+ }
+
+}
diff --git
a/components/camel-opentelemetry2/src/generated/java/org/apache/camel/opentelemetry2/OpenTelemetryTracerConfigurer.java
b/components/camel-opentelemetry2/src/generated/java/org/apache/camel/opentelemetry2/OpenTelemetryTracerConfigurer.java
index 1e97cb58200d..cc561a6e9841 100644
---
a/components/camel-opentelemetry2/src/generated/java/org/apache/camel/opentelemetry2/OpenTelemetryTracerConfigurer.java
+++
b/components/camel-opentelemetry2/src/generated/java/org/apache/camel/opentelemetry2/OpenTelemetryTracerConfigurer.java
@@ -29,6 +29,8 @@ public class OpenTelemetryTracerConfigurer extends
org.apache.camel.support.comp
case "excludePatterns":
target.setExcludePatterns(property(camelContext, java.lang.String.class,
value)); return true;
case "spanlifecyclemanager":
case "spanLifecycleManager":
target.setSpanLifecycleManager(property(camelContext,
org.apache.camel.telemetry.SpanLifecycleManager.class, value)); return true;
+ case "traceheadersinclusion":
+ case "traceHeadersInclusion":
target.setTraceHeadersInclusion(property(camelContext, boolean.class, value));
return true;
case "traceprocessors":
case "traceProcessors":
target.setTraceProcessors(property(camelContext, boolean.class, value)); return
true;
default: return false;
@@ -44,6 +46,8 @@ public class OpenTelemetryTracerConfigurer extends
org.apache.camel.support.comp
case "excludePatterns": return java.lang.String.class;
case "spanlifecyclemanager":
case "spanLifecycleManager": return
org.apache.camel.telemetry.SpanLifecycleManager.class;
+ case "traceheadersinclusion":
+ case "traceHeadersInclusion": return boolean.class;
case "traceprocessors":
case "traceProcessors": return boolean.class;
default: return null;
@@ -60,6 +64,8 @@ public class OpenTelemetryTracerConfigurer extends
org.apache.camel.support.comp
case "excludePatterns": return target.getExcludePatterns();
case "spanlifecyclemanager":
case "spanLifecycleManager": return target.getSpanLifecycleManager();
+ case "traceheadersinclusion":
+ case "traceHeadersInclusion": return target.isTraceHeadersInclusion();
case "traceprocessors":
case "traceProcessors": return target.isTraceProcessors();
default: return null;
diff --git a/components/camel-opentelemetry2/src/main/docs/opentelemetry2.adoc
b/components/camel-opentelemetry2/src/main/docs/opentelemetry2.adoc
index 1d92110a40ba..960f56b1ba4a 100644
--- a/components/camel-opentelemetry2/src/main/docs/opentelemetry2.adoc
+++ b/components/camel-opentelemetry2/src/main/docs/opentelemetry2.adoc
@@ -25,6 +25,7 @@ The configuration properties for the OpenTelemetry2 tracer
are:
|`excludePatterns` | | Sets exclude pattern that will disable tracing for
those spans that matches the pattern. The variable is a comma separated values
of filters to execute (eg, `log*,direct*,setBody*`, ...)
|`traceProcessors` | `false` | Setting this to true will create new
OpenTelemetry Spans for each Camel Processors.
Use the excludePattern property to filter out Processors
+|`traceHeadersInclusion`| false | Add the generated telemetry `CAMEL_TRACE_ID`
and `CAMEL_SPAN_ID` Exchange headers.
|=======================================================================
=== Using with standalone Camel
@@ -80,3 +81,10 @@ java -javaagent:path/to/opentelemetry-javaagent.jar \
=== Collect OpenTelemetry traces
OpenTelemetry is a tracing protocol which is implemented by several vendors.
You can use the Jaeger project which provides an open source all in one tracing
application. See details how to run it in
https://www.jaegertracing.io/docs/latest/getting-started/[Jaeger getting
started guide].
+
+=== MDC logging
+
+You can leverage the `traceHeadersInclusion` to include the generated
`CAMEL_TRACE_ID` and `CAMEL_SPAN_ID` into the Camel Exchange and together with
`camel-mdc` you can make those headers available in the MDC context (via
`camel.mdc.customHeaders=CAMEL_TRACE_ID,CAMEL_SPAN_ID` configuration). This is
the idiomatic way in Camel.
+
+As an alternative, you can add Mapped Diagnostic Context tracing information
(ie, `trace_id` and `span_id`) adding the specific
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/logger-mdc-instrumentation.md[Opentelemetry
Logger MDC auto instrumentation]. The logging configuration depends on the
logging framework you're using.
+
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 2ee1b76c3cc1..e1d0cefd52fd 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
@@ -154,7 +154,7 @@ public class OpenTelemetryTracer extends
org.apache.camel.telemetry.Tracer {
}
@Override
- public void inject(Span span, SpanContextPropagationInjector injector)
{
+ public void inject(Span span, SpanContextPropagationInjector injector,
boolean includeTracing) {
OpenTelemetrySpanAdapter otelSpan = (OpenTelemetrySpanAdapter)
span;
Context ctx = Context.current().with(otelSpan.getSpan());
if (otelSpan.getBaggage() != null) {
@@ -162,6 +162,10 @@ public class OpenTelemetryTracer extends
org.apache.camel.telemetry.Tracer {
}
contextPropagators.getTextMapPropagator().inject(ctx, injector,
(carrier, key, value) -> carrier.put(key, value));
+ if (includeTracing) {
+ injector.put(org.apache.camel.telemetry.Tracer.TRACE_HEADER,
otelSpan.getSpan().getSpanContext().getTraceId());
+ injector.put(org.apache.camel.telemetry.Tracer.SPAN_HEADER,
otelSpan.getSpan().getSpanContext().getSpanId());
+ }
}
}
diff --git
a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/MDCHeadersTraceTest.java
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/MDCHeadersTraceTest.java
new file mode 100644
index 000000000000..0eab0a484a07
--- /dev/null
+++
b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/MDCHeadersTraceTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.io.IOException;
+import java.util.Map;
+
+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.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 MDCHeadersTraceTest extends OpenTelemetryTracerTestSupport {
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ CamelContext context = super.createCamelContext();
+ OpenTelemetryTracer tst = new OpenTelemetryTracer();
+ tst.setTraceHeadersInclusion(true);
+ tst.setTracer(otelExtension.getOpenTelemetry().getTracer("traceTest"));
+
tst.setContextPropagators(otelExtension.getOpenTelemetry().getPropagators());
+ CamelContextAware.trySetCamelContext(tst, context);
+ tst.init(context);
+ return context;
+ }
+
+ @Test
+ void testProcessorsTraceRequest() throws InterruptedException, IOException
{
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(1);
+ template.sendBody("direct:start", "my-body");
+ Map<String, OtelTrace> traces = otelExtension.getTraces();
+ assertEquals(1, traces.size());
+ 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"));
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("mock:result");
+ }
+ };
+ }
+
+}
diff --git
a/components/camel-telemetry-dev/src/generated/java/org/apache/camel/telemetrydev/TelemetryDevTracerConfigurer.java
b/components/camel-telemetry-dev/src/generated/java/org/apache/camel/telemetrydev/TelemetryDevTracerConfigurer.java
index f2be9dfb3ea1..20d9f04a5fee 100644
---
a/components/camel-telemetry-dev/src/generated/java/org/apache/camel/telemetrydev/TelemetryDevTracerConfigurer.java
+++
b/components/camel-telemetry-dev/src/generated/java/org/apache/camel/telemetrydev/TelemetryDevTracerConfigurer.java
@@ -31,6 +31,8 @@ public class TelemetryDevTracerConfigurer extends
org.apache.camel.support.compo
case "spanLifecycleManager":
target.setSpanLifecycleManager(property(camelContext,
org.apache.camel.telemetry.SpanLifecycleManager.class, value)); return true;
case "traceformat":
case "traceFormat": target.setTraceFormat(property(camelContext,
java.lang.String.class, value)); return true;
+ case "traceheadersinclusion":
+ case "traceHeadersInclusion":
target.setTraceHeadersInclusion(property(camelContext, boolean.class, value));
return true;
case "traceprocessors":
case "traceProcessors":
target.setTraceProcessors(property(camelContext, boolean.class, value)); return
true;
default: return false;
@@ -48,6 +50,8 @@ public class TelemetryDevTracerConfigurer extends
org.apache.camel.support.compo
case "spanLifecycleManager": return
org.apache.camel.telemetry.SpanLifecycleManager.class;
case "traceformat":
case "traceFormat": return java.lang.String.class;
+ case "traceheadersinclusion":
+ case "traceHeadersInclusion": return boolean.class;
case "traceprocessors":
case "traceProcessors": return boolean.class;
default: return null;
@@ -66,6 +70,8 @@ public class TelemetryDevTracerConfigurer extends
org.apache.camel.support.compo
case "spanLifecycleManager": return target.getSpanLifecycleManager();
case "traceformat":
case "traceFormat": return target.getTraceFormat();
+ case "traceheadersinclusion":
+ case "traceHeadersInclusion": return target.isTraceHeadersInclusion();
case "traceprocessors":
case "traceProcessors": return target.isTraceProcessors();
default: return null;
diff --git a/components/camel-telemetry-dev/src/main/docs/telemetry-dev.adoc
b/components/camel-telemetry-dev/src/main/docs/telemetry-dev.adoc
index 4d75d0424a58..d3d9894814be 100644
--- a/components/camel-telemetry-dev/src/main/docs/telemetry-dev.adoc
+++ b/components/camel-telemetry-dev/src/main/docs/telemetry-dev.adoc
@@ -26,10 +26,11 @@ The configuration properties for the Telemetry component
are:
[width="100%",cols="10%,10%,80%",options="header",]
|=======================================================================
|Option |Default |Description
-|`camel.telemetryDev.enabled`| false | Turn the tracing on/off.
-|`camel.telemetryDev.traceFormat`| default | The format used to trace in the
log (default, tree, json).
-|`camel.telemetryDev.traceProcessors`| false | Trace inner processors.
-|`camel.telemetryDev.excludePatterns` | | Sets exclude pattern that will
disable tracing for those spans that matches the pattern. The variable is a
comma separated values of filters to execute (eg, `log*,direct*,setBody*`, ...)
+|`enabled`| false | Turn the tracing on/off.
+|`traceFormat`| default | The format used to trace in the log (default, tree,
json).
+|`traceProcessors`| false | Trace inner processors.
+|`excludePatterns` | | Sets exclude pattern that will disable tracing for
those spans that matches the pattern. The variable is a comma separated values
of filters to execute (eg, `log*,direct*,setBody*`, ...)
+|`traceHeadersInclusion`| false | Add the generated telemetry `CAMEL_TRACE_ID`
and `CAMEL_SPAN_ID` Exchange headers.
|=======================================================================
== Tracing format
@@ -72,3 +73,7 @@ This format is suitable if you need to integrate with any
third party. The outpu
=== Default
Default is a very simple serialization into the default Java object
`toString()` representation.
+
+=== MDC logging
+
+You can leverage the `traceHeadersInclusion` to include the generated
`CAMEL_TRACE_ID` and `CAMEL_SPAN_ID` into the Camel Exchange and together with
`camel-mdc` you can make those headers available in the MDC context (via
`camel.mdc.customHeaders=CAMEL_TRACE_ID,CAMEL_SPAN_ID` configuration).
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 3107b1777397..f4d0cdee05a8 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
@@ -136,13 +136,18 @@ public class TelemetryDevTracer extends Tracer {
}
@Override
- public void inject(Span span, SpanContextPropagationInjector injector)
{
+ public void inject(Span span, SpanContextPropagationInjector injector,
boolean includeTracing) {
String[] split = span.toString().split("-");
if (split.length < 2) {
LOG.error("TRACE ERROR: wrong format, could not split
traceparent {}", span);
return;
}
injector.put("traceparent", split[0] + "-" + split[1]);
+ if (includeTracing) {
+ DevSpanAdapter spanAdapter = (DevSpanAdapter) span;
+ injector.put(Tracer.TRACE_HEADER,
spanAdapter.getTag("traceid"));
+ injector.put(Tracer.SPAN_HEADER, spanAdapter.getTag("spanid"));
+ }
}
}
diff --git
a/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/MDCHeadersTraceTest.java
b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/MDCHeadersTraceTest.java
new file mode 100644
index 000000000000..2e9472aece6b
--- /dev/null
+++
b/components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/MDCHeadersTraceTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.telemetrydev;
+
+import java.io.IOException;
+import java.util.Map;
+
+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;
+
+public class MDCHeadersTraceTest extends TelemetryDevTracerTestSupport {
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ CamelContext context = super.createCamelContext();
+ TelemetryDevTracer tst = new TelemetryDevTracer();
+ tst.setTraceFormat("json");
+ tst.setTraceHeadersInclusion(true);
+ CamelContextAware.trySetCamelContext(tst, context);
+ tst.init(context);
+ return context;
+ }
+
+ @Test
+ void testProcessorsTraceRequest() throws InterruptedException, IOException
{
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(1);
+ template.sendBody("direct:start", "my-body");
+ Map<String, DevTrace> traces = tracesFromLog();
+ assertEquals(1, traces.size());
+ 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"));
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("mock:result");
+ }
+ };
+ }
+
+}
diff --git a/components/camel-telemetry/src/main/docs/telemetry.adoc
b/components/camel-telemetry/src/main/docs/telemetry.adoc
index 908128ec5691..6daad9776f25 100644
--- a/components/camel-telemetry/src/main/docs/telemetry.adoc
+++ b/components/camel-telemetry/src/main/docs/telemetry.adoc
@@ -26,8 +26,9 @@ The configuration properties for the Telemetry component are:
[width="100%",cols="10%,10%,80%",options="header",]
|=======================================================================
|Option |Default |Description
-|`camel.component.telemetry.traceProcessors`| false | Trace inner processors.
-|`camel.component.telemetry.excludePatterns` | | Sets exclude pattern that
will disable tracing for those spans that matches the pattern. The variable is
a comma separated values of filters to execute (eg, `log*,direct*,setBody*`,
...)
+|`traceProcessors`| false | Trace inner processors.
+|`excludePatterns` | | Sets exclude pattern that will disable tracing for
those spans that matches the pattern. The variable is a comma separated values
of filters to execute (eg, `log*,direct*,setBody*`, ...)
+|`traceHeadersInclusion`| false | Add the generated telemetry `CAMEL_TRACE_ID`
and `CAMEL_SPAN_ID` Exchange headers.
|=======================================================================
== Tracing structure
@@ -84,6 +85,10 @@ Camel uses a proprietary storage mechanism. The trace is
serialized into each ge
The component provide the possibility to exclude the trace of any component
when using the `excludePatterns` parameter. This feature is not implementation
specific.
+=== Exchange headers inclusion
+
+The component provide the possibility to include the generated `trace` and
`span` into the Exchange header. These headers can be then used for any
observability purposes, eg, included in MDC via `camel-mdc` component.
+
=== Component Span decoration
The component automatically includes certain useful parameter out of the box
for the different components you may use within Camel. As an example, if you're
using the Kafka component (`camel-kafka`), then, it will include in the Kafka
endpoint span a few useful information such as *partition* or *offset* which
you will be able to verify later in the trace collector.
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 3abf2940a522..501dd2430f20 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
@@ -29,6 +29,6 @@ public interface SpanLifecycleManager {
void close(Span span);
- void inject(Span span, SpanContextPropagationInjector injector);
+ void inject(Span span, SpanContextPropagationInjector injector, boolean
includeTracing);
}
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 eb608a08434f..514fd2044565 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
@@ -45,6 +45,9 @@ import org.slf4j.LoggerFactory;
public abstract class Tracer extends ServiceSupport implements
CamelTracingService, RoutePolicyFactory, StaticService {
+ public static final String TRACE_HEADER = "CAMEL_TRACE_ID";
+ public static final String SPAN_HEADER = "CAMEL_SPAN_ID";
+
private static final Logger LOG = LoggerFactory.getLogger(Tracer.class);
private CamelContext camelContext;
@@ -53,6 +56,7 @@ public abstract class Tracer extends ServiceSupport
implements CamelTracingServi
*/
private String excludePatterns;
private boolean traceProcessors;
+ private boolean traceHeadersInclusion;
private final TracingEventNotifier eventNotifier = new
TracingEventNotifier();
private final SpanStorageManager spanStorageManager = new
SpanStorageManagerExchange();
@@ -84,6 +88,15 @@ public abstract class Tracer extends ServiceSupport
implements CamelTracingServi
this.excludePatterns = excludePatterns;
}
+ @ManagedAttribute
+ public boolean isTraceHeadersInclusion() {
+ return traceHeadersInclusion;
+ }
+
+ public void setTraceHeadersInclusion(boolean traceHeadersInclusion) {
+ this.traceHeadersInclusion = traceHeadersInclusion;
+ }
+
@ManagedAttribute
public boolean isTraceProcessors() {
return traceProcessors;
@@ -262,7 +275,7 @@ public abstract class Tracer extends ServiceSupport
implements CamelTracingServi
spanDecorator.beforeTracingEvent(span, exchange, endpoint);
spanLifecycleManager.activate(span);
spanStorageManager.push(exchange, span);
- spanLifecycleManager.inject(span, spanDecorator.getInjector(exchange));
+ spanLifecycleManager.inject(span, spanDecorator.getInjector(exchange),
this.traceHeadersInclusion);
LOG.debug("Started event span: {}", span);
}
diff --git
a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/HeadersTraceTest.java
b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/HeadersTraceTest.java
new file mode 100644
index 000000000000..06358fb2ad65
--- /dev/null
+++
b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/HeadersTraceTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.telemetry;
+
+import java.util.Map;
+
+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.MockTrace;
+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;
+
+public class HeadersTraceTest extends ExchangeTestSupport {
+
+ MockTracer mockTracer;
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ CamelContext context = super.createCamelContext();
+ this.mockTracer = new MockTracer();
+ mockTracer.setTraceHeadersInclusion(true);
+ CamelContextAware.trySetCamelContext(mockTracer, context);
+ mockTracer.init(context);
+ return context;
+ }
+
+ @Test
+ void testProcessorsTraceRequest() throws InterruptedException {
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(1);
+ template.sendBody("direct:start", "my-body");
+ Map<String, MockTrace> traces = mockTracer.traces();
+ assertEquals(1, traces.size());
+ 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"));
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("mock:result");
+ }
+ };
+ }
+
+}
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 3301334ecef7..c1ea641fd9b9 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
@@ -89,8 +89,13 @@ public class MockTracer extends Tracer {
}
@Override
- public void inject(Span span, SpanContextPropagationInjector injector)
{
+ public void inject(Span span, SpanContextPropagationInjector injector,
boolean includeTracing) {
injector.put("traceparent", span.toString());
+ if (includeTracing) {
+ MockSpanAdapter msa = (MockSpanAdapter) span;
+ injector.put(Tracer.TRACE_HEADER, msa.getTag("traceid"));
+ injector.put(Tracer.SPAN_HEADER, msa.getTag("spanid"));
+ }
}
public Map<String, MockTrace> traces() {
diff --git a/proposals/tracing.adoc b/proposals/tracing.adoc
index 1c41d3e9b805..3001fa16496b 100644
--- a/proposals/tracing.adoc
+++ b/proposals/tracing.adoc
@@ -108,10 +108,6 @@ If we move most of the logic into the abstraction, the
implementation of a simpl
The feature specific implementation should be therefore limited to the
implementation of the abstract methods, as it would happen in the simple
implementation. With this approach we are limiting to the bare minimum the
maintenance of each specific technology. With this proposal we will need to
rework massively on the reduction of code in the existing implementations
(`camel-opentelemetry`).
-== Tracing refactoring POC
-
-In order to prove most of the above assumptions, I've developed a simple POC
which I used as a
https://github.com/squakez/camel/tree/feat/tracing_refactoring[base for this
proposal]. Testing this against some application, we can see traces are managed
correctly and in line with the structure proposed in this document.
-
== Development
This design proposals may introduce certain breaking compatibility changes,
reason why we must clarify the scope and plan the work in order to avoid adding
breaking compatibility within any non major version. We may work by adding a
new abstract component which will be compliant with this new specification and
once the new development is stable enough, we can deprecate the older
`camel-tracing` and let the user replace with the newer one.
@@ -132,4 +128,8 @@ Developed concrete OpenTelemetry component implementing the
`camel-telemetry` sp
=== `camel-micrometer-observability` component (2025-08-21)
-Developed concrete Micrometer Observability component implementing the
`camel-telemetry` specification. This component will eventually replace
`camel-observation` component.
\ No newline at end of file
+Developed concrete Micrometer Observability component implementing the
`camel-telemetry` specification. This component will eventually replace
`camel-observation` component.
+
+=== 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.