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