參考: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);
}
}