logback使用MDC打印租户code

HttpRequestMDCFilter拦截器统一处理

import com.****.config.AuthManager; import com.****.constant.MDCConstants; import org.slf4j.MDC; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component;  import javax.annotation.Resource; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration;  /**  * 在logback日志输出中增加MDC参数选项  * 注意,此Filter尽可能的放在其他Filter之前  * <p>  * 我们可以在logback.xml文件的layout部分,通过%X{key}的方式使用MDC中的变量  */ @Order(1) @Component public class HttpRequestMDCFilter implements Filter {      @Resource     private AuthManager authManager;     private String localIp;//本机IP      @Override     public void init(FilterConfig filterConfig) throws ServletException {         //getLocalIp         localIp = getLocalIp();     }      private String getLocalIp() {         try {             //一个主机有多个网络接口             Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();             while (netInterfaces.hasMoreElements()) {                 NetworkInterface netInterface = netInterfaces.nextElement();                 //每个网络接口,都会有多个网络地址,比如一定会有loopback地址,会有siteLocal地址等.以及IPV4或者IPV6    .                 Enumeration<InetAddress> addresses = netInterface.getInetAddresses();                 while (addresses.hasMoreElements()) {                     InetAddress address = addresses.nextElement();                     //get only :172.*,192.*,10.*                     if (address.isSiteLocalAddress() && !address.isLoopbackAddress()) {                         return address.getHostAddress();                     }                 }             }         } catch (Exception e) {             //         }         return null;     }       @Override     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {         HttpServletRequest hsr = (HttpServletRequest) request;         try {             mdc(hsr);         } catch (Exception e) {             //         }          try {             chain.doFilter(request, response);         } finally {             MDC.clear();//must be,threadLocal         }      }      private void mdc(HttpServletRequest hsr) {         MDC.put(MDCConstants.LOCAL_IP_MDC_KEY, localIp); //        MDC.put(MDCConstants.TIMESTAMP,  + System.currentTimeMillis());         MDC.put(MDCConstants.URI_MDC_KEY, hsr.getRequestURI());         MDC.put(MDCConstants.tenant, authManager.getTenantCode());     }      @Override     public void destroy() {      } } 

MDCConstants固定值

public interface MDCConstants {      String tenant = tenant;      String REQUEST_ID_HEADER = X-Request-ID;     String REQUEST_SEQ_HEADER = X-Request-Seq;     String REQUEST_ID_MDC_KEY = requestId;     String REQUEST_SEQ_MDC_KEY = requestSeq;     String NEXT_REQUEST_SEQ_MDC_KEY = nextRequestSeq;     String LOCAL_IP_MDC_KEY = localIp;     String URI_MDC_KEY = uri;     String TIMESTAMP = _timestamp_;//进入filter的时间戳     String COOKIE_KEY_PREFIX = _C_;     String HEADER_KEY_PREFIX = _H_;     String PARAMETER_KEY_PREFIX = _P_; } 

logback-spring.xml配置

    <property name=MONITOR_PATTERN               value=%d [%thread] %-5p [%c] [%F:%L] [trace=%X{X-B3-TraceId:-},span=%X{X-B3-SpanId:-},parent=%X{X-B3-ParentSpanId:-}][localIp=%X{localIp:-},tenant=%X{tenant:-},url=%X{uri:-}] - %msg%n/>  

新增了localIp,tenant,uri三个配置

MDC异步线程问题

使用上面的方式,发现异步线程,线程池,定时任务都无法打印,定时任务不说,没有这些参数,但是其他应该可以打印

兼容@Async

MdcTaskDecorator

import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; import org.springframework.core.task.TaskDecorator;  import java.util.Map;  /**  * @author qhong  * @date 2022/4/18 11:13  **/ @Slf4j public class MdcTaskDecorator implements TaskDecorator {      @Override     public Runnable decorate(Runnable runnable) {         // Right now: Web thread context !         // (Grab the current thread MDC data)         Map<String, String> contextMap = MDC.getCopyOfContextMap();         return () -> {             try {                 // Right now: @Async thread context !                 // (Restore the Web thread context's MDC data) when schedule use async,contextMap is null                 if (contextMap != null && !contextMap.isEmpty()) {                     MDC.setContextMap(contextMap);                 }                 runnable.run();             } catch (Exception e) {                 throw e;             } finally {                 MDC.clear();             }         };     } } 

MyAsyncUncaughtExceptionHandler

import lombok.extern.slf4j.Slf4j; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;  import java.lang.reflect.Method;  /**  * @author qhong  * @date 2022/4/18 14:14  **/ @Slf4j public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {     @Override     public void handleUncaughtException(Throwable ex, Method method, Object... params) {         log.info(class#method:  + method.getDeclaringClass().getName() + # + method.getName());         log.info(type        :  + ex.getClass().getName());         log.info(exception   :  + ex.getMessage());     } } 

AsyncConfigHandler

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurerSupport; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;  import java.util.concurrent.Executor;  /**  * @author qhong  * @date 2022/4/18 14:13  **/ @Configuration @EnableAsync public class AsyncConfigHandler extends AsyncConfigurerSupport {      @Override     public Executor getAsyncExecutor() {         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();         executor.setCorePoolSize(8);         executor.setMaxPoolSize(16);         executor.setQueueCapacity(16);         executor.setTaskDecorator(new MdcTaskDecorator());         //等待任务在关机时完成--表明等待所有线程执行完         //executor.setWaitForTasksToCompleteOnShutdown(true);         //等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止         //executor.setAwaitTerminationSeconds(60 * 15);         //线程名称前缀         executor.setThreadNamePrefix(MyAsync-);         executor.initialize();         return executor;     }      @Override     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {         return new MyAsyncUncaughtExceptionHandler();     } } 

参考

logback日志与MDC机制

Java异步线程池中处理logback MDC

AsyncConfigurerSupport 自定义异步线程池