其他分享
首页 > 其他分享> > logback使用MDC打印租户code

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 自定义异步线程池

标签:code,String,MDC,KEY,org,import,logback,public
来源: https://www.cnblogs.com/hongdada/p/16167847.html