MDC logging support (#2479)

MDC logging support

Signed-off-by: David Kral <david.k.kral@oracle.com>
This commit is contained in:
David Král
2020-11-10 20:59:13 +01:00
committed by GitHub
parent 332870ca03
commit 44df56bd41
44 changed files with 1625 additions and 8 deletions

View File

@@ -841,6 +841,27 @@
<artifactId>helidon-microprofile-tests-junit5</artifactId>
<version>${helidon.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>io.helidon.logging</groupId>
<artifactId>helidon-logging-common</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.logging</groupId>
<artifactId>helidon-logging-jul</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.logging</groupId>
<artifactId>helidon-logging-slf4j</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.logging</groupId>
<artifactId>helidon-logging-log4j</artifactId>
<version>${helidon.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -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

View File

@@ -42,5 +42,9 @@
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-service-loader</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -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<DataPropagationProvider> 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 <T> Callable<T> wrap(Callable<T> task) {
@SuppressWarnings(value = "unchecked")
protected <T> Callable<T> wrap(Callable<T> task) {
Optional<Context> context = Contexts.context();
if (context.isPresent()) {
return () -> Contexts.runInContext(context.get(), task);
Map<Class<?>, 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> context = Contexts.context();
if (context.isPresent()) {
return () -> Contexts.runInContext(context.get(), command);
Map<Class<?>, 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;
}

View File

@@ -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 <T> an actual type of the data which will be propagated
*/
public interface DataPropagationProvider<T> {
/**
* 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();
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -79,6 +79,7 @@
<version.lib.jsonp-impl>1.1.6</version.lib.jsonp-impl>
<version.lib.junit>5.6.2</version.lib.junit>
<version.lib.junit4>4.13.1</version.lib.junit4>
<version.lib.log4j>2.13.3</version.lib.log4j>
<version.lib.mariadb-java-client>2.6.2</version.lib.mariadb-java-client>
<version.lib.maven-wagon>2.10</version.lib.maven-wagon>
<version.lib.microprofile-config>1.4</version.lib.microprofile-config>
@@ -742,6 +743,11 @@
<artifactId>slf4j-simple</artifactId>
<version>${version.lib.slf4j}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${version.lib.log4j}</version>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>

37
logging/common/pom.xml Normal file
View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>helidon-logging-project</artifactId>
<groupId>io.helidon.logging</groupId>
<version>2.1.1-SNAPSHOT</version>
</parent>
<artifactId>helidon-logging-common</artifactId>
<name>Helidon Logging Common</name>
<dependencies>
<dependency>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-service-loader</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -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<MdcProvider> 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<String> get(String key) {
return MDC_PROVIDERS.stream()
.map(provider -> provider.get(key))
.filter(Objects::nonNull)
.findFirst();
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;
}

56
logging/jul/pom.xml Normal file
View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>helidon-logging-project</artifactId>
<groupId>io.helidon.logging</groupId>
<version>2.1.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helidon-logging-jul</artifactId>
<name>Helidon Java Util Logging Integration</name>
<dependencies>
<dependency>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-context</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.logging</groupId>
<artifactId>helidon-logging-common</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -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:
* <ul>
* <li>the output stream set to {@link System#out}</li>
* <li>the formatter set to a {@link HelidonFormatter}</li>
* <li>the level set to {@link Level#ALL}</li>
* </ul>.
*/
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();
}
}

View File

@@ -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<String, Pattern> 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<String> 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);
}
}

View File

@@ -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<Map<String, String>> MDC_PROPERTIES = ThreadLocal.withInitial(HashMap::new);
private JulMdc() {
throw new IllegalStateException("This class cannot be instantiated");
}
static void put(String key, String value) {
MDC_PROPERTIES.get().put(key, value);
}
/**
* Return value bound to the key from the MDC storage.
*
* @param key key value
* @return value bound to the key
*/
public static String get(String key) {
return MDC_PROPERTIES.get().get(key);
}
static void remove(String key) {
MDC_PROPERTIES.get().remove(key);
}
static void clear() {
MDC_PROPERTIES.get().clear();
}
static Map<String, String> properties() {
return new HashMap<>(MDC_PROPERTIES.get());
}
static void properties(Map<String, String> properties) {
MDC_PROPERTIES.set(new HashMap<>(properties));
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.Map;
import io.helidon.common.context.spi.DataPropagationProvider;
/**
* This is propagator of JUL MDC values between different threads.
* This class is loaded and used via SPI.
*/
public class JulMdcPropagator implements DataPropagationProvider<Map<String, String>> {
@Override
public Map<String, String> data() {
return JulMdc.properties();
}
@Override
public void propagateData(Map<String, String> data) {
JulMdc.properties(data);
}
@Override
public void clearData() {
JulMdc.clear();
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 io.helidon.logging.common.spi.MdcProvider;
/**
* Provider for setting MDC values to the Java Util Logging MDC support.
* This class is loaded and used via SPI.
*/
public class JulMdcProvider implements MdcProvider {
@Override
public void put(String key, String value) {
JulMdc.put(key, value);
}
@Override
public void remove(String key) {
JulMdc.remove(key);
}
@Override
public void clear() {
JulMdc.clear();
}
@Override
public String get(String key) {
return JulMdc.get(key);
}
}

View File

@@ -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 Java util logging MDC support.
*/
package io.helidon.logging.jul;

View File

@@ -0,0 +1,31 @@
/*
* 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 Java Util Logging MDC support module.
*/
module io.helidon.logging.jul {
requires java.logging;
requires io.helidon.common;
requires io.helidon.common.context;
requires io.helidon.logging.common;
exports io.helidon.logging.jul;
provides io.helidon.common.context.spi.DataPropagationProvider with io.helidon.logging.jul.JulMdcPropagator;
provides io.helidon.logging.common.spi.MdcProvider with io.helidon.logging.jul.JulMdcProvider;
}

View File

@@ -0,0 +1,16 @@
#
# Copyright (c) 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.
# 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.
#
io.helidon.logging.jul.JulMdcPropagator

View File

@@ -0,0 +1,16 @@
#
# Copyright (c) 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.
# 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.
#
io.helidon.logging.jul.JulMdcProvider

View File

@@ -0,0 +1,108 @@
/*
* 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.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import io.helidon.common.LogConfig;
import io.helidon.common.context.Context;
import io.helidon.common.context.Contexts;
import io.helidon.common.context.ExecutorException;
import io.helidon.logging.common.HelidonMdc;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Test proper function of Jul MDC propagator and provider.
*/
public class JulMdcTest {
private static final Logger LOGGER = Logger.getLogger(JulMdcTest.class.getName());
private static final ByteArrayOutputStream OUTPUT_STREAM = new ByteArrayOutputStream();
private static final PrintStream TEST_STREAM = new PrintStream(OUTPUT_STREAM);
private static final String TEST_KEY = "test";
private static final String TEST_VALUE = "value";
@Test
public void testMdc() {
PrintStream original = System.out;
try {
System.setOut(TEST_STREAM);
LogConfig.initClass();
OUTPUT_STREAM.reset();
HelidonMdc.set(TEST_KEY, TEST_VALUE);
String message = "This is test logging message";
String thread = Thread.currentThread().toString();
String logMessage = logMessage(message);
assertThat(logMessage, endsWith(thread + ": " + message + " " + TEST_VALUE + System.lineSeparator()));
HelidonMdc.remove(TEST_KEY);
logMessage = logMessage(message);
assertThat(logMessage, endsWith(thread + ": " + message + " " + System.lineSeparator()));
HelidonMdc.set(TEST_KEY, TEST_VALUE);
HelidonMdc.clear();
logMessage = logMessage(message);
assertThat(logMessage, endsWith(thread + ": " + message + " " + System.lineSeparator()));
} finally {
System.setOut(original);
}
}
private String logMessage(String message) {
try {
LOGGER.info(message);
return OUTPUT_STREAM.toString();
} finally {
OUTPUT_STREAM.reset();
}
}
@Test
public void testThreadPropagation() {
HelidonMdc.set(TEST_KEY, TEST_VALUE);
Context context = Context.create();
ExecutorService executor = Contexts.wrap(Executors.newFixedThreadPool(1));
Contexts.runInContext(context, () -> {
try {
String value = executor.submit(new TestCallable()).get();
assertThat(value, is(TEST_VALUE));
} catch (Exception e) {
throw new ExecutorException("failed to execute", e);
}
});
}
private static final class TestCallable implements Callable<String> {
@Override
public String call() {
return JulMdc.get(TEST_KEY);
}
}
}

View File

@@ -0,0 +1,24 @@
#
# 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.
#
# Send messages to the console
handlers=io.helidon.logging.jul.HelidonConsoleHandler
# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread
java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s %X{test}%n
# Global logging level. Can be overridden by specific loggers
.level=INFO

63
logging/log4j/pom.xml Normal file
View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>helidon-logging-project</artifactId>
<groupId>io.helidon.logging</groupId>
<version>2.1.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helidon-logging-log4j</artifactId>
<name>Helidon Log4J Integration</name>
<!-- Javadoc should not run here since log4j is multi-release jar and javadoc plugin does not recognize it as module.
This module also does not provide any specific user API so it is not required to have javadoc. -->
<properties>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
<dependencies>
<dependency>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-context</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.logging</groupId>
<artifactId>helidon-logging-common</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,44 @@
/*
* 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.log4j;
import java.util.Map;
import io.helidon.common.context.spi.DataPropagationProvider;
import org.apache.logging.log4j.ThreadContext;
/**
* This is propagator of Log4j MDC values between different threads.
* This class is loaded and used via SPI.
*/
public class Log4jMdcPropagator implements DataPropagationProvider<Map<String, String>> {
@Override
public Map<String, String> data() {
return ThreadContext.getContext();
}
@Override
public void propagateData(Map<String, String> data) {
ThreadContext.putAll(data);
}
@Override
public void clearData() {
ThreadContext.clearAll();
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.log4j;
import io.helidon.logging.common.spi.MdcProvider;
import org.apache.logging.log4j.ThreadContext;
/**
* Provider for setting MDC values to the Log4j MDC support.
* This class is loaded and used via SPI.
*/
public class Log4jMdcProvider implements MdcProvider {
@Override
public void put(String key, String value) {
ThreadContext.put(key, value);
}
@Override
public void remove(String key) {
ThreadContext.remove(key);
}
@Override
public void clear() {
ThreadContext.clearAll();
}
@Override
public String get(String key) {
return ThreadContext.get(key);
}
}

View File

@@ -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 Log4j MDC propagation support.
*/
package io.helidon.logging.log4j;

View File

@@ -0,0 +1,29 @@
/*
* 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 Log4j MDC module.
*/
module io.helidon.logging.log4j {
requires io.helidon.common.context;
requires org.apache.logging.log4j;
requires io.helidon.logging.common;
exports io.helidon.logging.log4j;
provides io.helidon.logging.common.spi.MdcProvider with io.helidon.logging.log4j.Log4jMdcProvider;
provides io.helidon.common.context.spi.DataPropagationProvider with io.helidon.logging.log4j.Log4jMdcPropagator;
}

View File

@@ -0,0 +1,16 @@
#
# Copyright (c) 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.
# 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.
#
io.helidon.logging.log4j.Log4jMdcPropagator

View File

@@ -0,0 +1,16 @@
#
# Copyright (c) 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.
# 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.
#
io.helidon.logging.log4j.Log4jMdcProvider

View File

@@ -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.log4j;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import io.helidon.common.context.Context;
import io.helidon.common.context.Contexts;
import io.helidon.common.context.ExecutorException;
import io.helidon.logging.common.HelidonMdc;
import org.apache.logging.log4j.ThreadContext;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Test proper function of Log4j MDC propagator and provider.
*/
public class Log4jMdcTest {
private static final String TEST_KEY = "test";
private static final String TEST_VALUE = "value";
@Test
public void testMdc() {
HelidonMdc.set(TEST_KEY, TEST_VALUE);
assertThat(ThreadContext.get(TEST_KEY), is(TEST_VALUE));
HelidonMdc.remove(TEST_KEY);
assertThat(ThreadContext.get(TEST_KEY), nullValue());
HelidonMdc.set(TEST_KEY, TEST_VALUE);
HelidonMdc.clear();
assertThat(ThreadContext.get(TEST_KEY), nullValue());
}
@Test
public void testThreadPropagation() {
HelidonMdc.set(TEST_KEY, TEST_VALUE);
Context context = Context.create();
ExecutorService executor = Contexts.wrap(Executors.newFixedThreadPool(1));
Contexts.runInContext(context, () -> {
try {
String value = executor.submit(new TestCallable()).get();
assertThat(value, is(TEST_VALUE));
} catch (Exception e) {
throw new ExecutorException("failed to execute", e);
}
});
}
private static final class TestCallable implements Callable<String> {
@Override
public String call() {
return ThreadContext.get(TEST_KEY);
}
}
}

39
logging/pom.xml Normal file
View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>helidon-project</artifactId>
<groupId>io.helidon</groupId>
<version>2.1.1-SNAPSHOT</version>
</parent>
<groupId>io.helidon.logging</groupId>
<artifactId>helidon-logging-project</artifactId>
<packaging>pom</packaging>
<name>Helidon Logging Project</name>
<modules>
<module>common</module>
<module>jul</module>
<module>slf4j</module>
<module>log4j</module>
</modules>
</project>

61
logging/slf4j/pom.xml Normal file
View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>helidon-logging-project</artifactId>
<groupId>io.helidon.logging</groupId>
<version>2.1.1-SNAPSHOT</version>
</parent>
<artifactId>helidon-logging-slf4j</artifactId>
<name>Helidon SLF4J Integration</name>
<dependencies>
<dependency>
<groupId>io.helidon.common</groupId>
<artifactId>helidon-common-context</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.logging</groupId>
<artifactId>helidon-logging-common</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,45 @@
/*
* 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.slf4j;
import java.util.Map;
import io.helidon.common.context.spi.DataPropagationProvider;
import org.slf4j.MDC;
/**
* This is propagator of Slf4j MDC values between different threads.
* This class is loaded and used via SPI.
*/
public class Slf4jMdcPropagator implements DataPropagationProvider<Map<String, String>> {
@Override
public Map<String, String> data() {
return MDC.getCopyOfContextMap();
}
@Override
public void propagateData(Map<String, String> data) {
MDC.setContextMap(data);
}
@Override
public void clearData() {
MDC.clear();
}
}

View File

@@ -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.logging.slf4j;
import io.helidon.logging.common.spi.MdcProvider;
import org.slf4j.MDC;
/**
* Provider for setting MDC values to the Slf4j MDC support.
* This class is loaded and used via SPI.
*/
public class Slf4jMdcProvider implements MdcProvider {
@Override
public void put(String key, String value) {
MDC.put(key, value);
}
@Override
public void remove(String key) {
MDC.remove(key);
}
@Override
public void clear() {
MDC.clear();
}
@Override
public String get(String key) {
return MDC.get(key);
}
}

View File

@@ -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 Slf4J MDC propagation support.
*/
package io.helidon.logging.slf4j;

View File

@@ -0,0 +1,30 @@
/*
* 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 Slf4j MDC module.
*/
module io.helidon.logging.slf4j {
requires io.helidon.common.context;
requires io.helidon.logging.common;
requires slf4j.api;
exports io.helidon.logging.slf4j;
provides io.helidon.common.context.spi.DataPropagationProvider with io.helidon.logging.slf4j.Slf4jMdcPropagator;
provides io.helidon.logging.common.spi.MdcProvider with io.helidon.logging.slf4j.Slf4jMdcProvider;
}

View File

@@ -0,0 +1,16 @@
#
# Copyright (c) 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.
# 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.
#
io.helidon.logging.slf4j.Slf4jMdcPropagator

View File

@@ -0,0 +1,16 @@
#
# Copyright (c) 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.
# 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.
#
io.helidon.logging.slf4j.Slf4jMdcProvider

View File

@@ -0,0 +1,76 @@
/*
* 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.
*/
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import io.helidon.common.context.Context;
import io.helidon.common.context.Contexts;
import io.helidon.common.context.ExecutorException;
import io.helidon.logging.common.HelidonMdc;
import org.junit.jupiter.api.Test;
import org.slf4j.MDC;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Test proper function of Slf4j MDC propagator and provider.
*/
public class Slf4jMdcTest {
private static final String TEST_KEY = "test";
private static final String TEST_VALUE = "value";
@Test
public void testMdc() {
HelidonMdc.set(TEST_KEY, TEST_VALUE);
assertThat(MDC.get(TEST_KEY), is(TEST_VALUE));
HelidonMdc.remove(TEST_KEY);
assertThat(MDC.get(TEST_KEY), nullValue());
HelidonMdc.set(TEST_KEY, TEST_VALUE);
HelidonMdc.clear();
assertThat(MDC.get(TEST_KEY), nullValue());
}
@Test
public void testThreadPropagation() {
HelidonMdc.set(TEST_KEY, TEST_VALUE);
Context context = Context.create();
ExecutorService executor = Contexts.wrap(Executors.newFixedThreadPool(1));
Contexts.runInContext(context, () -> {
try {
String value = executor.submit(new TestCallable()).get();
assertThat(value, is(TEST_VALUE));
} catch (Exception e) {
throw new ExecutorException("failed to execute", e);
}
});
}
private static final class TestCallable implements Callable<String> {
@Override
public String call() {
return MDC.get(TEST_KEY);
}
}
}

View File

@@ -180,6 +180,7 @@
<module>dbclient</module>
<module>messaging</module>
<module>fault-tolerance</module>
<module>logging</module>
</modules>