log trace (1) - @Sync 父子線程日誌MDC處理

在線程執行前,將原線程上下文中的MDC信息注入到新的線程中java

增長日誌輸出標識 

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface MDC {

}

切面: spring

import java.util.UUID;

import lombok.extern.slf4j.Slf4j;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.slf4j.MDC;

@Slf4j
public abstract class MdcInterceptor {
    protected final static String LOG_ID = "logId";

    @Pointcut("execution(* com.noob.api..*.*(..)) || @annotation(com.noob.aspectj.MDC)")
    public void pointCut() {
        // do nothings
    };

    @Around(value = "pointCut()")
    public Object invoke(ProceedingJoinPoint point) throws Throwable {
        Object result;
        try {
            setTradeId();
            result = point.proceed(point.getArgs());
            removeTraceId();
        } catch (Throwable throwable) {
            throw throwable;
        } finally {
            removeTraceId();
        }
        return result;
    }

    /**
     * 璁劇疆traceId
     */
    public static void setTradeId() {
        try {
            MDC.put(LOG_ID, UUID.randomUUID().toString().replace("-", ""));
        } catch (Exception e) {
            log.error("set log no exception", e);
        }
    }

    /**
     * remove traceId
     */
    public static void removeTraceId() {
        try {
            MDC.remove(LOG_ID);
        } catch (Exception e) {
            log.error("remove log no exception", e);
        }
    }
}

@Sync繼承MDC上下文

在使用@Sync時,Spring默認提供的TaskExecutor下的ThreadPoolTaskExecutor沒法支持MDC在線程切換時上下文內容的繼承。由於MDC本質上使用ThreadLocal來保存上下文。apache

須要注意的是:切面的織入必定要符合【外部調入】的原則!json

解決方法:api

重寫ThreadPoolTaskExecutor,在callable(真實處理過程)前注入父線程內容:
MDC.setContextMap(MDC.getCopyOfContextMap());dom

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.collections.MapUtils;
import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.google.common.base.Strings;
import com.alibaba.fastjson.JSONObject;

@Slf4j
public class MdcThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    private final static String LOG_ID           = "logId";

    /**
     * 
     */
    private static final long   serialVersionUID = 1L;

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        Map<String, String> context = MDC.getCopyOfContextMap();
        log.info("----MDC content:{}",  JSONObject.toJSONString(context));
        return super.submit(() -> {
            // 將父線程的MDC內容傳給子線程
                T result = null;
                if (MapUtils.isNotEmpty(context) && !Strings.isNullOrEmpty(context.get(LOG_ID))) {
                    MDC.setContextMap(context);
                } else {
                    MDC.put(LOG_ID, UUID.randomUUID().toString().replace("-", "")); //爲空設置新值
                }
                try {
                    result = task.call();
                } finally {
                    try {
                        MDC.clear();
                    } catch (Exception e2) {
                        log.warn("mdc clear exception.", e2);
                    }
                }
                return result;
            });
    }

}
相關文章
相關標籤/搜索