Java 具有完整未来的MDC记录器
我使用的是MDC Logger,除了一个案例外,它对我来说非常适合。在我们使用CompletableFuture的代码中,对于创建的线程,MDC数据不会传递到下一个线程,因此日志会失败。例如,我在下面使用的代码片段中创建了新线程Java 具有完整未来的MDC记录器,java,spring-boot,slf4j,completable-future,mdc,Java,Spring Boot,Slf4j,Completable Future,Mdc,我使用的是MDC Logger,除了一个案例外,它对我来说非常适合。在我们使用CompletableFuture的代码中,对于创建的线程,MDC数据不会传递到下一个线程,因此日志会失败。例如,我在下面使用的代码片段中创建了新线程 CompletableFuture.runAsync(() -> getAcountDetails(user)); 日志的结果如下所示 2019-04-29 11:44:13,690 INFO | /app/rest/controller/userdetail
CompletableFuture.runAsync(() -> getAcountDetails(user));
日志的结果如下所示
2019-04-29 11:44:13,690 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] RestServiceExecutor: service:
2019-04-29 11:44:13,690 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] RestServiceExecutor:
2019-04-29 11:44:13,779 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] UserDetailsRepoImpl:
2019-04-29 11:44:13,950 INFO [ForkJoinPool.commonPool-worker-3] RestServiceExecutor: header:
2019-04-29 11:44:13,950 INFO [ForkJoinPool.commonPool-worker-3] RestServiceExecutor: service:
2019-04-29 11:44:14,012 INFO [ForkJoinPool.commonPool-worker-3] CommonMasterDataServiceImpl: Cache: Retrieving Config Data details.
2019-04-29 11:44:14,028 INFO [ForkJoinPool.commonPool-worker-3] CommonMasterDataServiceImpl: Cache: Retrieved Config Data details : 1
2019-04-29 11:44:14,028 INFO [ForkJoinPool.commonPool-worker-3] CommonMasterDataServiceImpl: Cache: Retrieving Config Data details.
2019-04-29 11:44:14,033 INFO [ForkJoinPool.commonPool-worker-3] CommonMasterDataServiceImpl: Cache: Retrieved Config Data details : 1
2019-04-29 11:44:14,147 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] SecondaryCacheServiceImpl: Fetching from secondary cache
2019-04-29 11:44:14,715 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5] CommonMasterDataServiceImpl: Cache: Retrieving Config Data details.
2019-04-29 11:44:14,749 INFO | /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |[http-nio-8182-exec-5]
下面是我的MDC数据,它不能通过线程[ForkJoinPool.commonPool-worker-3]
| /app/rest/controller/userdetails | f80fdc1f-8123-3932-a405-dda2dc2a80d5 |
下面是我的logback.xml配置,其中sessionID是MDC数据
<configuration scan="true">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>utf-8</charset>
<Pattern>%d %-5level %X{sessionID} [%thread] %logger{0}: %msg%n</Pattern>
</encoder>
</appender>
</configuration>
这对TaskExecutor非常有效。但我还没有找到任何解决CompletableFuture的方法。创建包装器方法
static CompletableFuture<Void> myMethod(Runnable runnable) {
Map<String, String> previous = MDC.getCopyOfContextMap();
return CompletableFuture.runAsync(() -> {
MDC.setContextMap(previous);
try {
runnable.run();
} finally {
MDC.clear();
}
});
}
静态CompletableFuture myMethod(可运行可运行){
Map previous=MDC.getCopyOfContextMap();
返回CompletableFuture.runAsync(()->{
MDC.setContextMap(先前版本);
试一试{
runnable.run();
}最后{
MDC.clear();
}
});
}
并使用它而不是CompletableFuture.runAsync
创建包装器方法
static CompletableFuture<Void> myMethod(Runnable runnable) {
Map<String, String> previous = MDC.getCopyOfContextMap();
return CompletableFuture.runAsync(() -> {
MDC.setContextMap(previous);
try {
runnable.run();
} finally {
MDC.clear();
}
});
}
静态CompletableFuture myMethod(可运行可运行){
Map previous=MDC.getCopyOfContextMap();
返回CompletableFuture.runAsync(()->{
MDC.setContextMap(先前版本);
试一试{
runnable.run();
}最后{
MDC.clear();
}
});
}
使用它而不是CompletableFuture.runAsync
我的解决方案主题将是to(它将与JDK 9+一起工作,因为自该版本以来,公开了两个可重写的方法)
使整个生态系统了解MDC
为此,我们需要解决以下情况:
- 我们什么时候才能从这个类中获得CompletableFuture的新实例?→ 我们需要返回一个支持MDC的版本
- 我们什么时候才能从这个类之外获得CompletableFuture的新实例?→ 我们需要返回一个支持MDC的版本
- 在CompletableFuture类中使用哪个执行器?→ 在任何情况下,我们都需要确保所有执行人都了解MDC
CompletableFuture
版本类。我的版本如下所示
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.function.Supplier;
public class MDCAwareCompletableFuture<T> extends CompletableFuture<T> {
public static final ExecutorService MDC_AWARE_ASYNC_POOL = new MDCAwareForkJoinPool();
@Override
public CompletableFuture newIncompleteFuture() {
return new MDCAwareCompletableFuture();
}
@Override
public Executor defaultExecutor() {
return MDC_AWARE_ASYNC_POOL;
}
public static <T> CompletionStage<T> getMDCAwareCompletionStage(CompletableFuture<T> future) {
return new MDCAwareCompletableFuture<>()
.completeAsync(() -> null)
.thenCombineAsync(future, (aVoid, value) -> value);
}
public static <T> CompletionStage<T> getMDCHandledCompletionStage(CompletableFuture<T> future,
Function<Throwable, T> throwableFunction) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return getMDCAwareCompletionStage(future)
.handle((value, throwable) -> {
setMDCContext(contextMap);
if (throwable != null) {
return throwableFunction.apply(throwable);
}
return value;
});
}
}
要包装的实用方法如下
public static <T> Callable<T> wrapWithMdcContext(Callable<T> task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
return task.call();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static Runnable wrapWithMdcContext(Runnable task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
task.run();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static void setMDCContext(Map<String, String> contextMap) {
MDC.clear();
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
}
我的解决方案主题是(它将与JDK 9+一起工作,因为自该版本以来,公开了两个可重写的方法) 使整个生态系统了解MDC 为此,我们需要解决以下情况:
- 我们什么时候才能从这个类中获得CompletableFuture的新实例?→ 我们需要返回一个支持MDC的版本
- 我们什么时候才能从这个类之外获得CompletableFuture的新实例?→ 我们需要返回一个支持MDC的版本
- 在CompletableFuture类中使用哪个执行器?→ 在任何情况下,我们都需要确保所有执行人都了解MDC
CompletableFuture
版本类。我的版本如下所示
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.function.Supplier;
public class MDCAwareCompletableFuture<T> extends CompletableFuture<T> {
public static final ExecutorService MDC_AWARE_ASYNC_POOL = new MDCAwareForkJoinPool();
@Override
public CompletableFuture newIncompleteFuture() {
return new MDCAwareCompletableFuture();
}
@Override
public Executor defaultExecutor() {
return MDC_AWARE_ASYNC_POOL;
}
public static <T> CompletionStage<T> getMDCAwareCompletionStage(CompletableFuture<T> future) {
return new MDCAwareCompletableFuture<>()
.completeAsync(() -> null)
.thenCombineAsync(future, (aVoid, value) -> value);
}
public static <T> CompletionStage<T> getMDCHandledCompletionStage(CompletableFuture<T> future,
Function<Throwable, T> throwableFunction) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return getMDCAwareCompletionStage(future)
.handle((value, throwable) -> {
setMDCContext(contextMap);
if (throwable != null) {
return throwableFunction.apply(throwable);
}
return value;
});
}
}
要包装的实用方法如下
public static <T> Callable<T> wrapWithMdcContext(Callable<T> task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
return task.call();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static Runnable wrapWithMdcContext(Runnable task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
task.run();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static void setMDCContext(Map<String, String> contextMap) {
MDC.clear();
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
}
检查检查,但不要将其称为
myMethod
runAsyncWithMDC
或类似Hanks Talex,它应该可以工作,但问题是我们在多个地方使用completablefuture,我们可能需要在每个地方进行更新。对吗?因为您使用静态方法来运行异步调用,所以无法拦截它。所以你必须修改每个调用。但是不要调用它myMethod
runAsyncWithMDC
或类似Hanks Talex,它应该可以工作,但问题是我们在多个地方使用completablefuture,我们可能需要在每个地方进行更新。对吗?因为您使用静态方法来运行异步调用,所以无法拦截它。所以你必须修改每个电话。