使用若依Springboot项目,Controller均继承BaseController。
这里若依原日志切面不会打印接口请求参数,返回参数等信息,这里我做了修改。
修改后日志打印出现一个接口调用4次,5次的情况
15:08:58.342 [http-nio-8082-exec-4] INFO c.s.f.a.LogAspect - [doBefore,103] -
=== 开始请求 ===
request-url:http://localhost:8082/xxx/xxx/xxx
request-desc:
request-method:POST
request-ip:
class-method:com.syt.api.controller.BaseController.initBinder
request-param:{"sign":["00001111111dddddddddddd"]}
=== 返回值 ===
null
==== time cost ====
=== 开始请求 ===
request-url:http://localhost:8082/xxx/xxx/xxx
request-desc:
request-method:POST
request-ip:
class-method:com.syt.api.controller.BaseController.initBinder
request-param:{"sign":["00001111111dddddddddddd"]}
=== 返回值 ===
null
==== time cost ====
........
这里出现问题就是BaseController.initBinder
方法重复调用了好几次,且返回值均为null。
BaseController.initBinder
这个方法使用注解 @InitBinder
源代码为:
/**
* 将前台传递过来的日期格式的字符串,自动转化为Date类型
*/
@InitBinder
public void initBinder(WebDataBinder binder)
{
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
{
@Override
public void setAsText(String text)
{
setValue(DateUtils.parseDate(text));
}
});
}
@InitBinder从字面意思可以看出这个的作用是给Binder做初始化的,@InitBinder主要用在@Controller中标注于方法上(@RestController也算),表示初始化当前控制器的数据绑定器(或者属性绑定器),只对当前的Controller有效。@InitBinder标注的方法必须有一个参数WebDataBinder。所谓的属性编辑器可以理解就是帮助我们完成参数绑定,然后是在请求到达controller要执行方法前执行!
链接: 原文地址
为避免在日志重复显示接口调用,需要处理日志切面LogAspect.java
,在@Before注解下的方法添加下面代码
@Before("pointcut()")
public void doBefore(JoinPoint joinPoint) throws Exception {
*****
Signature signature1 = joinPoint.getSignature();
MethodSignature signature = (MethodSignature) signature1;
//如果获取的方法名为initBinder,则直接return
if (signature.getMethod().getName().contains("initBinder")) {
return;
}
******
}
@AfterReturning
注解的方法需要修改
@AfterReturning(returning = "ret", pointcut = "pointcut()")
public void doAfterReturning(Object ret) {
if (!isDoReturning) {
return;
}
//这里需要添加是否为空判断,不然会报错
if (StringUtils.isNotNull(ret)) {
log.info("\n=== 返回值 ===\n" + JSON.toJSONString(ret));
log.info("\n==== time cost" + ((System.currentTimeMillis() - threadLocal.get())) + "ms" + " ====\n");
}
return;
}
finish:修改后文件如下所示LogAspect.java
:
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import com.syt.common.utils.StringUtils;
/**
* 操作日志记录处理
*
* @author syt
*/
@Aspect
@Component
public class LogAspect
{
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
@Autowired
private Environment env;
private static boolean isDoBefore = false;
private static boolean isDoAfter = false;
private static boolean isDoReturning = false;
private static boolean isDoThrowing = false;
/**
* 保证每个线程都有一个单独的实例
*/
private ThreadLocal<Long> threadLocal = new ThreadLocal<>();
private static String[] params;
static {
params = new String[]{"aspect.logger.spring-application-name", "aspect.logger.request-url", "aspect.logger.request-uri",
"aspect.logger.request-desc", "aspect.logger.session", "aspect.logger.cookie",
"aspect.logger.content-type", "aspect.logger.request-method", "aspect.logger.request-ip",
"aspect.logger.request-user-agent", "aspect.logger.class-method", "aspect.logger.request-param"};
}
@PostConstruct
public void init() {
isDoBefore = env.getProperty("aspect.do-before") == null ? false : env.getProperty("aspect.do-before", Boolean.class);
isDoAfter = env.getProperty("aspect.do-after") == null ? false : env.getProperty("aspect.do-after", Boolean.class);
isDoReturning = env.getProperty("aspect.do-returning") == null ? false : env.getProperty("aspect.do-returning", Boolean.class);
isDoThrowing = env.getProperty("aspect.do-throwing") == null ? false : env.getProperty("aspect.do-throwing", Boolean.class);
}
@Pointcut("execution(* com.syt.*.controller..*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void doBefore(JoinPoint joinPoint) throws Exception {
if (!isDoBefore) {
return;
}
Signature signature1 = joinPoint.getSignature();
MethodSignature signature = (MethodSignature) signature1;
if (signature.getMethod().getName().contains("initBinder")) {
return;
}
threadLocal.set(System.currentTimeMillis());
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
StringBuilder stringBuilder = new StringBuilder()
.append("\n=== 开始请求 ===\n");
// 记录请求的内容
this.logHandle(joinPoint, params, request, stringBuilder);
log.info(stringBuilder.toString());
return;
}
@After("pointcut()")
public void doAfter(JoinPoint joinPoint) {
if (!isDoAfter) {
return;
}
log.info("\n==== doAfter ===\n" + joinPoint.toString());
}
/**
* 返回值信息
*
* @param ret
*/
@AfterReturning(returning = "ret", pointcut = "pointcut()")
public void doAfterReturning(Object ret) {
if (!isDoReturning) {
return;
}
if (StringUtils.isNotNull(ret)) {
log.info("\n=== 返回值 ===\n" + JSON.toJSONString(ret));
log.info("\n==== time cost" + ((System.currentTimeMillis() - threadLocal.get())) + "ms" + " ====\n");
}
return;
}
@AfterThrowing(throwing = "ex",pointcut = "pointcut()")
public void doThrowing(Throwable ex){
if (!isDoThrowing) {
return;
}
log.error("\n=== 异常 ===\n" + ex);
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param joinPoint 切点
* @return 方法描述
* @throws Exception
*/
private String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
Object temp = method.getAnnotation(ApiOperation.class);
if (temp != null) {
description = method.getAnnotation(ApiOperation.class).value();
}
break;
}
}
}
return description;
}
/**
* 处理请求参数输出
*
* @param joinPoint
* @param requestParams
* @param request
* @param stringBuilder
* @throws Exception
*/
public void logHandle(JoinPoint joinPoint, String[] requestParams, HttpServletRequest request, StringBuilder stringBuilder) throws Exception {
Map<String, Object> paramMap = new HashMap<>(16);
String contentType = request.getContentType();
paramMap.put("session", request.getSession());
paramMap.put("cookie", request.getCookies());
paramMap.put("spring-application-name", env.getProperty("spring.application.name"));
paramMap.put("request-url", request.getRequestURL());
paramMap.put("request-uri", request.getRequestURI());
paramMap.put("request-param", JSON.toJSONString(request.getParameterMap()));
paramMap.put("request-desc", getControllerMethodDescription(joinPoint));
paramMap.put("request-method", request.getMethod());
paramMap.put("content-type", contentType);
paramMap.put("class-method", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
paramMap.put("request-ip", request.getRemoteAddr());
paramMap.put("request-user-agent", request.getHeader("User-Agent"));
String reqParam = null;
Object[] o = joinPoint.getArgs();
if (contentType != null && contentType.contains("multipart/form-data")) {
MultipartFile file = (MultipartFile) o[0];
reqParam = file.getOriginalFilename();
} else {
if (o != null && o.length > 0) {
reqParam = o[0].toString();
}
}
paramMap.put("aspect.logger.request-param", reqParam);
// 按配置输出
for (String param : requestParams) {
Boolean property = env.getProperty(param, Boolean.class);
String p = param.replace("aspect.logger.", "");
if (property != null && property && paramMap.containsKey(p)) {
stringBuilder.append(p + ":" + paramMap.get(p) + "\n");
}
}
}
}
配置文件applicaton-test.yml
添加下面配置,这里aspect
顶格
# 日志切面处理
aspect:
logger:
spring-application-name: false
request-url: true
request-uri: false
class-method: true
request-method: true
request-param: true
request-desc: true
request-ip: true
request-user-agent: false
content-type: false
session: false
cookie: false
do-before: true
do-after: false
do-returning: true
do-throwing: true
if (contentType != null && contentType.contains("multipart/form-data")) {
MultipartFile file = (MultipartFile) o[0];
reqParam = file.getOriginalFilename();
} else {
if (o != null && o.length > 0) {
if (o[0] != null) { //在这里添加空指针判断
reqParam = o[0].toString(); //这里报空指针异常
}
}
}
if (contentType != null && contentType.contains("multipart/form-data")) {
StandardMultipartHttpServletRequest request1 = new StandardMultipartHttpServletRequest(request);
Map<String, MultipartFile> fileMap = request1.getFileMap();
MultipartFile file = fileMap.get("file");
// MultipartFile file = (MultipartFile) o[0];
reqParam = file.getOriginalFilename();
} else {
if (o != null && o.length > 0) {
if (o[0] != null) {
reqParam = o[0].toString();
}
}
}