Spring AOP日誌記錄介面請求引數,執行時間
阿新 • • 發佈:2019-02-09
本文用spring aop方式對請求攔截,獲取請求引數以及計算介面執行時間。注意:所需的環境以及依賴有:spring各包, jdk1.8,org.slf4j.Logger (請執行匯入)
前言
在前後端分離的專案中,常因為不知道是前端還是後端的問題,而苦苦尋找bug的根源。如果能在日誌中看到前端傳過來的引數,就能直觀的知道是前端引數的問題還是後臺程式的問題,以定位到問題的根源。同時計算執行時間可瞭解到介面是否符合要求
程式碼
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import java.lang.reflect.Field;
import java.util.Objects;
import java.util.stream.Stream;
/**
* aop採集日誌(介面請求引數,介面呼叫時間)
* @author Huangqing
* @date 2018/7/16 11:50
*/
@Aspect
@Component
public class ControllerInterceptor {
private static String[] types = {"java.lang.Integer", "java.lang.Double",
"java.lang.Float" , "java.lang.Long", "java.lang.Short",
"java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
"java.lang.String", "int", "double", "long", "short", "byte",
"boolean", "char", "float"};
private static final Logger log = LoggerFactory.getLogger(ControllerInterceptor.class);
private static ThreadLocal<Long> startTime = new ThreadLocal<Long>();
/**
* 定義攔截規則:攔截com.dxyl.controller..*(..))包下面的所有類中,有@PostMapping註解的方法
*/
@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public void controllerMethodPointcut() {
}
@Before("controllerMethodPointcut()")
public void controller(JoinPoint point) {
startTime.set(System.currentTimeMillis());
MethodSignature signature = (MethodSignature) point.getSignature();
Long count = Stream.of(signature.getMethod().getDeclaredAnnotations())
.filter(annotation -> annotation.annotationType() == PostMapping.class)
.count();
String requestPath = count >= 1 ? signature.getMethod().getAnnotation(PostMapping.class).value()[0] : "";
String info = String.format("\n =======> 請求路徑: %s %s", requestPath, getMethodInfo(point));
log.info(info);
}
private String getMethodInfo(JoinPoint point) {
String className = point.getSignature().getDeclaringType().getSimpleName();
String methodName = point.getSignature().getName();
String[] parameterNames = ((MethodSignature) point.getSignature()).getParameterNames();
StringBuilder sb = null;
if (Objects.nonNull(parameterNames)) {
sb = new StringBuilder();
for (int i = 0; i < parameterNames.length; i++) {
// 對引數解析(引數有可能為基礎資料型別,也可能為一個物件,若為物件則需要解析物件中變數名以及值)
String value = "";
if (point.getArgs()[i] == null) {
value = "null";
} else {
// 獲取物件型別
String typeName = point.getArgs()[i].getClass().getTypeName();
boolean flag = false;
for (String t : types) {
//1 判斷是否是基礎型別
if (t.equals(typeName)) {
value = point.getArgs()[i].toString();
flag = true;
}
if (flag) {
break;
}
}
if (!flag) {
//2 通過反射獲取實體類屬性
value = getFieldsValue(point.getArgs()[i]);
}
}
sb.append(parameterNames[i] + ":" + value + "; ");
}
}
sb = sb == null ? new StringBuilder() : sb;
String info = String.format("\n =======> 請求類名: %s \n =======> 請求方法: %s \n =======> 請求引數: %s", className, methodName, sb.toString());
return info;
}
/**
* 解析實體類,獲取實體類中的屬性
*/
public static String getFieldsValue(Object obj) {
//通過反射獲取所有的欄位,getFileds()獲取public的修飾的欄位
//getDeclaredFields獲取private protected public修飾的欄位
Field[] fields = obj.getClass().getDeclaredFields();
String typeName = obj.getClass().getTypeName();
for (String t : types) {
if (t.equals(typeName)) {
return "";
}
}
StringBuilder sb = new StringBuilder();
sb.append("{");
for (Field f : fields) {
//在反射時能訪問私有變數
f.setAccessible(true);
try {
for (String str : types) {
//這邊會有問題,如果實體類裡面繼續包含實體類,這邊就沒法獲取。
//其實,我們可以通遞迴的方式去處理實體類包含實體類的問題。
if (f.getType().getName().equals(str)) {
sb.append(f.getName() + " : " + f.get(obj) + ", ");
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
sb.append("}");
return sb.toString();
}
/**
* 計算介面執行時間
*/
@AfterReturning(pointcut = "controllerMethodPointcut()")
public void doAfterReturing() {
long costTime = System.currentTimeMillis() - startTime.get();
log.info("\n =======> 耗費時間: " + costTime + "ms");
}
}
測試結果
===2018-07-19 17:42:40.748 [http-nio-8085-exec-3] INFO com.dxyl.aop.ControllerInterceptor Line:54 -
=======> 請求路徑: /list
=======> 請求類名: TagController
=======> 請求方法: getList
=======> 請求引數: pageNum:1; pageSize:10;
===2018-07-19 17:42:41.522 [http-nio-8085-exec-3] INFO com.dxyl.aop.ControllerInterceptor Line:136 -
=======> 耗費時間: 779ms
引數中有實體物件的也進行了解析,但仍存在一些問題,在物件中存在物件則沒有解析出來,博主想到的有遞迴,等有時間去寫好了再更新給大家。
PS:博主這樣的做法被同事否定了,原因是這樣浪費了資源,降低了效能。等待博主尋找到更好的記錄引數的做法再與大家分享,感謝觀看,如果有幫助不妨點個贊
時間沒有放過任何人,你欠歲月的將以另一種形式償還。