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();
}
}
参考
AsyncConfigurerSupport 自定义异步线程池
标签:code,String,MDC,KEY,org,import,logback,public 来源: https://www.cnblogs.com/hongdada/p/16167847.html