使用若依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类型*/@InitBinderpublic void initBinder(WebDataBinder binder){// Date 类型转换binder.registerCustomEditor(Date.class, new PropertyEditorSupport(){@Overridepublic 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,则直接returnif (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);@Autowiredprivate 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"};}@PostConstructpublic 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: falserequest-url: truerequest-uri: falseclass-method: truerequest-method: truerequest-param: truerequest-desc: truerequest-ip: truerequest-user-agent: falsecontent-type: falsesession: falsecookie: falsedo-before: truedo-after: falsedo-returning: truedo-throwing: true