Slf4j MDC機制及日誌應用實戰

參考:https://blog.csdn.net/xxcupid/article/details/51955356(這個作者寫了一系列,挺容易上手看的)

下面是使用時須知

MDC 是日誌的增強功能,如果配置了MDC,並添加了相應的key value,就會在打日誌的時候把key對應的value打印出來。

如果不用MDC.clear()的話會有什麼影響?
答:如果都是用new Thread方法建立的線程沒有問題,因爲之後線程會消亡。
但是如果用ThreadPool線程池的話,線程是可以重用的,如果之前的線程的MDC內容沒有清除掉的話,再次重線程池中獲取到這個線程,會取出之前的數據(髒數據),會導致一些不可預期的錯誤,所以當前線程結束後一定要清掉。可以用上一篇提到的的AbstractRunnable這個類,封裝了clear操作,程序員只需關心業務即可,不要擔心忘記clear了。

我們來使用吧

首先再明確一點是,MDC使用的get 和 put 都是由XXXAdapter 中ThreadLocal 進行把控的,所以每個線程都是數據獨立的。
竟然他是用來做日誌的log打印的,其實和其他的沒什麼多少區別,只是用他的話,解決了多線程情況下的問題

實戰

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

    //注入MDC
    boolean log() default true;
}

@Aspect
@Component
public class AspectLogger {

    private final static char[] HEX_DIGITS_CAPITAL = {'0','1' ,'2','3','4','5','6','7','8','9',
            'A','B','C','D','E','F'};

    private final static char[] HEX_DIGITS_SMALL = {'0','1' ,'2','3','4','5','6','7','8','9',
            'a','b','c','d','e','f'};


    @Pointcut("@annotation(com.longchuang.ronghe.common.config.Logger)")
    public void pointcut() {
        // do nothing
    }

    @Before("pointcut()")
    public void doBefore(JoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        Logger annotation = method.getAnnotation(Logger.class);
        if(annotation.log()){
            JSONObject jsonObject = JSONObject.fromObject(point.getArgs()[0]);
            String traceLogId = (String)jsonObject.get("traceLogId");// 這裏寫死先
            if(StringUtils.isNotEmpty(traceLogId) && traceLogId.length() == 32){
                MDC.put("TRACE_LOG_ID",traceLogId);
            }else{
                createTraceLogId();
            }
        }
    }

    @After("pointcut()")
    public void doAfter(JoinPoint point){
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        Logger annotation = method.getAnnotation(Logger.class);
        if(annotation.log()){
            MDC.clear();
        }
    }

    private void createTraceLogId() {
        byte[] bytes = new byte[16];
        ThreadLocalRandom.current().nextBytes(bytes);
        MDC.put("TRACE_LOG_ID",byteArrayToHex(bytes,false));
    }

    /**
     * 將字節數組轉換爲16進制字符串
     * @param byteArray 字節數組
     * @param isCapital 是否輸出爲大寫字母
     * @return
     */
    public static String byteArrayToHex(byte[] byteArray , boolean isCapital){
        char[] resultCharArray = new char[byteArray.length * 2];
        int index = 0;
        char[] hexDigits = isCapital ? HEX_DIGITS_CAPITAL : HEX_DIGITS_SMALL;
        for (byte b : byteArray){
            resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
            resultCharArray[index++] = hexDigits[b & 0xf];
        }
        return new String(resultCharArray);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章