前言
通常情况下,我们将项目部署到服务器后,如果接口出现了bug,那么我们就只有干着急(时间一长,我们就不知道接口的请求参数,也不知道接口返给用户的信息)。这时,如果我们通过AOP将接口的请求参数和返回对象保存到日志文件中,那么我们就可以继续调试。
代码
这里我们使用了以下的第三方包:
- fastjson: https://mvnrepository.com/artifact/com.alibaba/fastjson
- AOP: https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop
- lombok: https://mvnrepository.com/artifact/org.projectlombok/lombok
- slf4j:https://mvnrepository.com/artifact/org.slf4j/slf4j-api
- Log4j2:https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2
package com.cfl.jd.aop;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 类描述:
* 日志切面:记录请求的参数,返回的结果
* @ClassName LogAOP
* @Author msi
* @Date 2020/11/4 16:13
* @Version 1.0
*/
@Slf4j
@Aspect
@Component
public class LogAOP {
/**
* 手动创建的对象所在包
*/
public static final String PACKAGE_NAME = "com.cfl.jd";
/**
* 全局日志输出
* @param pjp
* @return
* @throws Throwable
*/
@Around(value = "execution(* com.cfl.jd.controller.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 参数值
Object[] args = pjp.getArgs();
Signature signature = pjp.getSignature();
// 完全方法名(完全类名+方法名)
String entirelyMethodName = signature.getDeclaringTypeName() + "." + signature.getName();
// 参数名
MethodSignature methodSignature = (MethodSignature) signature;
String[] parameterNames = methodSignature.getParameterNames();
Class[] parameterTypes = methodSignature.getParameterTypes();
// 方法返回值
// String returnTypeName = methodSignature.getReturnType().getName();
StringBuilder sb = new StringBuilder();
for (int i = 0, length = args.length; i < length; i++) {
sb.append("参数:").append(parameterNames[i]).append("(").append(parameterTypes[i].getName()).append(")")
.append(" ————> ");
if (Objects.nonNull(args[i])) {
if (parameterTypes[i].getName().indexOf(LogAOP.PACKAGE_NAME) != -1) {
sb.append(JSON.toJSONString(args[i]));
} else{
sb.append(args[i].toString());
}
} else {
sb.append("null");
}
if (i < length - 1) {
sb.append("\n");
}
}
if (args.length > 0) {
log.info("方法:{} 开始执行,请求参数如下:\n{}",entirelyMethodName, sb.toString());
}
// 继续执行方法
Object obj = pjp.proceed();
if (Objects.nonNull(obj)) {
String name = obj.getClass().getName();
if (name.indexOf(LogAOP.PACKAGE_NAME) != -1) {
log.info("方法:{} 结束执行,返回值:\n{}", entirelyMethodName, JSON.toJSONString(obj));
} else {
log.info("方法:{} 结束执行,返回值:\n{}", entirelyMethodName, obj.toString());
}
}
return obj;
}
}
输出测试
[11-05 21:24:46] [INFO] - com.cfl.jd.aop.LogAOP.around(LogAOP.java:70) - 方法:com.cfl.jd.controller.UserController.login 开始执行,请求参数如下:
参数:loginUsername(java.lang.String) ————> 603088455@qq.com
参数:loginPassword(java.lang.String) ————> 123456
参数:rememberPassword(boolean) ————> false
[11-05 21:24:46] [INFO] - com.cfl.jd.aop.LogAOP.around(LogAOP.java:79) - 方法:com.cfl.jd.controller.UserController.login 结束执行,返回值:
{"code":"1","data":{"admin":false,"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiMTIiLCI2MDMwODg0NTVAcXEuY29tIiwiNjU4NDRiOTE2MjA5NTdjZDJiYWVjNzViMzQyMGIxYzRlMzg2YjUzZDhjZjI5ZWUzZGI0Y2YxYTE0NDQ3ODI0M2E3OTMxMGEyNDY2OWIwMTMwODQ2ZjFkYzA4ZWY0N2ExMjJmYzQ4ZDMzMzAwNDkwOWQyY2QwYTUwMTgwYTY2NDciLCJmYWxzZSJdLCJpc3MiOiJtc2kiLCJleHAiOjE2MDU4Nzg2ODYsImlhdCI6MTYwNDU4MjY4Nn0.p5fMVH1LGt5T84IfBlNom_-qj-Ha01oNwEa7BZUomMo"},"message":"成功"}
辅助工具
因为本人使用的idea开发工具,所以这里介绍两款比较好用的插件:
- Grep Console ,输出控制台自定义输出样式插件
- All Format, 格式化字符串
写在最后
注意:因为JSON.toJSONString(obj) 这个方法在遇到 obj是一些比较大的对象(类似于HttpServletRequest, MultipartFile…),就会报错。所以,我们在记录日志时,如果参数是我们自己可控的代码,那么就使用json字符串,但是如果是框架不可控的参数对象,就是用toString方法。
log4j 的配置详情,请看我之前的博客: Java 配置日志系统
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/78279.html