feat(components): MDC Intercept strategy and UT

* Required to wrap any Camel processor
* Unit test to cover main use cases
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
new file mode 100644
index 0000000..3306a40
--- /dev/null
+++ b/components/camel-mdc/src/main/java/org/apache/camel/mdc/MDCProcessorsInterceptStrategy.java
@@ -0,0 +1,64 @@
+/*
+ * 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.Exchange;
+import org.apache.camel.NamedNode;
+import org.apache.camel.Processor;
+import org.apache.camel.spi.InterceptStrategy;
+import org.apache.camel.support.processor.DelegateAsyncProcessor;
+
+/**
+ * MDCProcessorsInterceptStrategy is used to wrap each processor calls and generate the MDC context for each process
+ * execution.
+ */
+public class MDCProcessorsInterceptStrategy implements InterceptStrategy {
+
+    private MDCService mdcService;
+
+    public MDCProcessorsInterceptStrategy(MDCService mdcService) {
+        this.mdcService = mdcService;
+    }
+
+    @Override
+    public Processor wrapProcessorInInterceptors(
+            CamelContext camelContext,
+            NamedNode processorDefinition, Processor target, Processor nextTarget)
+            throws Exception {
+        return new DelegateAsyncProcessor(new TraceProcessor(target));
+    }
+
+    private class TraceProcessor implements Processor {
+        private final Processor target;
+
+        public TraceProcessor(Processor target) {
+            this.target = target;
+        }
+
+        @Override
+        public void process(Exchange exchange) throws Exception {
+            mdcService.setMDC(exchange);
+            try {
+                target.process(exchange);
+            } finally {
+                mdcService.unsetMDC();
+            }
+        }
+    }
+
+}
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 bd38d6a..5ac3a42 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
@@ -22,6 +22,7 @@
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.spi.CamelLogger;
 import org.apache.camel.spi.CamelMDCService;
+import org.apache.camel.spi.InterceptStrategy;
 import org.apache.camel.spi.LogListener;
 import org.apache.camel.spi.annotations.JdkService;
 import org.apache.camel.support.service.ServiceSupport;
@@ -33,12 +34,12 @@
 @JdkService("mdc-service")
 public class MDCService extends ServiceSupport implements CamelMDCService {
 
-    private String MDC_BREADCRUMB_ID = "camel.breadcrumbId";
-    private String MDC_EXCHANGE_ID = "camel.exchangeId";
-    private String MDC_MESSAGE_ID = "camel.messageId";
-    private String MDC_CORRELATION_ID = "camel.correlationId";
-    private String MDC_ROUTE_ID = "camel.routeId";
-    private String MDC_CAMEL_CONTEXT_ID = "camel.contextId";
+    static String MDC_BREADCRUMB_ID = "camel.breadcrumbId";
+    static String MDC_EXCHANGE_ID = "camel.exchangeId";
+    static String MDC_MESSAGE_ID = "camel.messageId";
+    static String MDC_CORRELATION_ID = "camel.correlationId";
+    static String MDC_ROUTE_ID = "camel.routeId";
+    static String MDC_CAMEL_CONTEXT_ID = "camel.contextId";
 
     private static final Logger LOG = LoggerFactory.getLogger(MDCService.class);
 
@@ -90,7 +91,9 @@
     @Override
     public void doInit() {
         ObjectHelper.notNull(camelContext, "CamelContext", this);
-        camelContext.getCamelContextExtension().addLogListener(new TracingLogListener());
+        camelContext.getCamelContextExtension().addLogListener(new MDCLogListener());
+        InterceptStrategy interceptStrategy = new MDCProcessorsInterceptStrategy(this);
+        camelContext.getCamelContextExtension().addInterceptStrategy(interceptStrategy);
     }
 
     @Override
@@ -99,92 +102,101 @@
         LOG.info("Mapped Diagnostic Context (MDC) enabled");
     }
 
