前言 1,在一些特定的場景我們往往需要看一下介面的入參,特別是跨系統的介面調用(下發,推送),這個時候的介面入參就很重要,我們保存入參入庫,如果出問題就可以馬上定位是上游還是下游的問題(方便扯皮) 2,還有一般需要在系統中看普通日誌,還有特殊的異常(報錯)日誌,一般我們可以通過伺服器去查看相應的位置 ...
前言
1,在一些特定的場景我們往往需要看一下介面的入參,特別是跨系統的介面調用(下發,推送),這個時候的介面入參就很重要,我們保存入參入庫,如果出問題就可以馬上定位是上游還是下游的問題(方便扯皮)
2,還有一般需要在系統中看普通日誌,還有特殊的異常(報錯)日誌,一般我們可以通過伺服器去查看相應的位置,但是由於伺服器是一直運行的,日誌是一直在生成的,這個時候就不太方便。
3,保存入參,我們之間本地調試的時候就可以不用造數據,這個也是很方便的,只需改改就好。
這個時候就體現入參請求和相應結果的重要性
Aop基本概念
Aspect(切麵): Aspect 聲明類似於 Java 中的類聲明,在 Aspect 中會包含著一些 Pointcut 以及相應的 Advice。
Joint point(連接點):表示在程式中明確定義的點,典型的包括方法調用,對類成員的訪問以及異常處理程式塊的執行等等,它自身還可以嵌套其它 joint point。
Pointcut(切點):表示一組 joint point,這些 joint point 或是通過邏輯關係組合起來,或是通過通配、正則表達式等方式集中起來,它定義了相應的 Advice 將要發生的地方。
Advice(增強):Advice 定義了在 Pointcut 裡面定義的程式點具體要做的操作,它通過 before、after 和 around 來區別是在每個 joint point 之前、之後還是代替執行的代碼。
Target(目標對象):織入 Advice 的目標對象.。
Weaving(織入):將 Aspect 和其他對象連接起來, 並創建 Adviced object 的過程
自定義註解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAccess {
String desc() default "無信息";
}
對自定義註解進行aop切麵
一般使用更加詳細的日誌切麵
@Component
@Aspect
public class UserAccessAspect {
// 這裡就是對上面進行切麵
@Pointcut(value = "@annotation(com.xncoding.aop.aspect.UserAccess)")
public void access() {
}
@Before("access()")
public void deBefore(JoinPoint joinPoint) throws Throwable {
System.out.println("second before");
}
@Around("@annotation(userAccess)")
public Object around(ProceedingJoinPoint pjp, UserAccess userAccess) {
//獲取註解里的值
System.out.println("second around:" + userAccess.desc());
try {
return pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
}
日誌切麵
如果想保存到資料庫或者進行更加詳細的日誌列印就可以使用下麵的日誌切麵,
這裡目前是列印,如果想保存到資料庫,可以創建相應的實體,set進去,然後進行insert到資料庫,如果使用mybatis-plus是很方便的,先創建表,然後用mybatis-plus 自動生成代碼,再用相應的mapper.inster() 保存到資料庫。
/**
* 日誌切麵
*/
@Aspect
@Component
public class LogAspect {
// 這個目前是對從 com.xncoding.aop.controller.* 下的都進行切入,如果想對上面的自定義註解進行切入,只需改成相對應的路徑
// 例如:@Pointcut(value = "@annotation(com.xncoding.aop.aspect.UserAccess)")
@Pointcut("execution(public * com.xncoding.aop.controller.*.*(..))")
public void webLog(){}
@Before("webLog()")
public void deBefore(JoinPoint joinPoint) throws Throwable {
// 接收到請求,記錄請求內容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 記錄下請求內容
System.out.println("URL : " + request.getRequestURL().toString());
System.out.println("HTTP_METHOD : " + request.getMethod());
System.out.println("IP : " + request.getRemoteAddr());
System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 處理完請求,返回內容
System.out.println("方法的返回值 : " + ret);
}
//後置異常通知
@AfterThrowing("webLog()")
public void throwss(JoinPoint jp){
System.out.println("方法異常時執行.....");
}
//後置最終通知,final增強,不管是拋出異常或者正常退出都會執行
@After("webLog()")
public void after(JoinPoint jp){
System.out.println("方法最後執行.....");
}
//環繞通知,環繞增強,相當於MethodInterceptor
@Around("webLog()")
public Object arround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("方法環繞start.....");
try {
Object o = pjp.proceed();
System.out.println("方法環繞proceed,結果是 :" + o);
return o;
} catch (Throwable e) {
throw e;
}
}
}
測試controller
@RestController
@RequestMapping("aop")
public class UserController {
@GetMapping("/first")
public Object first() {
return "first controller";
}
@GetMapping("/doError")
public Object error() {
return 1 / 0;
}
@GetMapping("/second")
@UserAccess(desc = "second")
public Object second() {
return "second controller 666";
}
@PostMapping("/user")
public void userPost(@RequestBody UserVo userVo){
System.out.println("我user進來了");
}
}
user類
@Data
public class UserVo {
@ApiModelProperty("姓名")
private String name;
@ApiModelProperty("性別")
private Integer sex;
}
創建表SQL語句
CREATE TABLE `sys_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`log_type` varchar(50) NOT NULL COMMENT '日誌類型',
`create_user_code` varchar(64) NOT NULL COMMENT '創建用戶編碼',
`create_user_name` varchar(100) NOT NULL COMMENT '創建用戶名稱',
`create_date` datetime NOT NULL COMMENT '創建時間',
`request_uri` varchar(500) DEFAULT NULL COMMENT '請求URI',
`request_method` varchar(10) DEFAULT NULL COMMENT '請求方式',
`request_params` text COMMENT '請求參數',
`response_params` text COMMENT '響應參數',
`request_ip` varchar(20) NOT NULL COMMENT '請求IP',
`server_address` varchar(50) NOT NULL COMMENT '請求伺服器地址',
`is_exception` char(1) DEFAULT NULL COMMENT '是否異常',
`exception_info` text COMMENT '異常信息',
`start_time` datetime NOT NULL COMMENT '開始時間',
`end_time` datetime NOT NULL COMMENT '結束時間',
`execute_time` int(11) DEFAULT NULL COMMENT '執行時間',
`user_agent` varchar(500) DEFAULT NULL COMMENT '用戶代理',
`device_name` varchar(100) DEFAULT NULL COMMENT '操作系統',
`browser_name` varchar(100) DEFAULT NULL COMMENT '瀏覽器名稱',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_sys_log_lt` (`log_type`) USING BTREE,
KEY `idx_sys_log_cub` (`create_user_code`) USING BTREE,
KEY `idx_sys_log_ie` (`is_exception`) USING BTREE,
KEY `idx_sys_log_cd` (`create_date`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1560559854462500867 DEFAULT CHARSET=utf8 COMMENT='系統日誌表';
測試
請求參數:
debug圖片