diff --git a/bom/pom.xml b/bom/pom.xml
index 6933f5c92..a4a1d58b2 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -841,6 +841,27 @@
helidon-microprofile-tests-junit5
${helidon.version}
+
+
+ io.helidon.logging
+ helidon-logging-common
+ ${helidon.version}
+
+
+ io.helidon.logging
+ helidon-logging-jul
+ ${helidon.version}
+
+
+ io.helidon.logging
+ helidon-logging-slf4j
+ ${helidon.version}
+
+
+ io.helidon.logging
+ helidon-logging-log4j
+ ${helidon.version}
+
diff --git a/common/common/src/main/java/io/helidon/common/HelidonConsoleHandler.java b/common/common/src/main/java/io/helidon/common/HelidonConsoleHandler.java
index 29f353fbe..fb1de3652 100644
--- a/common/common/src/main/java/io/helidon/common/HelidonConsoleHandler.java
+++ b/common/common/src/main/java/io/helidon/common/HelidonConsoleHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,10 @@ import java.util.regex.Pattern;
/**
* A {@link StreamHandler} that writes to {@link System#out standard out} and uses a {@link ThreadFormatter} for formatting.
* Sets the level to {@link Level#ALL} so that level filtering is performed solely by the loggers.
+ *
+ * @deprecated use io.helidon.logging.jul.HelidonConsoleHandler from helidon-logging-jul module instead
*/
+@Deprecated(since = "2.1.1")
public class HelidonConsoleHandler extends StreamHandler {
/**
@@ -40,6 +43,9 @@ public class HelidonConsoleHandler extends StreamHandler {
setOutputStream(System.out);
setLevel(Level.ALL); // Handlers should not filter, loggers should
setFormatter(new ThreadFormatter());
+ System.out.println("You are using deprecated logging handler -> io.helidon.common.HelidonConsoleHandler "
+ + "Please use helidon-logging-jul module and change your handler to "
+ + "io.helidon.logging.jul.HelidonConsoleHandler");
}
@Override
diff --git a/common/context/pom.xml b/common/context/pom.xml
index 96dc33fae..200180d75 100644
--- a/common/context/pom.xml
+++ b/common/context/pom.xml
@@ -42,5 +42,9 @@
hamcrest-all
test
+
+ io.helidon.common
+ helidon-common-service-loader
+
diff --git a/common/context/src/main/java/io/helidon/common/context/ContextAwareExecutorImpl.java b/common/context/src/main/java/io/helidon/common/context/ContextAwareExecutorImpl.java
index b7173c6ae..2e1660ce3 100644
--- a/common/context/src/main/java/io/helidon/common/context/ContextAwareExecutorImpl.java
+++ b/common/context/src/main/java/io/helidon/common/context/ContextAwareExecutorImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,11 @@
package io.helidon.common.context;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.ServiceLoader;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -26,7 +29,15 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
+import io.helidon.common.context.spi.DataPropagationProvider;
+import io.helidon.common.serviceloader.HelidonServiceLoader;
+
class ContextAwareExecutorImpl implements ContextAwareExecutorService {
+
+ @SuppressWarnings("rawtypes")
+ private static final List PROVIDERS = HelidonServiceLoader
+ .builder(ServiceLoader.load(DataPropagationProvider.class)).build().asList();
+
private final ExecutorService delegate;
ContextAwareExecutorImpl(ExecutorService toWrap) {
@@ -112,21 +123,39 @@ class ContextAwareExecutorImpl implements ContextAwareExecutorService {
}
- protected Callable wrap(Callable task) {
+ @SuppressWarnings(value = "unchecked")
+ protected Callable wrap(Callable task) {
Optional context = Contexts.context();
-
if (context.isPresent()) {
- return () -> Contexts.runInContext(context.get(), task);
+ Map, Object> properties = new HashMap<>();
+ PROVIDERS.forEach(provider -> properties.put(provider.getClass(), provider.data()));
+ return () -> {
+ try {
+ PROVIDERS.forEach(provider -> provider.propagateData(properties.get(provider.getClass())));
+ return Contexts.runInContext(context.get(), task);
+ } finally {
+ PROVIDERS.forEach(DataPropagationProvider::clearData);
+ }
+ };
} else {
return task;
}
}
+ @SuppressWarnings(value = "unchecked")
protected Runnable wrap(Runnable command) {
Optional context = Contexts.context();
-
if (context.isPresent()) {
- return () -> Contexts.runInContext(context.get(), command);
+ Map, Object> properties = new HashMap<>();
+ PROVIDERS.forEach(provider -> properties.put(provider.getClass(), provider.data()));
+ return () -> {
+ try {
+ PROVIDERS.forEach(provider -> provider.propagateData(properties.get(provider.getClass())));
+ Contexts.runInContext(context.get(), command);
+ } finally {
+ PROVIDERS.forEach(DataPropagationProvider::clearData);
+ }
+ };
} else {
return command;
}
diff --git a/common/context/src/main/java/io/helidon/common/context/spi/DataPropagationProvider.java b/common/context/src/main/java/io/helidon/common/context/spi/DataPropagationProvider.java
new file mode 100644
index 000000000..e1115c24b
--- /dev/null
+++ b/common/context/src/main/java/io/helidon/common/context/spi/DataPropagationProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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 io.helidon.common.context.spi;
+
+/**
+ * This is SPI provider which helps user to propagate values from one thread to another.
+ *
+ * Every provider has its method {@link #data()} invoked before thread switch, to obtain
+ * value for propagation. After the thread is switched, the new thread executes
+ * {@link #propagateData(Object)} to propagate data.
+ *
+ * @param an actual type of the data which will be propagated
+ */
+public interface DataPropagationProvider {
+
+ /**
+ * Return data that should be propagated.
+ *
+ * @return data for propagation
+ */
+ T data();
+
+ /**
+ * Propagates the data to be used by the new thread.
+ *
+ * @param data data for propagation
+ */
+ void propagateData(T data);
+
+ /**
+ * Clears the propagated date from the new thread when it finishes.
+ */
+ void clearData();
+
+}
diff --git a/common/context/src/main/java/io/helidon/common/context/spi/package-info.java b/common/context/src/main/java/io/helidon/common/context/spi/package-info.java
new file mode 100644
index 000000000..28db72f8d
--- /dev/null
+++ b/common/context/src/main/java/io/helidon/common/context/spi/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+/**
+ * Provider for data propagation between threads in executor service.
+ */
+package io.helidon.common.context.spi;
diff --git a/common/context/src/main/java/module-info.java b/common/context/src/main/java/module-info.java
index 04ccaf072..449da6de9 100644
--- a/common/context/src/main/java/module-info.java
+++ b/common/context/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,10 @@
module io.helidon.common.context {
requires java.logging;
requires io.helidon.common;
+ requires io.helidon.common.serviceloader;
exports io.helidon.common.context;
+ exports io.helidon.common.context.spi;
+
+ uses io.helidon.common.context.spi.DataPropagationProvider;
}
diff --git a/dependencies/pom.xml b/dependencies/pom.xml
index 4f2097228..62348aab5 100644
--- a/dependencies/pom.xml
+++ b/dependencies/pom.xml
@@ -79,6 +79,7 @@
1.1.6
5.6.2
4.13.1
+ 2.13.3
2.6.2
2.10
1.4
@@ -742,6 +743,11 @@
slf4j-simple
${version.lib.slf4j}
+
+ org.apache.logging.log4j
+ log4j-api
+ ${version.lib.log4j}
+
io.smallrye
diff --git a/logging/common/pom.xml b/logging/common/pom.xml
new file mode 100644
index 000000000..2472ee1c1
--- /dev/null
+++ b/logging/common/pom.xml
@@ -0,0 +1,37 @@
+
+
+
+ 4.0.0
+
+ helidon-logging-project
+ io.helidon.logging
+ 2.1.1-SNAPSHOT
+
+
+ helidon-logging-common
+ Helidon Logging Common
+
+
+
+ io.helidon.common
+ helidon-common-service-loader
+
+
+
+
\ No newline at end of file
diff --git a/logging/common/src/main/java/io/helidon/logging/common/HelidonMdc.java b/logging/common/src/main/java/io/helidon/logging/common/HelidonMdc.java
new file mode 100644
index 000000000..d0936e960
--- /dev/null
+++ b/logging/common/src/main/java/io/helidon/logging/common/HelidonMdc.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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 io.helidon.logging.common;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.ServiceLoader;
+
+import io.helidon.common.serviceloader.HelidonServiceLoader;
+import io.helidon.logging.common.spi.MdcProvider;
+
+/**
+ * Helidon MDC delegates values across all of the supported logging frameworks on the classpath.
+ */
+public class HelidonMdc {
+
+ private static final List MDC_PROVIDERS = HelidonServiceLoader
+ .builder(ServiceLoader.load(MdcProvider.class)).build().asList();
+
+ private HelidonMdc() {
+ throw new UnsupportedOperationException("This class cannot be instantiated");
+ }
+
+ /**
+ * Propagate value to all of the {@link MdcProvider} registered via SPI.
+ *
+ * @param key entry key
+ * @param value entry value
+ */
+ public static void set(String key, String value) {
+ MDC_PROVIDERS.forEach(provider -> provider.put(key, value));
+ }
+
+ /**
+ * Remove value with the specific key from all of the instances of {@link MdcProvider}.
+ *
+ * @param key key
+ */
+ public static void remove(String key) {
+ MDC_PROVIDERS.forEach(provider -> provider.remove(key));
+ }
+
+ /**
+ * Remove all of the entries bound to the current thread from the instances of {@link MdcProvider}.
+ */
+ public static void clear() {
+ MDC_PROVIDERS.forEach(MdcProvider::clear);
+ }
+
+ /**
+ * Return the first value found to the specific key.
+ *
+ * @param key key
+ * @return found value bound to key
+ */
+ public static Optional get(String key) {
+ return MDC_PROVIDERS.stream()
+ .map(provider -> provider.get(key))
+ .filter(Objects::nonNull)
+ .findFirst();
+ }
+
+}
diff --git a/logging/common/src/main/java/io/helidon/logging/common/package-info.java b/logging/common/src/main/java/io/helidon/logging/common/package-info.java
new file mode 100644
index 000000000..294c314c1
--- /dev/null
+++ b/logging/common/src/main/java/io/helidon/logging/common/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+/**
+ * Helidon MDC support for delegation of the MDC values to all of the logging frameworks.
+ */
+package io.helidon.logging.common;
diff --git a/logging/common/src/main/java/io/helidon/logging/common/spi/MdcProvider.java b/logging/common/src/main/java/io/helidon/logging/common/spi/MdcProvider.java
new file mode 100644
index 000000000..be5dad082
--- /dev/null
+++ b/logging/common/src/main/java/io/helidon/logging/common/spi/MdcProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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 io.helidon.logging.common.spi;
+
+import io.helidon.logging.common.HelidonMdc;
+
+/**
+ * Provider which is used to propagate values passed from {@link HelidonMdc} to the
+ * corresponding logging framework MDC storage.
+ */
+public interface MdcProvider {
+
+ /**
+ * Set value to the specific logging framework MDC storage.
+ * @param key entry key
+ * @param value entry value
+ */
+ void put(String key, String value);
+
+ /**
+ * Remove value bound to the key from the specific logging framework MDC storage.
+ *
+ * @param key entry to remove
+ */
+ void remove(String key);
+
+ /**
+ * Clear all of the MDC values from the specific logging framework MDC storage.
+ */
+ void clear();
+
+ /**
+ * Return value bound to the specific key.
+ *
+ * @param key entry key
+ * @return value bound to the key
+ */
+ String get(String key);
+
+}
diff --git a/logging/common/src/main/java/io/helidon/logging/common/spi/package-info.java b/logging/common/src/main/java/io/helidon/logging/common/spi/package-info.java
new file mode 100644
index 000000000..f1f9935c7
--- /dev/null
+++ b/logging/common/src/main/java/io/helidon/logging/common/spi/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+/**
+ * This package provides interface for an actual MDC support implementation.
+ */
+package io.helidon.logging.common.spi;
diff --git a/logging/common/src/main/java/module-info.java b/logging/common/src/main/java/module-info.java
new file mode 100644
index 000000000..9a3a1fc1d
--- /dev/null
+++ b/logging/common/src/main/java/module-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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.
+ */
+
+/**
+ * Helidon logging.
+ */
+module io.helidon.logging.common {
+ requires io.helidon.common.serviceloader;
+
+ exports io.helidon.logging.common;
+ exports io.helidon.logging.common.spi;
+
+ uses io.helidon.logging.common.spi.MdcProvider;
+
+}
\ No newline at end of file
diff --git a/logging/jul/pom.xml b/logging/jul/pom.xml
new file mode 100644
index 000000000..8212c5aaf
--- /dev/null
+++ b/logging/jul/pom.xml
@@ -0,0 +1,56 @@
+
+
+
+
+ helidon-logging-project
+ io.helidon.logging
+ 2.1.1-SNAPSHOT
+
+ 4.0.0
+
+ helidon-logging-jul
+ Helidon Java Util Logging Integration
+
+
+
+ io.helidon.common
+ helidon-common-context
+
+
+ io.helidon.logging
+ helidon-logging-common
+
+
+ io.helidon.common
+ helidon-common
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+
+
+
\ No newline at end of file
diff --git a/logging/jul/src/main/java/io/helidon/logging/jul/HelidonConsoleHandler.java b/logging/jul/src/main/java/io/helidon/logging/jul/HelidonConsoleHandler.java
new file mode 100644
index 000000000..4ca8d088a
--- /dev/null
+++ b/logging/jul/src/main/java/io/helidon/logging/jul/HelidonConsoleHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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 io.helidon.logging.jul;
+
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.StreamHandler;
+
+/**
+ * A {@link StreamHandler} that writes to {@link System#out standard out} and uses a {@link HelidonFormatter} for formatting.
+ * Sets the level to {@link Level#ALL} so that level filtering is performed solely by the loggers.
+ */
+public class HelidonConsoleHandler extends StreamHandler {
+
+ /**
+ * Creates a new {@link HelidonConsoleHandler} configured with:
+ *
+ * - the output stream set to {@link System#out}
+ * - the formatter set to a {@link HelidonFormatter}
+ * - the level set to {@link Level#ALL}
+ *
.
+ */
+ public HelidonConsoleHandler() {
+ setOutputStream(System.out);
+ setLevel(Level.ALL); // Handlers should not filter, loggers should
+ setFormatter(new HelidonFormatter());
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ super.publish(record);
+ flush();
+ }
+
+ @Override
+ public void close() {
+ flush();
+ }
+
+}
diff --git a/logging/jul/src/main/java/io/helidon/logging/jul/HelidonFormatter.java b/logging/jul/src/main/java/io/helidon/logging/jul/HelidonFormatter.java
new file mode 100644
index 000000000..fa9b8ea68
--- /dev/null
+++ b/logging/jul/src/main/java/io/helidon/logging/jul/HelidonFormatter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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 io.helidon.logging.jul;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.SimpleFormatter;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import io.helidon.logging.common.HelidonMdc;
+
+/**
+ * A {@link SimpleFormatter} that replaces all occurrences of MDC tags like {@code %X{value}} with specific values.
+ * It also supports replacement of {@code "!thread!"} with the current thread.
+ */
+public class HelidonFormatter extends SimpleFormatter {
+ private static final String THREAD = "thread";
+ private static final String THREAD_TOKEN = "!" + THREAD + "!";
+ private static final Pattern THREAD_PATTERN = Pattern.compile(THREAD_TOKEN);
+ private static final Pattern X_VALUE = Pattern.compile("(\\s?%X\\{)(\\S*?)(})");
+ private static final Map PATTERN_CACHE = new HashMap<>();
+ private static final String JUL_FORMAT_PROP_KEY = "java.util.logging.SimpleFormatter.format";
+ private final String format = LogManager.getLogManager().getProperty(JUL_FORMAT_PROP_KEY);
+ private final Set parsedProps = new HashSet<>();
+ private final boolean thread;
+
+ HelidonFormatter() {
+ thread = format.contains(THREAD_TOKEN) || format.contains("%X{" + THREAD + "}");
+ Matcher matcher = X_VALUE.matcher(format);
+ while (matcher.find()) {
+ parsedProps.add(matcher.group(2));
+ }
+ }
+
+ @Override
+ public String format(LogRecord record) {
+ String message = thread ? thread() : format;
+ for (String parsedKey : parsedProps) {
+ String value = HelidonMdc.get(parsedKey).orElse("");
+ message = PATTERN_CACHE.computeIfAbsent(parsedKey, key -> Pattern.compile("%X\\{" + key + "}"))
+ .matcher(message).replaceAll(value);
+ }
+ return formatRow(record, message);
+ }
+
+ private String thread() {
+ String currentThread = Thread.currentThread().toString();
+ String message = PATTERN_CACHE.computeIfAbsent(THREAD, key -> Pattern.compile("%X\\{" + THREAD + "}"))
+ .matcher(format).replaceAll(currentThread);
+ message = THREAD_PATTERN.matcher(message).replaceAll(currentThread);
+ return message;
+ }
+
+ //Copied from SimpleFormatter
+ private String formatRow(LogRecord record, String format) {
+ ZonedDateTime zdt = ZonedDateTime.ofInstant(
+ record.getInstant(), ZoneId.systemDefault());
+ String source;
+ if (record.getSourceClassName() != null) {
+ source = record.getSourceClassName();
+ if (record.getSourceMethodName() != null) {
+ source += " " + record.getSourceMethodName();
+ }
+ } else {
+ source = record.getLoggerName();
+ }
+ String message = formatMessage(record);
+ String throwable = "";
+ if (record.getThrown() != null) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ pw.println();
+ record.getThrown().printStackTrace(pw);
+ pw.close();
+ throwable = sw.toString();
+ }
+ return String.format(format,
+ zdt,
+ source,
+ record.getLoggerName(),
+ record.getLevel().getLocalizedName(),
+ message,
+ throwable);
+ }
+}
diff --git a/logging/jul/src/main/java/io/helidon/logging/jul/JulMdc.java b/logging/jul/src/main/java/io/helidon/logging/jul/JulMdc.java
new file mode 100644
index 000000000..52329368c
--- /dev/null
+++ b/logging/jul/src/main/java/io/helidon/logging/jul/JulMdc.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates.
+ *
+ * Licensed 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 io.helidon.logging.jul;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * MDC implementation for Java Util Logging framework.
+ */
+public class JulMdc {
+
+ private static final ThreadLocal