This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch mdc in repository https://gitbox.apache.org/repos/asf/camel.git
commit 6715a0405c480a4339d9f7dd13310071f7f9c061 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sun Dec 17 13:59:31 2023 +0100 CAMEL-17721: camel-core - MDC custom keys should preserve existing value during routing, so users can alter its value. --- .../apache/camel/impl/engine/MDCUnitOfWork.java | 7 +- .../apache/camel/processor/MDCCustomKeysTest.java | 141 +++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/MDCUnitOfWork.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/MDCUnitOfWork.java index e7c6c748849..d497fdbeb99 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/MDCUnitOfWork.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/MDCUnitOfWork.java @@ -309,7 +309,12 @@ public class MDCUnitOfWork extends DefaultUnitOfWork implements Service { MDC.put(MDC_CAMEL_CONTEXT_ID, camelContextId); } if (custom != null) { - custom.forEach(MDC::put); + // keep existing custom value to not override + custom.forEach((k, v) -> { + if (MDC.get(k) == null) { + MDC.put(k, v); + } + }); } } // need to setup the routeId finally diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/MDCCustomKeysTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/MDCCustomKeysTest.java new file mode 100644 index 00000000000..f76e54f2750 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/processor/MDCCustomKeysTest.java @@ -0,0 +1,141 @@ +/* + * 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.processor; + +import org.apache.camel.ContextTestSupport; +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.junit.jupiter.api.Test; +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.assertTrue; + +public class MDCCustomKeysTest extends ContextTestSupport { + + @Test + public void testMdcPreserved() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:end"); + mock.expectedMessageCount(1); + + template.sendBody("direct:a", "A,B"); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + // enable MDC and breadcrumb + context.setUseMDCLogging(true); + context.setUseBreadcrumb(true); + context.setMDCLoggingKeysPattern("custom*,my*"); + + MdcCheckerProcessor checker1 = new MdcCheckerProcessor("N/A"); + MdcCheckerProcessor checker2 = new MdcCheckerProcessor("World"); + + from("direct:a").process(e -> { + // custom is propagated + MDC.put("custom.hello", "N/A"); + // foo is propagated due we use the same thread + MDC.put("foo", "Bar"); + // myKey is propagated + MDC.put("myKey", "Baz"); + }).process(checker1) + .to("log:foo") + .process(e -> { + MDC.put("custom.hello", "World"); + }) + .process(checker2) + .to("mock:end"); + } + }; + } + + /** + * Stores values from the first invocation to compare them with the second invocation later. + */ + private static class MdcCheckerProcessor implements Processor { + + private String exchangeId; + private String messageId; + private String breadcrumbId; + private String contextId; + private Long threadId; + private String foo; + + private final String expected; + + public MdcCheckerProcessor(String expected) { + this.expected = expected; + } + + @Override + public void process(Exchange exchange) throws Exception { + // custom is propagated as its pattern matches + assertEquals(expected, MDC.get("custom.hello")); + assertEquals("Baz", MDC.get("myKey")); + + if (foo != null) { + // foo propagated because its the same thread + assertEquals(foo, MDC.get("foo")); + } else { + foo = MDC.get("foo"); + } + + if (threadId != null) { + long currId = Thread.currentThread().getId(); + assertEquals(threadId, (Object) currId); + } else { + threadId = Thread.currentThread().getId(); + } + if (exchangeId != null) { + assertNotEquals(exchangeId, MDC.get("camel.exchangeId")); + } else { + exchangeId = MDC.get("camel.exchangeId"); + assertTrue(exchangeId != null && exchangeId.length() > 0); + } + + if (messageId != null) { + assertNotEquals(messageId, MDC.get("camel.messageId")); + } else { + messageId = MDC.get("camel.messageId"); + assertTrue(messageId != null && messageId.length() > 0); + } + + if (breadcrumbId != null) { + assertEquals(breadcrumbId, MDC.get("camel.breadcrumbId")); + } else { + breadcrumbId = MDC.get("camel.breadcrumbId"); + assertTrue(breadcrumbId != null && breadcrumbId.length() > 0); + } + + if (contextId != null) { + assertEquals(contextId, MDC.get("camel.contextId")); + } else { + contextId = MDC.get("camel.contextId"); + assertTrue(contextId != null && contextId.length() > 0); + } + } + } + +}