diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/mdc.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/mdc.json
index 21670e7e2e74a..d83aea8af6ecc 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/mdc.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/mdc.json
@@ -2,7 +2,7 @@
"other": {
"kind": "other",
"name": "mdc",
- "title": "Mdc",
+ "title": "MDC Logging",
"description": "Logging MDC (Mapped Diagnostic Context) Service",
"deprecated": false,
"firstVersion": "4.15.0",
diff --git a/components/camel-mdc/pom.xml b/components/camel-mdc/pom.xml
index 353aa413150ca..000db0227b947 100644
--- a/components/camel-mdc/pom.xml
+++ b/components/camel-mdc/pom.xml
@@ -31,6 +31,7 @@
4.15.0
Preview
+
MDC Logging
camel-mdc
diff --git a/components/camel-mdc/src/generated/resources/mdc.json b/components/camel-mdc/src/generated/resources/mdc.json
index 21670e7e2e74a..d83aea8af6ecc 100644
--- a/components/camel-mdc/src/generated/resources/mdc.json
+++ b/components/camel-mdc/src/generated/resources/mdc.json
@@ -2,7 +2,7 @@
"other": {
"kind": "other",
"name": "mdc",
- "title": "Mdc",
+ "title": "MDC Logging",
"description": "Logging MDC (Mapped Diagnostic Context) Service",
"deprecated": false,
"firstVersion": "4.15.0",
diff --git a/components/camel-mdc/src/main/docs/mdc.adoc b/components/camel-mdc/src/main/docs/mdc.adoc
index ddcfb2557c536..6811ab56e23e7 100644
--- a/components/camel-mdc/src/main/docs/mdc.adoc
+++ b/components/camel-mdc/src/main/docs/mdc.adoc
@@ -1,5 +1,5 @@
-= Mdc Component
-:doctitle: Mdc
+= MDC Logging Component
+:doctitle: MDC Logging
:shortname: mdc
:artifactid: camel-mdc
:description: Logging MDC (Mapped Diagnostic Context) Service
@@ -19,26 +19,32 @@ When you enable the MDC Service provided in this component, you will instruct yo
If you want to use the feature, you need to include the `camel-mdc` dependency in your `pom.xml` and configure it with the parameters in the `application.properties` configuration file (at least set `camel.mdc.enabled=true`).
+NOTE: don't set legacy MDC via `context.setUseMDCLogging(true)` on `CamelContext` at the same time. Instead, only use `camel-mdc`.
+
The goal of this component is to avoid to work on low level API in Java. In older MDC implementations you had to hack into the code to include MDC such as:
-```java
+[source,java]
+----
org.slf4j.MDC.put("myCustomMDCKey", "myCustomKeyValue");
-```
+----
And later you had to make sure to provide MDC context propagation in async components (eg, `wiretap`) in order to make sure to have such context available in the new executing async thread. With this new service, the only thing to do is to add the value as a Camel Exchange header (or a property), for example:
-```java
+[source,java]
+----
.setHeader("myCustomMDCKey", simple("myCustomKeyValue"))
-```
+----
+
include the MDC service and additionally instruct the Camel application to treat that header as a MDC trace (via `camel.mdc.customHeaders=myCustomMDCKey` or `*` to include all headers). You won't need any longer to worry about context propagation as the propagation will be done via Camel Exchange instead.
NOTE: you won't also need any longer to hack the code using Java DSL, as you can put headers in any Camel DSL.
Depending on the logging technology used, you can now include the MDC parameters you want to trace in your logging configuration. For example, in `log4j2` configuration you can include them as shown below:
-```
+[source,text]
+----
... [%X{camel.contextId}, %X{camel.routeId}, %X{camel.exchangeId}, %X{camel.messageId}, %X{customHead}, %X{customProp}]
-```
+----
During the execution you can verify the output of the log to see the traces appended to your logger.
@@ -54,7 +60,7 @@ This is the list of default MDC values included in each execution:
* camel.contextId
* camel.threadId
-If they exists in the Exchange, then, they will be included in the MDC. You can use `camel.mdc.customHeaders` and `camel.mdc.customProperties` to include any further header and property you need to trace.
+If they exist in the Exchange, then, they will be included in the MDC. You can use `camel.mdc.customHeaders` and `camel.mdc.customProperties` to include any further header and property you need to trace.
== Configuration
diff --git a/components/camel-mdc/src/main/java/org/apache/camel/mdc/MDCProcessorsInterceptStrategy.java b/components/camel-mdc/src/main/java/org/apache/camel/mdc/MDCProcessorsInterceptStrategy.java
index e48d7bda9cd20..956374c499879 100644
--- a/components/camel-mdc/src/main/java/org/apache/camel/mdc/MDCProcessorsInterceptStrategy.java
+++ b/components/camel-mdc/src/main/java/org/apache/camel/mdc/MDCProcessorsInterceptStrategy.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.mdc;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.apache.camel.AsyncCallback;
@@ -26,10 +27,15 @@
import org.apache.camel.Processor;
import org.apache.camel.spi.InterceptStrategy;
import org.apache.camel.support.AsyncProcessorConverterHelper;
+import org.slf4j.MDC;
/**
* MDCProcessorsInterceptStrategy is used to wrap each processor calls and generate the MDC context for each process
- * execution.
+ * execution. IMPORTANT NOTE: When working in async mode there is no possible way to clean the thread MDC context
+ * reliably as any spinoff process (for example, InterceptSendToEndpoint EIP) would loose the possibility to reuse the
+ * context map previously set by this InterceptStrategy. This is not a consistency problem, since, the MDC service is in
+ * charge to reset the MDC context at every exchange execution with the values expected for each execution (either
+ * synchronous or asynchronous).
*/
public class MDCProcessorsInterceptStrategy implements InterceptStrategy {
@@ -53,12 +59,20 @@ public Processor wrapProcessorInInterceptors(
@Override
public boolean process(Exchange exchange, AsyncCallback callback) {
+ Map previousContext = MDC.getCopyOfContextMap();
mdcService.setMDC(exchange);
- boolean answer = asyncProcessor.process(exchange, doneSync -> {
- callback.done(doneSync);
+ return asyncProcessor.process(exchange, doneSync -> {
+ try {
+ callback.done(doneSync);
+ } finally {
+ mdcService.unsetMDC(exchange);
+ if (previousContext != null) {
+ MDC.setContextMap(previousContext);
+ } else {
+ MDC.clear();
+ }
+ }
});
- mdcService.unsetMDC(exchange);
- return answer;
}
@Override
@@ -74,6 +88,7 @@ public void process(Exchange exchange) throws Exception {
@Override
public CompletableFuture processAsync(Exchange exchange) {
CompletableFuture future = new CompletableFuture<>();
+ Map previousContext = MDC.getCopyOfContextMap();
mdcService.setMDC(exchange);
asyncProcessor.process(exchange, doneSync -> {
if (exchange.getException() != null) {
@@ -81,8 +96,12 @@ public CompletableFuture processAsync(Exchange exchange) {
} else {
future.complete(exchange);
}
+ if (previousContext != null) {
+ MDC.setContextMap(previousContext);
+ } else {
+ MDC.clear();
+ }
});
- mdcService.unsetMDC(exchange);
return future;
}
};
diff --git a/components/camel-mdc/src/main/java/org/apache/camel/mdc/MDCService.java b/components/camel-mdc/src/main/java/org/apache/camel/mdc/MDCService.java
index 89959a042e778..9e52ac66bd9ec 100644
--- a/components/camel-mdc/src/main/java/org/apache/camel/mdc/MDCService.java
+++ b/components/camel-mdc/src/main/java/org/apache/camel/mdc/MDCService.java
@@ -16,6 +16,8 @@
*/
package org.apache.camel.mdc;
+import java.util.Map;
+
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePropertyKey;
@@ -137,8 +139,13 @@ protected void unsetMDC(Exchange exchange) {
private final class MDCLogListener implements LogListener {
+ // NOTE: the onLog and afterLog are executed on the same thread, so we can
+ // reliably store the context here.
+ Map previousContext;
+
@Override
public String onLog(Exchange exchange, CamelLogger camelLogger, String message) {
+ previousContext = MDC.getCopyOfContextMap();
setMDC(exchange);
return message;
}
@@ -146,6 +153,7 @@ public String onLog(Exchange exchange, CamelLogger camelLogger, String message)
@Override
public void afterLog(Exchange exchange, CamelLogger camelLogger, String message) {
unsetMDC(exchange);
+ MDC.setContextMap(previousContext);
}
}
diff --git a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllHeadersTest.java b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllHeadersTest.java
index 14ab6a56c3ead..b7f4adbbdbabb 100644
--- a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllHeadersTest.java
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllHeadersTest.java
@@ -22,10 +22,9 @@
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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -33,8 +32,6 @@
public class MDCAllHeadersTest extends ExchangeTestSupport {
- private static final Logger LOG = LoggerFactory.getLogger(MDCSelectedPropertiesTest.class);
-
@Override
protected CamelContext createCamelContext() throws Exception {
MDCService mdcSvc = new MDCService();
@@ -46,10 +43,22 @@ protected CamelContext createCamelContext() throws Exception {
}
@Test
- void testRouteSingleRequest() throws IOException {
+ void testRouteSingleRequest() throws IOException, InterruptedException {
+ MockEndpoint mock = getMockEndpoint("mock:assertMdc");
+ mock.expectedMessageCount(1);
+ mock.whenAnyExchangeReceived(exchange -> {
+ assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
+
+ assertEquals("Header1", MDC.get("head1"));
+ assertEquals("Header2", MDC.get("head2"));
+ });
+
template.request("direct:start", null);
- // We should get no MDC after the route has been executed
- assertEquals(0, MDC.getCopyOfContextMap().size());
+
+ mock.assertIsSatisfied();
}
@Override
@@ -62,15 +71,7 @@ public void configure() {
.log("A message")
.setHeader("head1", simple("Header1"))
.setHeader("head2", simple("Header2"))
- .process(exchange -> {
- LOG.info("A process");
- assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
- assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
- assertEquals("Header1", MDC.get("head1"));
- assertEquals("Header2", MDC.get("head2"));
- })
+ .to("mock:assertMdc")
.to("log:info");
}
};
diff --git a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllPropertiesTest.java b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllPropertiesTest.java
index 57562b6172809..0f6510a220784 100644
--- a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllPropertiesTest.java
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllPropertiesTest.java
@@ -16,16 +16,13 @@
*/
package org.apache.camel.mdc;
-import java.io.IOException;
-
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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -33,8 +30,6 @@
public class MDCAllPropertiesTest extends ExchangeTestSupport {
- private static final Logger LOG = LoggerFactory.getLogger(MDCSelectedPropertiesTest.class);
-
@Override
protected CamelContext createCamelContext() throws Exception {
MDCService mdcSvc = new MDCService();
@@ -46,10 +41,26 @@ protected CamelContext createCamelContext() throws Exception {
}
@Test
- void testRouteSingleRequest() throws IOException {
+ void testRouteSingleRequest() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:assertMdc");
+
+ mock.expectedMessageCount(1);
+ mock.whenAnyExchangeReceived(exchange -> {
+ // Assert MDC values
+ assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
+ // Assert exchange properties are correctly set
+ assertEquals("Property1", exchange.getProperty("prop1"));
+ assertEquals("Property2", exchange.getProperty("prop2"));
+ });
+
+ // Trigger the route
template.request("direct:start", null);
- // We should get no MDC after the route has been executed
- assertEquals(0, MDC.getCopyOfContextMap().size());
+
+ // Wait for assertions to pass
+ mock.assertIsSatisfied();
}
@Override
@@ -60,17 +71,9 @@ public void configure() {
from("direct:start")
.routeId("start")
.log("A message")
- .setProperty("prop1", simple("Property1"))
- .setProperty("prop2", simple("Property2"))
- .process(exchange -> {
- LOG.info("A process");
- assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
- assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
- assertEquals("Property1", MDC.get("prop1"));
- assertEquals("Property2", MDC.get("prop2"));
- })
+ .setProperty("prop1", constant("Property1"))
+ .setProperty("prop2", constant("Property2"))
+ .to("mock:assertMdc")
.to("log:info");
}
};
diff --git a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAsyncTest.java b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAsyncTest.java
index f1b43b08d1998..d2bafc10da561 100644
--- a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAsyncTest.java
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAsyncTest.java
@@ -21,6 +21,7 @@
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
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 org.slf4j.MDC;
@@ -44,15 +45,21 @@ protected CamelContext createCamelContext() throws Exception {
@Test
public void testAsyncEndpoint() throws Exception {
- getMockEndpoint("mock:before").expectedBodiesReceived("Hello Camel");
- getMockEndpoint("mock:after").expectedBodiesReceived("Bye Camel");
- getMockEndpoint("mock:result").expectedBodiesReceived("Bye Camel");
+ MockEndpoint before = getMockEndpoint("mock:before");
+ MockEndpoint after = getMockEndpoint("mock:after");
+ MockEndpoint result = getMockEndpoint("mock:result");
+ before.expectedBodiesReceived("Hello Camel");
+ after.expectedBodiesReceived("Bye Camel");
+ result.expectedBodiesReceived("Bye Camel");
String reply = template.requestBody("direct:start", "Hello Camel", String.class);
assertEquals("Bye Camel", reply);
- // We should get no MDC after the route has been executed
- assertEquals(0, MDC.getCopyOfContextMap().size());
+ before.assertIsSatisfied();
+ after.assertIsSatisfied();
+ result.assertIsSatisfied();
+
+ // NOTE: more assertions directly in process as it was simpler to verify the condition while executing the async process.
}
@Override
diff --git a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAsyncWiretapTest.java b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAsyncWiretapTest.java
index f549a830dd9dc..42d7cd015d36f 100644
--- a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAsyncWiretapTest.java
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAsyncWiretapTest.java
@@ -56,8 +56,8 @@ void testRouteSingleRequest() throws IOException, InterruptedException {
mock.setAssertPeriod(5000);
context.createProducerTemplate().sendBody("direct:start", null);
mock.assertIsSatisfied(1000);
- // We should get no MDC after the route has been executed
- assertEquals(0, MDC.getCopyOfContextMap().size());
+
+ // NOTE: more assertions directly in process as it was simpler to verify the condition while executing the async process.
}
@Override
diff --git a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCDefaultTest.java b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCDefaultTest.java
index 162d5ae5cb766..234013e0a5f51 100644
--- a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCDefaultTest.java
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCDefaultTest.java
@@ -16,25 +16,19 @@
*/
package org.apache.camel.mdc;
-import java.io.IOException;
-
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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class MDCDefaultTest extends ExchangeTestSupport {
- private static final Logger LOG = LoggerFactory.getLogger(MDCDefaultTest.class);
-
@Override
protected CamelContext createCamelContext() throws Exception {
MDCService mdcSvc = new MDCService();
@@ -45,10 +39,19 @@ protected CamelContext createCamelContext() throws Exception {
}
@Test
- void testRouteSingleRequest() throws IOException {
+ void testRouteSingleRequest() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:assertMdc");
+
+ mock.expectedMessageCount(1);
+ mock.whenAnyExchangeReceived(exchange -> {
+ assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID), "MDC_MESSAGE_ID should be set");
+ assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID), "MDC_EXCHANGE_ID should be set");
+ assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID), "MDC_ROUTE_ID should be set");
+ assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID), "MDC_CAMEL_CONTEXT_ID should be set");
+ });
+
template.request("direct:start", null);
- // We should get no MDC after the route has been executed
- assertEquals(0, MDC.getCopyOfContextMap().size());
+ mock.assertIsSatisfied();
}
@Override
@@ -59,13 +62,7 @@ public void configure() {
from("direct:start")
.routeId("start")
.log("A message")
- .process(exchange -> {
- LOG.info("A process");
- assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
- assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
- })
+ .to("mock:assertMdc")
.to("log:info");
}
};
diff --git a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCInterceptToEndpointBeanTest.java b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCInterceptToEndpointBeanTest.java
new file mode 100644
index 0000000000000..489aa0ac2b29d
--- /dev/null
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCInterceptToEndpointBeanTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.mdc;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.Exchange;
+import org.apache.camel.LoggingLevel;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class MDCInterceptToEndpointBeanTest extends CamelTestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger("MyRoute.myBean");
+
+ private Processor myBean = new Processor() {
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ LOG.info("MDC Values present");
+
+ assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
+ }
+ };
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ MDCService mdcSvc = new MDCService();
+ CamelContext context = super.createCamelContext();
+ CamelContextAware.trySetCamelContext(mdcSvc, context);
+ mdcSvc.init(context);
+ return context;
+ }
+
+ @Test
+ public void testMDC() throws Exception { // NOSONAR
+ template.sendBody("direct:start", "Hello World");
+ // NOTE: asserts are done directly in process to make it easier understand the test
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ context.getRegistry().bind("myMapper", myBean);
+
+ interceptSendToEndpoint("bean*").to("direct:beforeSend");
+
+ from("direct:start")
+ .log(LoggingLevel.INFO, "MyRoute.logBefore", "MDC Values present")
+ .process(e -> {
+ assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
+ })
+ .to("bean:myMapper")
+ .log(LoggingLevel.INFO, "MyRoute.logAfter", "MDC Values present")
+ .process(e -> {
+ assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
+ assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
+ });
+
+ from("direct:beforeSend")
+ .log(LoggingLevel.INFO, "MyRoute.beforeSend", "MDC Values present");
+
+ }
+ };
+ }
+}
diff --git a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedHeadersTest.java b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedHeadersTest.java
index 3ff151b206ca4..549df87f91a84 100644
--- a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedHeadersTest.java
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedHeadersTest.java
@@ -16,16 +16,13 @@
*/
package org.apache.camel.mdc;
-import java.io.IOException;
-
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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -34,8 +31,6 @@
public class MDCSelectedHeadersTest extends ExchangeTestSupport {
- private static final Logger LOG = LoggerFactory.getLogger(MDCAllPropertiesTest.class);
-
@Override
protected CamelContext createCamelContext() throws Exception {
MDCService mdcSvc = new MDCService();
@@ -47,10 +42,30 @@ protected CamelContext createCamelContext() throws Exception {
}
@Test
- void testRouteSingleRequest() throws IOException {
+ void testRouteSingleRequest() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:assertMdc");
+ mock.expectedMessageCount(1);
+ mock.whenAnyExchangeReceived(exchange -> {
+ // Required MDC entries
+ assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID), "MDC_MESSAGE_ID should be set");
+ assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID), "MDC_EXCHANGE_ID should be set");
+ assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID), "MDC_ROUTE_ID should be set");
+ assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID), "MDC_CAMEL_CONTEXT_ID should be set");
+
+ // Headers propagated to MDC
+ assertEquals("Header1", MDC.get("head1"));
+ assertEquals("Header2", MDC.get("head2"));
+
+ // Headers that were not set remain null
+ assertNull(MDC.get("head3"));
+ assertNull(MDC.get("head4")); // note: intentionally not included
+ });
+
+ // Trigger the route
template.request("direct:start", null);
- // We should get no MDC after the route has been executed
- assertEquals(0, MDC.getCopyOfContextMap().size());
+
+ // Wait for expectations
+ mock.assertIsSatisfied();
}
@Override
@@ -61,21 +76,11 @@ public void configure() {
from("direct:start")
.routeId("start")
.log("A message")
- .setHeader("head1", simple("Header1"))
- .setHeader("head2", simple("Header2"))
- // head3 is missing on purpose!
- .setHeader("head4", simple("Header4"))
- .process(exchange -> {
- LOG.info("A process");
- assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
- assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
- assertEquals("Header1", MDC.get("head1"));
- assertEquals("Header2", MDC.get("head2"));
- assertNull(MDC.get("head3"));
- assertNull(MDC.get("head4"));
- })
+ .setHeader("head1", constant("Header1"))
+ .setHeader("head2", constant("Header2"))
+ // head3 is missing on purpose
+ .setHeader("head4", constant("Header4"))
+ .to("mock:assertMdc")
.to("log:info");
}
};
diff --git a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedPropertiesTest.java b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedPropertiesTest.java
index e3fa275a73711..2afa0b8fb114d 100644
--- a/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedPropertiesTest.java
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedPropertiesTest.java
@@ -16,16 +16,13 @@
*/
package org.apache.camel.mdc;
-import java.io.IOException;
-
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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -34,8 +31,6 @@
public class MDCSelectedPropertiesTest extends ExchangeTestSupport {
- private static final Logger LOG = LoggerFactory.getLogger(MDCAllPropertiesTest.class);
-
@Override
protected CamelContext createCamelContext() throws Exception {
MDCService mdcSvc = new MDCService();
@@ -47,10 +42,31 @@ protected CamelContext createCamelContext() throws Exception {
}
@Test
- void testRouteSingleRequest() throws IOException {
+ void testRouteSingleRequest() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:assertMdc");
+ mock.expectedMessageCount(1);
+
+ mock.whenAnyExchangeReceived(exchange -> {
+ // Required MDC entries
+ assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID), "MDC_MESSAGE_ID should be set");
+ assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID), "MDC_EXCHANGE_ID should be set");
+ assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID), "MDC_ROUTE_ID should be set");
+ assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID), "MDC_CAMEL_CONTEXT_ID should be set");
+
+ // Properties propagated to MDC
+ assertEquals("Property1", MDC.get("prop1"));
+ assertEquals("Property2", MDC.get("prop2"));
+
+ // Properties that were not set remain null
+ assertNull(MDC.get("prop3"));
+ assertNull(MDC.get("prop4")); // intentionally not included
+ });
+
+ // Trigger the route
template.request("direct:start", null);
- // We should get no MDC after the route has been executed
- assertEquals(0, MDC.getCopyOfContextMap().size());
+
+ // Wait for assertions
+ mock.assertIsSatisfied();
}
@Override
@@ -61,21 +77,12 @@ public void configure() {
from("direct:start")
.routeId("start")
.log("A message")
- .setProperty("prop1", simple("Property1"))
- .setProperty("prop2", simple("Property2"))
- // prop3 is missing on purpose!
- .setProperty("prop4", simple("Property4"))
- .process(exchange -> {
- LOG.info("A process");
- assertNotNull(MDC.get(MDCService.MDC_MESSAGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_EXCHANGE_ID));
- assertNotNull(MDC.get(MDCService.MDC_ROUTE_ID));
- assertNotNull(MDC.get(MDCService.MDC_CAMEL_CONTEXT_ID));
- assertEquals("Property1", MDC.get("prop1"));
- assertEquals("Property2", MDC.get("prop2"));
- assertNull(MDC.get("prop3"));
- assertNull(MDC.get("prop4"));
- })
+ .setProperty("prop1", constant("Property1"))
+ .setProperty("prop2", constant("Property2"))
+ // prop3 is missing on purpose
+ .setProperty("prop4", constant("Property4"))
+ // No assertions inside the route
+ .to("mock:assertMdc")
.to("log:info");
}
};
diff --git a/docs/components/modules/others/nav.adoc b/docs/components/modules/others/nav.adoc
index 93e4ed21385b7..657c81472ec18 100644
--- a/docs/components/modules/others/nav.adoc
+++ b/docs/components/modules/others/nav.adoc
@@ -29,7 +29,7 @@
** xref:lra.adoc[LRA]
** xref:mail-microsoft-oauth.adoc[Mail Microsoft Oauth]
** xref:main.adoc[Main]
-** xref:mdc.adoc[Mdc]
+** xref:mdc.adoc[MDC Logging]
** xref:observation.adoc[Micrometer Observability]
** xref:micrometer-observability.adoc[Micrometer Observability 2]
** xref:micrometer-prometheus.adoc[Micrometer Prometheus]