SpringAOP .切面,是Spring得一大特性,使用目前是使用得面還很窄,用氣對Controller層做日志管理,其實還可以做參數校驗和RSA校驗等一系列前置操作。
在所有Controller得每一個方法裡面做請求日志記錄,會讓代碼變得很臃腫和閱讀得低效。
沒有使用統一請求日志記錄得時候,我記錄Controller的日志十分痛苦:
@RestController
@RequestMapping("gua")
public class GuaController {
private Logger logger = LoggerFactory.getLogger(GuaController.class);
@GetMapping("str")
public ResponseData str() {
logger.info("Request 請求日志: 請求控制器,請求位址 ,請求方法,請求傳回值。。。。。。還有很多需要記錄的細節,為了追溯。。。");
return ResponseDataUtil.buildSuccess("Result String");
}
@GetMapping("data")
public ResponseData data() {
logger.info("Request 請求日志: 請求控制器,請求位址 ,請求方法,請求傳回值。。。。。。還有很多需要記錄的細節,為了追溯。。。");
return ResponseDataUtil.buildSuccess(new User());
}
@GetMapping("map")
public ResponseData map() {
logger.info("Request 請求日志: 請求控制器,請求位址 ,請求方法,請求傳回值。。。。。。還有很多需要記錄的細節,為了追溯。。。");
HashMap<String, Object> map = new HashMap<>(1);
map.put("Result", "Map");
return ResponseDataUtil.buildSuccess(map);
}
}
現在隻有三個映射,要是有更多呢?全都進行CV程式設計嗎?日志變得雜亂不堪,如果你說不需要記錄,那麼業務出現細微差錯或者需要追溯源頭這種情況出現,就會讓自己變得束手無策。日志得記錄,是為了輔助我們進行程式執行追溯得。但是日志記錄得雜亂無章,讓程式越來亂。
我是用AOP做日志記錄,在執行映射方法之前進行資料紀記錄,在執行完成以後進行傳回資料記錄,然後再統一輸出。
下面是我得AOP代碼,非常簡單:
@Component
@Aspect
public class RequestLogAspect {
private final Logger logger = LoggerFactory.getLogger(RequestLogAspect.class);
/**
* 定義切點
*/
@Pointcut("execution(* com.dong.gua.web.controller..*(..))")
public void requestServer() {
}
@Around("requestServer()")
public Object doAround(ProceedingJoinPoint pjp) {
//記錄請求開始執行時間:
long beginTime = System.currentTimeMillis();
//擷取請求資訊
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = sra.getRequest();
//擷取代理位址、請求位址、請求類名、方法名
String remoteAddress = IPUtils.getProxyIP(request);
String requestURI = request.getRequestURI();
String methodName = pjp.getSignature().getName();
String clazzName = pjp.getTarget().getClass().getSimpleName();
//擷取請求參數:
MethodSignature ms = (MethodSignature) pjp.getSignature();
//擷取請求參數類型
String[] parameterNames = ms.getParameterNames();
//擷取請求參數值
Object[] parameterValues = pjp.getArgs();
StringBuilder sb = new StringBuilder();
//組合請求參數,進行日志列印
if (parameterNames != null && parameterNames.length > 0) {
for (int i = 0; i < parameterNames.length; i++) {
if (parameterNames[i].equals("bindingResult")) {
break;
}
if ((parameterValues[i] instanceof HttpServletRequest) || (parameterValues[i] instanceof HttpServletResponse)) {
sb.
append("[").
append(parameterNames[i]).append("=").append(parameterValues[i])
.append("]");
} else {
sb.
append("[").
append(parameterNames[i]).append("=")
.append(JSON.toJSONString(parameterValues[i], SerializerFeature.WriteDateUseDateFormat))
.append("]");
}
}
}
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable throwable) {
//請求操縱失敗
//記錄錯誤日志
logger.error("(ง•̀_•́)ง (っ•̀ω•́)っ 切面處理請求錯誤! IP資訊(ง•̀_•́)ง->: 【{}}】 " +
"URI資訊(ง•̀_•́)ง->:【{}】 請求映射控制類(ง•̀_•́)ง->:【{}】 " +
"請求方法(ง•̀_•́)ง->:【{}】 請求參數清單(ง•̀_•́)ง->:【{}】", remoteAddress, requestURI, clazzName, methodName,
sb.toString());
throw throwable;
}
//請求操作成功
String resultJosnString = "";
if (result != null) {
if (result instanceof ResponseData) {
resultJosnString = JSON.toJSONString(result, SerializerFeature.WriteDateUseDateFormat);
} else {
resultJosnString = String.valueOf(result);
}
}
//記錄請求完成執行時間:
long endTime = System.currentTimeMillis();
long usedTime = endTime - beginTime;
//記錄日志
logger.info("請求操作成功! 請求耗時:【{}】 " +
"IP資訊(◍'౪`◍)ノ゙->: 【{}}】 URI資訊(◍'౪`◍)ノ゙->:【{}】 " +
"請求映射控制類(◍'౪`◍)ノ゙->:【{}】 請求方法(◍'౪`◍)ノ゙->:【{}】 " +
"請求參數清單(◍'౪`◍)ノ゙->:【{}】 傳回值(◍'౪`◍)ノ゙->:【{}】", usedTime, remoteAddress, requestURI, clazzName,
methodName, sb.toString(), resultJosnString);
return result;
}
編寫了AOP以後,Controller的代碼是這樣的:
@RestController
@RequestMapping("gua")
public class GuaController {
private Logger logger = LoggerFactory.getLogger(GuaController.class);
@GetMapping("str")
public ResponseData str() {
return ResponseDataUtil.buildSuccess("Result String");
}
@GetMapping("data")
public ResponseData data() {
return ResponseDataUtil.buildSuccess(new User());
}
@GetMapping("map")
public ResponseData map() {
HashMap<String, Object> map = new HashMap<>(1);
map.put("Result", "Map");
return ResponseDataUtil.buildSuccess(map);
}
}
幹淨、整潔、易讀。
其請求日志是這樣的:
請求操作成功! 請求耗時:【137】 IP資訊(◍'౪`◍)ノ゙->: 【Client IP: 0:0:0:0:0:0:0:1, fromSource: request.getRemoteAddr}】 URI資訊(◍'౪`◍)ノ゙->:【/gua/str】 請求映射控制類(◍'౪`◍)ノ゙->:【GuaController】 請求方法(◍'౪`◍)ノ゙->:【str】 請求參數清單(◍'౪`◍)ノ゙->:【】 傳回值(◍'౪`◍)ノ゙->:【{"code":"0000","msg":"Result String"}】
感興趣的小夥伴,快去試試吧!