-    private final class TracingLogListener implements LogListener {
+    protected void setMDC(Exchange exchange) {
+        try {
+            // Default values
+            prepareMDC(exchange);
+            if (getCustomHeaders() != null) {
+                if (getCustomHeaders().equals("*")) {
+                    allHeadersMDC(exchange);
+                } else {
+                    userSelectedHeadersMDC(exchange);
+                }
+            }
+            if (getCustomProperties() != null) {
+                if (getCustomProperties().equals("*")) {
+                    allPropertiesMDC(exchange);
+                } else {
+                    userSelectedPropertiesMDC(exchange);
+                }
+            }
+        } catch (Exception t) {
+            // This exception is ignored
+            LOG.warn("MDC: failed to store MDC data. This exception is ignored.", t);
+        }
+    }
+
+    public void unsetMDC() {
+        MDC.clear();
+    }
+
+    private final class MDCLogListener implements LogListener {
 
         @Override
         public String onLog(Exchange exchange, CamelLogger camelLogger, String message) {
-            try {
-                // Default values
-                prepareMDC(exchange);
-                if (getCustomHeaders() != null) {
-                    if (getCustomHeaders().equals("*")){
-                        allHeadersMDC(exchange);
-                    } else {
-                        userSelectedHeadersMDC(exchange);
-                    }
-                }
-                if (getCustomProperties() != null) {
-                    if (getCustomProperties().equals("*")){
-                        allPropertiesMDC(exchange);
-                    } else {
-                        userSelectedPropertiesMDC(exchange);
-                    }
-                }
-            } catch (Exception t) {
-                // This exception is ignored
-                LOG.warn("MDC: failed to store MDC data. This exception is ignored.", t);
-            }
+            setMDC(exchange);
             return message;
         }
 
         @Override
         public void afterLog(Exchange exchange, CamelLogger camelLogger, String message) {
-            MDC.clear();
+            unsetMDC();
         }
+    }
 
-        // Default basic MDC properties to set
-        private void prepareMDC(Exchange exchange) {
-            MDC.put(MDC_EXCHANGE_ID, exchange.getExchangeId());
-            MDC.put(MDC_MESSAGE_ID, exchange.getMessage().getMessageId());
-            MDC.put(MDC_CAMEL_CONTEXT_ID, exchange.getContext().getName());
-            // Backward compatibility: this info may not be longer widely used
-            String corrId = exchange.getProperty(ExchangePropertyKey.CORRELATION_ID, String.class);
-            if (corrId != null) {
-                MDC.put(MDC_CORRELATION_ID, corrId);
-            }
-            // Backward compatibility: this info may not be longer widely used
-            String breadcrumbId = exchange.getIn().getHeader(Exchange.BREADCRUMB_ID, String.class);
-            if (breadcrumbId != null) {
-                MDC.put(MDC_BREADCRUMB_ID, breadcrumbId);
-            }
-            String routeId = exchange.getFromRouteId();
-            if (routeId != null) {
-                MDC.put(MDC_ROUTE_ID, routeId);
-            }
+    // Default basic MDC properties to set
+    private void prepareMDC(Exchange exchange) {
+        MDC.put(MDC_EXCHANGE_ID, exchange.getExchangeId());
+        MDC.put(MDC_MESSAGE_ID, exchange.getMessage().getMessageId());
+        MDC.put(MDC_CAMEL_CONTEXT_ID, exchange.getContext().getName());
+        // Backward compatibility: this info may not be longer widely used
+        String corrId = exchange.getProperty(ExchangePropertyKey.CORRELATION_ID, String.class);
+        if (corrId != null) {
+            MDC.put(MDC_CORRELATION_ID, corrId);
         }
+        // Backward compatibility: this info may not be longer widely used
+        String breadcrumbId = exchange.getIn().getHeader(Exchange.BREADCRUMB_ID, String.class);
+        if (breadcrumbId != null) {
+            MDC.put(MDC_BREADCRUMB_ID, breadcrumbId);
+        }
+        String routeId = exchange.getFromRouteId();
+        if (routeId != null) {
+            MDC.put(MDC_ROUTE_ID, routeId);
+        }
+    }
 
-        // Include those headers selected by the user
-        private void userSelectedHeadersMDC(Exchange exchange) {
-            for (String customHeader : getCustomHeaders().split(",")) {
-                if (exchange.getIn().getHeader(customHeader) != null) {
-                    MDC.put(customHeader, exchange.getIn().getHeader(customHeader, String.class));
-                }
+    // Include those headers selected by the user
+    private void userSelectedHeadersMDC(Exchange exchange) {
+        for (String customHeader : getCustomHeaders().split(",")) {
+            if (exchange.getIn().getHeader(customHeader) != null) {
+                MDC.put(customHeader, exchange.getIn().getHeader(customHeader, String.class));
             }
         }
-        // Include all available headers
-        private void allHeadersMDC(Exchange exchange) {
-            for (String header : exchange.getIn().getHeaders().keySet()) {
-                if (exchange.getIn().getHeader(header) != null) {
-                    MDC.put(header, exchange.getIn().getHeader(header, String.class));
-                }
-            }
-        }
+    }
 
-        // Include those properties selected by the user
-        private void userSelectedPropertiesMDC(Exchange exchange) {
-            for (String customProperty : getCustomProperties().split(",")) {
-                if (exchange.getProperty(customProperty) != null) {
-                    MDC.put(customProperty, exchange.getProperty(customProperty, String.class));
-                }
+    // Include all available headers
+    private void allHeadersMDC(Exchange exchange) {
+        for (String header : exchange.getIn().getHeaders().keySet()) {
+            if (exchange.getIn().getHeader(header) != null) {
+                MDC.put(header, exchange.getIn().getHeader(header, String.class));
             }
         }
+    }
 
-        // Include all available properties
-        private void allPropertiesMDC(Exchange exchange) {
-            for (String property : exchange.getAllProperties().keySet()) {
-                if (exchange.getProperty(property) != null) {
-                    MDC.put(property, exchange.getProperty(property, String.class));
-                }
+    // Include those properties selected by the user
+    private void userSelectedPropertiesMDC(Exchange exchange) {
+        for (String customProperty : getCustomProperties().split(",")) {
+            if (exchange.getProperty(customProperty) != null) {
+                MDC.put(customProperty, exchange.getProperty(customProperty, String.class));
+            }
+        }
+    }
+
+    // Include all available properties
+    private void allPropertiesMDC(Exchange exchange) {
+        for (String property : exchange.getAllProperties().keySet()) {
+            if (exchange.getProperty(property) != null) {
+                MDC.put(property, exchange.getProperty(property, String.class));
             }
         }
     }
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
new file mode 100644
index 0000000..14ab6a5
--- /dev/null
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllHeadersTest.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.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.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 MDCAllHeadersTest extends ExchangeTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MDCSelectedPropertiesTest.class);
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        MDCService mdcSvc = new MDCService();
+        mdcSvc.setCustomHeaders("*");
+        CamelContext context = super.createCamelContext();
+        CamelContextAware.trySetCamelContext(mdcSvc, context);
+        mdcSvc.init(context);
+        return context;
+    }
+
+    @Test
+    void testRouteSingleRequest() throws IOException {
+        template.request("direct:start", null);
+        // We should get no MDC after the route has been executed
+        assertEquals(0, MDC.getCopyOfContextMap().size());
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start")
+                        .routeId("start")
+                        .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("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
new file mode 100644
index 0000000..57562b6
--- /dev/null
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAllPropertiesTest.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.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.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 MDCAllPropertiesTest extends ExchangeTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MDCSelectedPropertiesTest.class);
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        MDCService mdcSvc = new MDCService();
+        mdcSvc.setCustomProperties("*");
+        CamelContext context = super.createCamelContext();
+        CamelContextAware.trySetCamelContext(mdcSvc, context);
+        mdcSvc.init(context);
+        return context;
+    }
+
+    @Test
+    void testRouteSingleRequest() throws IOException {
+        template.request("direct:start", null);
+        // We should get no MDC after the route has been executed
+        assertEquals(0, MDC.getCopyOfContextMap().size());
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            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"));
+                        })
+                        .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
new file mode 100644
index 0000000..7a5c228
--- /dev/null
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCAsyncTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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 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.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class MDCAsyncTest extends ExchangeTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MDCAsyncTest.class);
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        MDCService mdcSvc = new MDCService();
+        mdcSvc.setCustomHeaders("*");
+        mdcSvc.setCustomProperties("*");
+        CamelContext context = super.createCamelContext();
+        CamelContextAware.trySetCamelContext(mdcSvc, context);
+        mdcSvc.init(context);
+        return context;
+    }
+
+    @Test
+    void testRouteSingleRequest() throws IOException, InterruptedException {
+        MockEndpoint mock = getMockEndpoint("mock:end");
+        mock.expectedMessageCount(1);
+        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());
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start")
+                        .setBody()
+                        .simple("start")
+                        .log("start: ${exchangeId}")
+                        .to("direct:a")
+                        .wireTap("direct:b");
+
+                from("direct:a")
+                        .setProperty("prop1", simple("Property1"))
+                        .setHeader("head", simple("Header1"))
+                        .process(exchange -> {
+                            LOG.info("Direct: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("head"));
+                            assertEquals("Property1", MDC.get("prop1"));
+                            assertNull(MDC.get("prop2"));
+                            // We store the exchange of this execution in a property
+                            // as we will use this property to evaluate the exchange in the direct:b execution
+                            exchange.setProperty("directa-exchange", exchange.getExchangeId());
+                        })
+                        .setBody()
+                        .simple("Direct a")
+                        .log("directa: ${exchangeId}");
+
+                from("direct:b")
+                        .setProperty("prop2", simple("Property2"))
+                        .setHeader("head", simple("Header2"))
+                        .process(exchange -> {
+                            LOG.info("Direct:b 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("Header2", MDC.get("head"));
+                            // NOTE: properties are shared
+                            assertEquals("Property1", MDC.get("prop1"));
+                            assertEquals("Property2", MDC.get("prop2"));
+                            // We use as support storage the same properties
+                            assertNotEquals(exchange.getProperty("directa-exchange"), MDC.get(MDCService.MDC_EXCHANGE_ID));
+                        })
+                        .delay(2000)
+                        .setBody()
+                        .simple("Direct b")
+                        .log("directb: ${exchangeId}")
+                        .to("mock:end");
+            }
+        };
+    }
+
+}
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
new file mode 100644
index 0000000..162d5ae
--- /dev/null
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCDefaultTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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 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.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();
+        CamelContext context = super.createCamelContext();
+        CamelContextAware.trySetCamelContext(mdcSvc, context);
+        mdcSvc.init(context);
+        return context;
+    }
+
+    @Test
+    void testRouteSingleRequest() throws IOException {
+        template.request("direct:start", null);
+        // We should get no MDC after the route has been executed
+        assertEquals(0, MDC.getCopyOfContextMap().size());
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            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("log:info");
+            }
+        };
+    }
+
+}
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
new file mode 100644
index 0000000..3ff151b
--- /dev/null
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedHeadersTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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 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.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;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class MDCSelectedHeadersTest extends ExchangeTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MDCAllPropertiesTest.class);
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        MDCService mdcSvc = new MDCService();
+        mdcSvc.setCustomHeaders("head1,head2,head3");
+        CamelContext context = super.createCamelContext();
+        CamelContextAware.trySetCamelContext(mdcSvc, context);
+        mdcSvc.init(context);
+        return context;
+    }
+
+    @Test
+    void testRouteSingleRequest() throws IOException {
+        template.request("direct:start", null);
+        // We should get no MDC after the route has been executed
+        assertEquals(0, MDC.getCopyOfContextMap().size());
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            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"));
+                        })
+                        .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
new file mode 100644
index 0000000..e3fa275
--- /dev/null
+++ b/components/camel-mdc/src/test/java/org/apache/camel/mdc/MDCSelectedPropertiesTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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 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.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;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class MDCSelectedPropertiesTest extends ExchangeTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MDCAllPropertiesTest.class);
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        MDCService mdcSvc = new MDCService();
+        mdcSvc.setCustomProperties("prop1,prop2,prop3");
+        CamelContext context = super.createCamelContext();
+        CamelContextAware.trySetCamelContext(mdcSvc, context);
+        mdcSvc.init(context);
+        return context;
+    }
+
+    @Test
+    void testRouteSingleRequest() throws IOException {
+        template.request("direct:start", null);
+        // We should get no MDC after the route has been executed
+        assertEquals(0, MDC.getCopyOfContextMap().size());
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            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"));
+                        })
+                        .to("log:info");
+            }
+        };
+    }
+
+}
diff --git a/components/camel-mdc/src/test/resources/log4j2.properties b/components/camel-mdc/src/test/resources/log4j2.properties
index c9d7df8..590933e 100644
--- a/components/camel-mdc/src/test/resources/log4j2.properties
+++ b/components/camel-mdc/src/test/resources/log4j2.properties
@@ -17,14 +17,14 @@
 appender.console.type = Console
 appender.console.name = console
 appender.console.layout.type = PatternLayout
-appender.console.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+appender.console.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m [%X{camel.contextId}, %X{camel.routeId}, %X{camel.exchangeId}, %X{camel.messageId}, %X{head1}, %X{prop1}, %X{head2}, %X{prop2}]%n
 
 appender.file.type = File
 appender.file.name = file
-appender.file.fileName = target/camel-telemetry-test.log
+appender.file.fileName = target/camel-mdc-test.log
 appender.file.append = true
 appender.file.layout.type = PatternLayout
-appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m [%X{camel.contextId}, %X{camel.routeId}, %X{camel.exchangeId}, %X{camel.messageId}, %X{head1}, %X{prop1}, %X{head2}, %X{prop2}]%n
 
 rootLogger.level = INFO
 rootLogger.appenderRef.file.ref = file