為了甩鍋,我寫了個牛逼的日志切面!
來源:r6d.cn/2Zrc
最近項目進入聯(lián)調(diào)階段,服務(wù)層的接口需要和協(xié)議層進行交互,協(xié)議層需要將入?yún)json字符串]組裝成服務(wù)層所需的json字符串,組裝的過程中很容易出錯。入?yún)⒊鲥e導(dǎo)致接口調(diào)試失敗問題在聯(lián)調(diào)中出現(xiàn)很多次,因此就想寫一個請求日志切面把入?yún)⑿畔⒋蛴∫幌拢瑫r協(xié)議層調(diào)用服務(wù)層接口名稱對不上也出現(xiàn)了幾次,通過請求日志切面就可以知道上層是否有沒有發(fā)起調(diào)用,方便前后端甩鍋還能拿出證據(jù)
寫在前面
切面介紹
集中處理某一關(guān)注點/橫切邏輯 可以很方便的添加/刪除關(guān)注點 侵入性少,增強代碼可讀性及可維護性 因此當想打印請求日志時很容易想到切面,對控制層代碼0侵入
切面的使用【基于注解】
切點注解:
通知注解:
@Before => 在切點之前執(zhí)行代碼 @After => 在切點之后執(zhí)行代碼 @AfterReturning => 切點返回內(nèi)容后執(zhí)行代碼,可以對切點的返回值進行封裝 @AfterThrowing => 切點拋出異常后執(zhí)行 @Around => 環(huán)繞,在切點前后執(zhí)行代碼
動手寫一個請求日志切面
使用@Pointcut定義切點
@Pointcut("execution(*?your_package.controller..*(..))")
public?void?requestServer()?{
}
使用@Before再切點前執(zhí)行
@Before("requestServer()")
public?void?doBefore(JoinPoint?joinPoint)?{
????ServletRequestAttributes?attributes?=?(ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
????HttpServletRequest?request?=?attributes.getRequest();
????LOGGER.info("===============================Start========================");
????LOGGER.info("IP?????????????????:?{}",?request.getRemoteAddr());
????LOGGER.info("URL????????????????:?{}",?request.getRequestURL().toString());
????LOGGER.info("HTTP?Method????????:?{}",?request.getMethod());
????LOGGER.info("Class?Method???????:?{}.{}",?joinPoint.getSignature().getDeclaringTypeName(),?joinPoint.getSignature().getName());
}
使用@Around打印進入控制層的入?yún)?/section>
@Around("requestServer()")
public?Object?doAround(ProceedingJoinPoint?proceedingJoinPoint)?throws?Throwable?{
????long?start?=?System.currentTimeMillis();
????Object?result?=?proceedingJoinPoint.proceed();
????LOGGER.info("Request?Params???????:?{}",?getRequestParams(proceedingJoinPoint));
????LOGGER.info("Result???????????????:?{}",?result);
????LOGGER.info("Time?Cost????????????:?{}?ms",?System.currentTimeMillis()?-?start);
????return?result;
}
private?Map ?getRequestParams(ProceedingJoinPoint?proceedingJoinPoint)?{
?????Map?requestParams?=?new?HashMap<>();
??????//參數(shù)名
?????String[]?paramNames?=?((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
?????//參數(shù)值
?????Object[]?paramValues?=?proceedingJoinPoint.getArgs();
?????for?(int?i?=?0;?i??????????Object?value?=?paramValues[i];
?????????//如果是文件對象
?????????if?(value?instanceof?MultipartFile)?{
?????????????MultipartFile?file?=?(MultipartFile)?value;
?????????????value?=?file.getOriginalFilename();??//獲取文件名
?????????}
?????????requestParams.put(paramNames[i],?value);
?????}
?????return?requestParams;
?}
@After方法調(diào)用后執(zhí)行
@After("requestServer()")
public?void?doAfter(JoinPoint?joinPoint)?{
????LOGGER.info("===============================End========================");
}
完整切面代碼
@Component
@Aspect
public?class?RequestLogAspect?{
????private?final?static?Logger?LOGGER?=?LoggerFactory.getLogger(RequestLogAspect.class);
????@Pointcut("execution(*?your_package.controller..*(..))")
????public?void?requestServer()?{
????}
????@Before("requestServer()")
????public?void?doBefore(JoinPoint?joinPoint)?{
????????ServletRequestAttributes?attributes?=?(ServletRequestAttributes)?
RequestContextHolder.getRequestAttributes();
????????HttpServletRequest?request?=?attributes.getRequest();
????????LOGGER.info("===============================Start========================");
????????LOGGER.info("IP?????????????????:?{}",?request.getRemoteAddr());
????????LOGGER.info("URL????????????????:?{}",?request.getRequestURL().toString());
????????LOGGER.info("HTTP?Method????????:?{}",?request.getMethod());
????????LOGGER.info("Class?Method???????:?{}.{}",?joinPoint.getSignature().getDeclaringTypeName(),?
?joinPoint.getSignature().getName());
????}
????@Around("requestServer()")
????public?Object?doAround(ProceedingJoinPoint?proceedingJoinPoint)?throws?Throwable?{
????????long?start?=?System.currentTimeMillis();
????????Object?result?=?proceedingJoinPoint.proceed();
????????LOGGER.info("Request?Params?????:?{}",?getRequestParams(proceedingJoinPoint));
????????LOGGER.info("Result???????????????:?{}",?result);
????????LOGGER.info("Time?Cost????????????:?{}?ms",?System.currentTimeMillis()?-?start);
????????return?result;
????}
????@After("requestServer()")
????public?void?doAfter(JoinPoint?joinPoint)?{
????????LOGGER.info("===============================End========================");
????}
????/**
?????*?獲取入?yún)?br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">?????*?@param?proceedingJoinPoint
?????*
?????*?@return
?????*?*/
????private?Map?getRequestParams(ProceedingJoinPoint?proceedingJoinPoint)?{
????????Map?requestParams?=?new?HashMap<>();
????????//參數(shù)名
????????String[]?paramNames?=?
((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
????????//參數(shù)值
????????Object[]?paramValues?=?proceedingJoinPoint.getArgs();
????????for?(int?i?=?0;?i?????????????Object?value?=?paramValues[i];
????????????//如果是文件對象
????????????if?(value?instanceof?MultipartFile)?{
????????????????MultipartFile?file?=?(MultipartFile)?value;
????????????????value?=?file.getOriginalFilename();??//獲取文件名
????????????}
????????????requestParams.put(paramNames[i],?value);
????????}
????????return?requestParams;
????}
}
高并發(fā)下請求日志切面

RequestInfo.java
@Data
public?class?RequestInfo?{
????private?String?ip;
????private?String?url;
????private?String?httpMethod;
????private?String?classMethod;
????private?Object?requestParams;
????private?Object?result;
????private?Long?timeCost;
}
環(huán)繞通知方法體
@Around("requestServer()")
public?Object?doAround(ProceedingJoinPoint?proceedingJoinPoint)?throws?Throwable?{
????long?start?=?System.currentTimeMillis();
????ServletRequestAttributes?attributes?=?(ServletRequestAttributes)?RequestContextHolder.getRequestAttributes();
????HttpServletRequest?request?=?attributes.getRequest();
????Object?result?=?proceedingJoinPoint.proceed();
????RequestInfo?requestInfo?=?new?RequestInfo();
????????????requestInfo.setIp(request.getRemoteAddr());
????requestInfo.setUrl(request.getRequestURL().toString());
????requestInfo.setHttpMethod(request.getMethod());
????requestInfo.setClassMethod(String.format("%s.%s",?proceedingJoinPoint.getSignature().getDeclaringTypeName(),
????????????proceedingJoinPoint.getSignature().getName()));
????requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
????requestInfo.setResult(result);
????requestInfo.setTimeCost(System.currentTimeMillis()?-?start);
????LOGGER.info("Request?Info??????:?{}",?JSON.toJSONString(requestInfo));
????return?result;
}

RequestErrorInfo.java
@Data
public?class?RequestErrorInfo?{
????private?String?ip;
????private?String?url;
????private?String?httpMethod;
????private?String?classMethod;
????private?Object?requestParams;
????private?RuntimeException?exception;
}
@AfterThrowing(pointcut?=?"requestServer()",?throwing?=?"e")
public?void?doAfterThrow(JoinPoint?joinPoint,?RuntimeException?e)?{
????ServletRequestAttributes?attributes?=?(ServletRequestAttributes)?RequestContextHolder.getRequestAttributes();
????HttpServletRequest?request?=?attributes.getRequest();
????RequestErrorInfo?requestErrorInfo?=?new?RequestErrorInfo();
????requestErrorInfo.setIp(request.getRemoteAddr());
????requestErrorInfo.setUrl(request.getRequestURL().toString());
????requestErrorInfo.setHttpMethod(request.getMethod());
????requestErrorInfo.setClassMethod(String.format("%s.%s",?joinPoint.getSignature().getDeclaringTypeName(),
????????????joinPoint.getSignature().getName()));
????requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
????requestErrorInfo.setException(e);
????LOGGER.info("Error?Request?Info??????:?{}",?JSON.toJSONString(requestErrorInfo));
}
@Component
@Aspect
public?class?RequestLogAspect?{
????private?final?static?Logger?LOGGER?=?LoggerFactory.getLogger(RequestLogAspect.class);
????@Pointcut("execution(*?your_package.controller..*(..))")
????public?void?requestServer()?{
????}
????@Around("requestServer()")
????public?Object?doAround(ProceedingJoinPoint?proceedingJoinPoint)?throws?Throwable?{
????????long?start?=?System.currentTimeMillis();
????????ServletRequestAttributes?attributes?=?(ServletRequestAttributes)?RequestContextHolder.getRequestAttributes();
????????HttpServletRequest?request?=?attributes.getRequest();
????????Object?result?=?proceedingJoinPoint.proceed();
????????RequestInfo?requestInfo?=?new?RequestInfo();
????????????????requestInfo.setIp(request.getRemoteAddr());
????????requestInfo.setUrl(request.getRequestURL().toString());
????????requestInfo.setHttpMethod(request.getMethod());
????????requestInfo.setClassMethod(String.format("%s.%s",?proceedingJoinPoint.getSignature().getDeclaringTypeName(),
????????????????proceedingJoinPoint.getSignature().getName()));
????????requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
????????requestInfo.setResult(result);
????????requestInfo.setTimeCost(System.currentTimeMillis()?-?start);
????????LOGGER.info("Request?Info??????:?{}",?JSON.toJSONString(requestInfo));
????????return?result;
????}
????@AfterThrowing(pointcut?=?"requestServer()",?throwing?=?"e")
????public?void?doAfterThrow(JoinPoint?joinPoint,?RuntimeException?e)?{
????????ServletRequestAttributes?attributes?=?(ServletRequestAttributes)?RequestContextHolder.getRequestAttributes();
????????HttpServletRequest?request?=?attributes.getRequest();
????????RequestErrorInfo?requestErrorInfo?=?new?RequestErrorInfo();
????????requestErrorInfo.setIp(request.getRemoteAddr());
????????requestErrorInfo.setUrl(request.getRequestURL().toString());
????????requestErrorInfo.setHttpMethod(request.getMethod());
????????requestErrorInfo.setClassMethod(String.format("%s.%s",?joinPoint.getSignature().getDeclaringTypeName(),
????????????????joinPoint.getSignature().getName()));
????????requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
????????requestErrorInfo.setException(e);
????????LOGGER.info("Error?Request?Info??????:?{}",?JSON.toJSONString(requestErrorInfo));
????}
????/**
?????*?獲取入?yún)?br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">?????*?@param?proceedingJoinPoint
?????*
?????*?@return
?????*?*/
????private?Map?getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint?proceedingJoinPoint)?{
????????//參數(shù)名
????????String[]?paramNames?=?((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
????????//參數(shù)值
????????Object[]?paramValues?=?proceedingJoinPoint.getArgs();
????????return?buildRequestParam(paramNames,?paramValues);
????}
????private?Map?getRequestParamsByJoinPoint(JoinPoint?joinPoint)?{
????????//參數(shù)名
????????String[]?paramNames?=?((MethodSignature)joinPoint.getSignature()).getParameterNames();
????????//參數(shù)值
????????Object[]?paramValues?=?joinPoint.getArgs();
????????return?buildRequestParam(paramNames,?paramValues);
????}
????private?Map?buildRequestParam(String[]?paramNames,?Object[]?paramValues)?{
????????Map?requestParams?=?new?HashMap<>();
????????for?(int?i?=?0;?i?????????????Object?value?=?paramValues[i];
????????????//如果是文件對象
????????????if?(value?instanceof?MultipartFile)?{
????????????????MultipartFile?file?=?(MultipartFile)?value;
????????????????value?=?file.getOriginalFilename();??//獲取文件名
????????????}
????????????requestParams.put(paramNames[i],?value);
????????}
????????return?requestParams;
????}
????@Data
????public?class?RequestInfo?{
????????private?String?ip;
????????private?String?url;
????????private?String?httpMethod;
????????private?String?classMethod;
????????private?Object?requestParams;
????????private?Object?result;
????????private?Long?timeCost;
????}
????@Data
????public?class?RequestErrorInfo?{
????????private?String?ip;
????????private?String?url;
????????private?String?httpMethod;
????????private?String?classMethod;
????????private?Object?requestParams;
????????private?RuntimeException?exception;
????}
}

添加攔截器
public?class?LogInterceptor?implements?HandlerInterceptor?{
????private?final?static?String?TRACE_ID?=?"traceId";
????@Override
????public?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler)?throws?Exception?{
????????String?traceId?=?java.util.UUID.randomUUID().toString().replaceAll("-",?"").toUpperCase();
????????ThreadContext.put("traceId",?traceId);
????????return?true;
????}
????@Override
????public?void?postHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler,?ModelAndView?modelAndView)
????????????throws?Exception?{
????}
????@Override
????public?void?afterCompletion(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler,?Exception?ex)
????????????throws?Exception?{
????????ThreadContext.?remove(TRACE_ID);
????}
}
修改日志配置文件 在原來的日志格式中 添加traceId的占位符
"pattern">[TRACEID:%X{traceId}]?%d{HH:mm:ss.SSS}?%-5level?%class{-1}.%M()/%L?-?%msg%xEx%n
執(zhí)行效果


歡迎關(guān)注“Java引導(dǎo)者”,我們分享最有價值的Java的干貨文章,助力您成為有思想的Java開發(fā)工程師!
評論
圖片
表情
