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() { } }
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_"; }
<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三个配置
使用上面的方式,发现异步线程,线程池,定时任务都无法打印,定时任务不说,没有这些参数,但是其他应该可以打印
兼容@Async
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(); } }; } }
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()); } }
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 自定义异步线程池