前言:
本次主要是提供了两种打印日志的方式,可供大家挑选
1.基于注解:
优点:
灵活
缺点:
工作量大,需要在每一个需要打印的地方加上注解,并且配置比较繁琐
代码:
/**
* 切点配置类
*/
@Slf4j
@Aspect
@Component
@EnableAspectJAutoProxy
public class AopConfig {
/**
* 方法用途(切入点表达式可以用&&,||,!来组合使用):
* execution: 匹配连接点:execution(* com.example.demo.*(..))--com.example.demo包下所有类的方法
* within: 某个类里面
* this: 指定AOP代理类的类型
* target:指定目标对象的类型
* args: 指定参数的类型
* bean:指定特定的bean名称,可以使用通配符(Spring自带的)
* @target: 带有指定注解的类型
* @args: 指定运行时传的参数带有指定的注解
* @within: 匹配使用指定注解的类
* @annotation:指定方法所应用的注解
* <br/>
* 操作步骤: TODO<br/>
* ${tags}
*/
@Pointcut("@annotation(com.deve.config.annotation.WinControllerPointCut)")
public void winPointCut() {
}
/**
* 前置通知
* 方法用途:
* 在winPointCut注解之前执行
* 标识一个前置增强方法,相当于BeforeAdvice的功能.
* <br/>
* 操作步骤: TODO<br/>
* ${tags}
*/
@Async//异步注解
@Before("winPointCut()")
public void beforeCall(JoinPoint joinPoint) {
log.info("日志记录开始-------------");
//前置通知-业务,入参需要切点方法定义好
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
log.info("before-接口路径:{}", request.getRequestURL().toString());
log.info("before-请求方式:{}", request.getMethod());
log.info("before-类方法:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
log.info("before-请求参数:" + Arrays.toString(joinPoint.getArgs()));
}
/**
* 返回通知
* 方法用途:
* 在winPointCut注解之后执行
* final增强,不管是抛出异常或者正常退出都会执行。
* @AfterReturning: 后置增强,似于AfterReturningAdvice, 方法正常退出时执行.
* @AfterThrowing: 异常抛出增强,相当于ThrowsAdvice.
* <br/>
* 操作步骤: TODO<br/>
* ${tags}
*/
@Async//异步注解
@AfterReturning(pointcut = "winPointCut()", returning = "resp")
public void returnCall(Object resp) {
//后置通知-业务,入参需要切点方法定义好
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
log.info("接口路径:{}", request.getRequestURL().toString());
log.info("Result:{}", JSON.toJSONString(resp));
}
/**
* 异常通知
* @param jp
* @param ex
*/
@Async//异步注解
@AfterThrowing(throwing = "ex", pointcut = "winPointCut()")
public void throwsCall(JoinPoint jp,Exception ex){
log.error("产生异常的方法:{}"+jp);
log.error("异常种类:{}"+ex);
}
/**
* 后置通知
* @param jp
*/
@Async//异步注解
@After("winPointCut()")
public void afterCall(JoinPoint jp){
log.info("日志记录结束-------------");
}
/**
* 环绕通知
* 方法用途:
* @Around 环绕增强,相当于MethodInterceptor,对带@winPointCut注解的方法进行切面,并获取到注解的属性值
* ProceedingJoinPoint: 环绕通知
* <br/>
* 操作步骤: TODO<br/>
* ${tags}
*/
@Async //异步注解
//@Around("winPointCut() && @annotation(winControllerPointCut)")
public Object aroundCall(ProceedingJoinPoint joinPoint, WinControllerPointCut winControllerPointCut) throws Throwable {
Object obj = null;
try {
// winPointCut注解的属性值
winControllerPointCut.remark();
Object[] args = joinPoint.getArgs();
String arg = JSON.toJSONString(args);
log.info("请求参数:{}"+ arg);
Signature signature = joinPoint.getSignature();
// 方法package路径
String methodUrl = signature.getDeclaringTypeName();
// 方法名,不包含package路径
String method = signature.getName();
log.info("正在执行:{}" + method + "方法,路径:{}" + methodUrl);
// obj是返回的结果,joinPoint.proceed用于启动目标方法执行,非常重要
obj = joinPoint.proceed(args);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return obj;
}
}
/**
* 接口切点自定义注解,用于捕捉个别方法运行时详细情况
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface WinControllerPointCut {
/**
* 操作名称
* @return
*/
String remark() default "";
/**
* 操作类型
* @return
*/
String type() default "QUERY";
}
2.基于工具类
优点:
配置简单,操作方便
缺点:
会打印每一个方法,消耗比较大
代码:
/**
* 切点日志打印工具类
* 打印所有请求的 url 、方法 、 参数
*/
@Aspect
@Component
@Slf4j
public class LogAspectUtils {
/** 以 controller 包 的所有请求 为 切入点 */
@Pointcut("execution(* com.deve.controller..*.*(..))")
public void pointCat() {
}
/** 切点之前 */
@Before("pointCat()")
public void doBefore(JoinPoint joinPoint) {
//获取request对象
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
log.info("========================================== Start");
log.info("请求地址 :{}", request.getRequestURL().toString());
log.info("请求路径 :{}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
log.info("请求参数 :{}", JSON.toJSONString(joinPoint.getArgs()));
}
/**
* 切点之后
*/
@After("pointCat()")
public void doAfter() {
log.info("");
}
/**
* 环绕
*/
@Around("pointCat()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
log.info("开始时间 :{}", startTime);
Object result = proceedingJoinPoint.proceed();
log.info("返回参数 :{}", JSON.toJSONString(result).length()>=1000?"参数太大不打印了":JSON.toJSONString(result));
long endTime = System.currentTimeMillis();
log.info("结束时间 :{}", endTime);
log.info("========================================== 执行耗时 :{} ms", endTime - startTime);
return result;
}
}