前言:
在我們后端開發(fā)過程中,打印日志,是最常見的排查問題的手段。但是每個(gè)接口里面去打印日志還是太麻煩,下面介紹下使用AOP打印接口日志的方法
初始化一個(gè)切面
首先我們對切面設(shè)計(jì)要求是,打印接口入?yún)?/strong>、出參、接口耗時(shí)等日志信息。出現(xiàn)異常時(shí)也順手打印一個(gè)異常信息
@Slf4j
@Aspect
@Component
public class RequestInterceptor {
/**
* 切入點(diǎn):包含所有的控制器
*/
@Pointcut("execution(public * com.spring.demo.javastudydemo.contrller.*.*(..))")
public void pointcut(){}
/**
* 切面實(shí)現(xiàn)
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try {
long s = System.currentTimeMillis();//開始時(shí)間
Object o = proceedingJoinPoint.proceed();//執(zhí)行目標(biāo)方法
if(System.currentTimeMillis()-s>2000L) {//結(jié)束時(shí)間-開始時(shí)間超過兩秒的接口
log.info("調(diào)用接口 {} 耗時(shí):{} 入?yún)? {} {} 出參 {}", proceedingJoinPoint.getSignature().toShortString(),(System.currentTimeMillis()-s), JSONObject.toJSONString(proceedingJoinPoint.getArgs()),System.lineSeparator(),JSONObject.toJSONString(o));
}
return o;
}catch (Throwable e) {
Object [] args =proceedingJoinPoint.getArgs();
log.error("調(diào)用接口異常 {} 入?yún)? {} {} 異常 {}", proceedingJoinPoint.getSignature().toShortString(),JSONObject.toJSONString(args),System.lineSeparator(), ExceptionUtil.exMassage(e));
throw e;
}
}
}
這里選擇使用了@Around注解的原因是,我們需要打印出參還有執(zhí)行時(shí)間。使用環(huán)繞可以自定義目標(biāo)方法的執(zhí)行時(shí)機(jī),
之所以選擇打印執(zhí)行時(shí)間超過2秒的日志,主要是防止請求量較大的項(xiàng)目導(dǎo)致日志過多。如果日志數(shù)量沒有壓力的項(xiàng)目,可以放開限制
創(chuàng)建被代理類
切面設(shè)計(jì)好后,我們回到被代理的控制器,由于我們切入方式使用的execution匹配命名空間。所以我們的控制器不需要任何跟切面相關(guān)的代碼。這也是我們做這個(gè)切面的目的
@RestController
@RequestMapping("hello")
public class HelloController {
@GetMapping("index")
public String index(@RequestParam("name") String name) {
try {
Thread.sleep(2000L);//人為制造慢接口
} catch (InterruptedException e) {
}
return "hello "+name;
}
}
檢驗(yàn)執(zhí)行結(jié)果
我們啟動(dòng)服務(wù)后,訪問這個(gè)路由

訪問接口
訪問成功,在兩秒后返回了結(jié)果
我們再看看控制臺打印日志的結(jié)果

打印結(jié)果
符合我們設(shè)計(jì)預(yù)期,打印了方法名,入?yún)?,出參和?zhí)行時(shí)間。