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