|
@@ -1,31 +1,29 @@
|
|
|
package cn.com.qmth.examcloud.web.support;
|
|
|
|
|
|
-import java.util.Collection;
|
|
|
-import java.util.Map;
|
|
|
-
|
|
|
-import javax.servlet.http.HttpServletRequest;
|
|
|
-import javax.servlet.http.HttpServletResponse;
|
|
|
-
|
|
|
-import org.aspectj.lang.ProceedingJoinPoint;
|
|
|
-import org.aspectj.lang.annotation.Around;
|
|
|
-import org.aspectj.lang.annotation.Aspect;
|
|
|
-import org.aspectj.lang.annotation.Pointcut;
|
|
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-import org.springframework.http.HttpStatus;
|
|
|
-import org.springframework.http.ResponseEntity;
|
|
|
-import org.springframework.stereotype.Component;
|
|
|
-
|
|
|
import cn.com.qmth.examcloud.api.commons.exchange.BaseResponse;
|
|
|
import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
|
|
|
import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
|
|
|
import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
|
|
|
-import cn.com.qmth.examcloud.commons.util.JsonUtil;
|
|
|
+import cn.com.qmth.examcloud.commons.util.JsonMapper;
|
|
|
import cn.com.qmth.examcloud.commons.util.ObjectUtil;
|
|
|
-import cn.com.qmth.examcloud.commons.util.StringUtil;
|
|
|
import cn.com.qmth.examcloud.commons.util.ThreadLocalUtil;
|
|
|
import cn.com.qmth.examcloud.web.config.LogProperties;
|
|
|
import cn.com.qmth.examcloud.web.enums.HttpServletRequestAttribute;
|
|
|
import cn.com.qmth.examcloud.web.redis.RedisClient;
|
|
|
+import org.apache.commons.lang3.ArrayUtils;
|
|
|
+import org.aspectj.lang.ProceedingJoinPoint;
|
|
|
+import org.aspectj.lang.annotation.Around;
|
|
|
+import org.aspectj.lang.annotation.Aspect;
|
|
|
+import org.aspectj.lang.annotation.Pointcut;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.util.Collection;
|
|
|
+import java.util.Map;
|
|
|
|
|
|
/**
|
|
|
* spring mvc controller aspect.
|
|
@@ -38,198 +36,227 @@ import cn.com.qmth.examcloud.web.redis.RedisClient;
|
|
|
@Aspect
|
|
|
public class ControllerAspect {
|
|
|
|
|
|
- private static final ExamCloudLog DEBUG_LOG = ExamCloudLogFactory
|
|
|
- .getLog(ControllerAspect.class);
|
|
|
-
|
|
|
- private static final ExamCloudLog INTERFACE_LOG = ExamCloudLogFactory
|
|
|
- .getLog("INTERFACE_LOGGER");
|
|
|
-
|
|
|
- @Autowired(required = false)
|
|
|
- HttpMethodProcessor httpMethodProcessor;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- LogProperties logProperties;
|
|
|
-
|
|
|
- @Autowired(required = false)
|
|
|
- RedisClient redisClient;
|
|
|
-
|
|
|
- /**
|
|
|
- * 构造函数
|
|
|
- *
|
|
|
- */
|
|
|
- public ControllerAspect() {
|
|
|
- super();
|
|
|
- DEBUG_LOG.info("Aspect class [ControllerAspect] is active!");
|
|
|
- }
|
|
|
-
|
|
|
- private static String[] excludeFields = new String[]{"password", ".*Password"};
|
|
|
-
|
|
|
- /**
|
|
|
- * handlerMethods
|
|
|
- *
|
|
|
- * @author WANGWEI
|
|
|
- */
|
|
|
- @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) "
|
|
|
- + "|| @annotation(org.springframework.web.bind.annotation.GetMapping) "
|
|
|
- + "|| @annotation(org.springframework.web.bind.annotation.PostMapping)"
|
|
|
- + "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)"
|
|
|
- + "|| @annotation(org.springframework.web.bind.annotation.PutMapping)")
|
|
|
- public void handlerMethods() {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 接口处理
|
|
|
- *
|
|
|
- * @author WANG WEI
|
|
|
- *
|
|
|
- * @param joinPoint
|
|
|
- * @return
|
|
|
- */
|
|
|
- @Around("handlerMethods()")
|
|
|
- public Object doAroundWebRequests(ProceedingJoinPoint joinPoint) {
|
|
|
- long startTime = System.currentTimeMillis();
|
|
|
-
|
|
|
- // 获取request对象
|
|
|
- HttpServletRequest request = ServletUtil.getRequest();
|
|
|
-
|
|
|
- String path = request.getServletPath();
|
|
|
- String method = request.getMethod();
|
|
|
-
|
|
|
- if (INTERFACE_LOG.isInfoEnabled()) {
|
|
|
- INTERFACE_LOG
|
|
|
- .info(StringUtil.join("[HTTP-IN]. path=\"", path, "\", method=[", method, "]"));
|
|
|
- }
|
|
|
-
|
|
|
- Object[] args = joinPoint.getArgs();
|
|
|
-
|
|
|
- if (INTERFACE_LOG.isInfoEnabled()) {
|
|
|
- if (null != args) {
|
|
|
- if (1 == args.length && args[0] instanceof JsonSerializable) {
|
|
|
- INTERFACE_LOG
|
|
|
- .info("[HTTP-REQ]. request=" + JsonUtil.toJson(args[0], excludeFields));
|
|
|
- } else if (1 <= args.length) {
|
|
|
- StringBuilder sb = new StringBuilder();
|
|
|
- sb.append("[HTTP-REQ]. args: ");
|
|
|
- for (int i = 0; i < args.length; i++) {
|
|
|
- Object curArg = args[i];
|
|
|
- sb.append("args[").append(i).append("]=");
|
|
|
- if (null == curArg) {
|
|
|
- sb.append("null");
|
|
|
- } else if (curArg instanceof JsonSerializable) {
|
|
|
- sb.append(JsonUtil.toJson(curArg));
|
|
|
- } else if (ObjectUtil.isBaseDataType(curArg.getClass())) {
|
|
|
- sb.append(curArg);
|
|
|
- }
|
|
|
- sb.append("; ");
|
|
|
- }
|
|
|
-
|
|
|
- INTERFACE_LOG.info(sb.toString());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Object ret = null;
|
|
|
- try {
|
|
|
- if (null != httpMethodProcessor) {
|
|
|
- httpMethodProcessor.beforeMethod(request, args);
|
|
|
- }
|
|
|
- // 执行
|
|
|
- ret = joinPoint.proceed();
|
|
|
-
|
|
|
- request.setAttribute(HttpServletRequestAttribute.$_EXCEPTION_HAPPENED.name(),
|
|
|
- new Boolean(false));
|
|
|
-
|
|
|
- } catch (Throwable e) {
|
|
|
-
|
|
|
- request.setAttribute(HttpServletRequestAttribute.$_EXCEPTION_HAPPENED.name(),
|
|
|
- new Boolean(true));
|
|
|
-
|
|
|
- if (null != httpMethodProcessor) {
|
|
|
- try {
|
|
|
- httpMethodProcessor.onException(request, args, e);
|
|
|
- } catch (Exception ex) {
|
|
|
- INTERFACE_LOG.error("[AFTER-METHOD].onException()", ex);
|
|
|
- }
|
|
|
- }
|
|
|
- INTERFACE_LOG.error(StringUtil.join("[HTTP-FAIL]. path=\"", path, "\", method=[",
|
|
|
- method, "] ; cost ", System.currentTimeMillis() - startTime, " ms."));
|
|
|
- throw new RuntimeException(e);
|
|
|
- }
|
|
|
-
|
|
|
- if (null != httpMethodProcessor) {
|
|
|
- try {
|
|
|
- httpMethodProcessor.onSuccess(request, args, ret);
|
|
|
- } catch (Exception ex) {
|
|
|
- INTERFACE_LOG.error("[AFTER-METHOD].onSuccess()", ex);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (null != ret && BaseResponse.class.isAssignableFrom(ret.getClass())) {
|
|
|
- BaseResponse baseResponse = (BaseResponse) ret;
|
|
|
- baseResponse.setCost(System.currentTimeMillis() - startTime);
|
|
|
- }
|
|
|
-
|
|
|
- if (INTERFACE_LOG.isDebugEnabled() && logProperties.isNormalResponseLogEnable()) {
|
|
|
- boolean jsonSerializable = false;
|
|
|
- if (null == ret) {
|
|
|
- INTERFACE_LOG.debug("[HTTP-RESP]. status=" + HttpStatus.OK);
|
|
|
- INTERFACE_LOG.debug("[HTTP-RESP]. response=void");
|
|
|
- } else if (ret instanceof ResponseEntity) {
|
|
|
- ResponseEntity<?> re = (ResponseEntity<?>) ret;
|
|
|
- Object body = re.getBody();
|
|
|
- if (null != body) {
|
|
|
- if (body instanceof JsonSerializable) {
|
|
|
- jsonSerializable = true;
|
|
|
- } else if (body instanceof Collection) {
|
|
|
- jsonSerializable = true;
|
|
|
- } else if (body instanceof Map) {
|
|
|
- jsonSerializable = true;
|
|
|
- } else if (body instanceof String) {
|
|
|
- jsonSerializable = true;
|
|
|
- }
|
|
|
- INTERFACE_LOG.debug("[HTTP-RESP]. status=" + re.getStatusCodeValue());
|
|
|
- if (jsonSerializable) {
|
|
|
- String json = JsonUtil.toJson(body);
|
|
|
- int responseJsonMaxSize = logProperties.getResponseLogJsonMaxSize();
|
|
|
- if (json.length() > responseJsonMaxSize) {
|
|
|
- INTERFACE_LOG.debug("[HTTP-RESP]. response= too large");
|
|
|
- } else {
|
|
|
- INTERFACE_LOG.debug("[HTTP-RESP]. response=" + json);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (ret instanceof JsonSerializable) {
|
|
|
- jsonSerializable = true;
|
|
|
- } else if (ret instanceof Collection) {
|
|
|
- jsonSerializable = true;
|
|
|
- } else if (ret instanceof Map) {
|
|
|
- jsonSerializable = true;
|
|
|
- } else if (ret instanceof String) {
|
|
|
- jsonSerializable = true;
|
|
|
- }
|
|
|
- INTERFACE_LOG.info("[HTTP-RESP]. status=" + HttpStatus.OK.value());
|
|
|
- if (jsonSerializable) {
|
|
|
- String json = JsonUtil.toJson(ret);
|
|
|
- int responseJsonMaxSize = logProperties.getResponseLogJsonMaxSize();
|
|
|
- if (json.length() > responseJsonMaxSize) {
|
|
|
- INTERFACE_LOG.debug("[HTTP-RESP]. response= too large");
|
|
|
- } else {
|
|
|
- INTERFACE_LOG.debug("[HTTP-RESP]. response=" + json);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- HttpServletResponse response = ServletUtil.getResponse();
|
|
|
- response.setHeader("Trace-Id", ThreadLocalUtil.getTraceId());
|
|
|
- response.setHeader("cost", String.valueOf(System.currentTimeMillis() - startTime));
|
|
|
-
|
|
|
- INTERFACE_LOG.info(StringUtil.join("[HTTP-OK]. path=\"", path, "\", method=[", method,
|
|
|
- "] ; cost ", System.currentTimeMillis() - startTime, " ms."));
|
|
|
-
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
+ private static final ExamCloudLog DEBUG_LOG = ExamCloudLogFactory.getLog(ControllerAspect.class);
|
|
|
+
|
|
|
+ private static final ExamCloudLog INTERFACE_LOG = ExamCloudLogFactory.getLog("INTERFACE_LOGGER");
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ HttpMethodProcessor httpMethodProcessor;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ LogProperties logProperties;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ RedisClient redisClient;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构造函数
|
|
|
+ */
|
|
|
+ public ControllerAspect() {
|
|
|
+ super();
|
|
|
+ DEBUG_LOG.info("ControllerAspect init...");
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String[] excludeFields = new String[]{"password", ".*Password"};
|
|
|
+
|
|
|
+ /**
|
|
|
+ * handlerMethods
|
|
|
+ *
|
|
|
+ * @author WANGWEI
|
|
|
+ */
|
|
|
+ @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) "
|
|
|
+ + "|| @annotation(org.springframework.web.bind.annotation.GetMapping) "
|
|
|
+ + "|| @annotation(org.springframework.web.bind.annotation.PostMapping)"
|
|
|
+ + "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)"
|
|
|
+ + "|| @annotation(org.springframework.web.bind.annotation.PutMapping)")
|
|
|
+ public void handlerMethods() {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 接口处理
|
|
|
+ *
|
|
|
+ * @param joinPoint
|
|
|
+ * @return
|
|
|
+ * @author WANG WEI
|
|
|
+ */
|
|
|
+ @Around("handlerMethods()")
|
|
|
+ public Object doAroundWebRequests(ProceedingJoinPoint joinPoint) {
|
|
|
+ // 获取request对象
|
|
|
+ HttpServletRequest request = ServletUtil.getRequest();
|
|
|
+ String path = request.getServletPath();
|
|
|
+ try {
|
|
|
+ if ("/".equals(request.getServletPath())) {
|
|
|
+ return joinPoint.proceed();
|
|
|
+ }
|
|
|
+ } catch (Throwable e) {
|
|
|
+ INTERFACE_LOG.error(e.getMessage());
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ long startTime = System.currentTimeMillis();
|
|
|
+ String method = request.getMethod();
|
|
|
+ Object[] args = joinPoint.getArgs();
|
|
|
+
|
|
|
+ if (INTERFACE_LOG.isInfoEnabled()) {
|
|
|
+ StringBuilder params = new StringBuilder();
|
|
|
+
|
|
|
+ if (args != null) {
|
|
|
+ if (args.length == 0) {
|
|
|
+ Map<String, String[]> paramMaps = request.getParameterMap();
|
|
|
+ for (Map.Entry<String, String[]> entry : paramMaps.entrySet()) {
|
|
|
+ String name = entry.getKey();
|
|
|
+ String[] values = entry.getValue();
|
|
|
+ params.append("&").append(name).append("=");
|
|
|
+ if (ArrayUtils.isNotEmpty(values)) {
|
|
|
+ if (name.toLowerCase().indexOf(excludeFields[0]) >= 0) {
|
|
|
+ params.append("***");
|
|
|
+ } else {
|
|
|
+ params.append(values[0]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (int i = 0; i < args.length; i++) {
|
|
|
+ Object curArg = args[i];
|
|
|
+ if (curArg == null) {
|
|
|
+ params.append("null");
|
|
|
+ } else if (ObjectUtil.isBaseDataType(curArg.getClass())) {
|
|
|
+ params.append(curArg);
|
|
|
+ } else {
|
|
|
+ String content = this.parseContent(curArg);
|
|
|
+ if (content == null) {
|
|
|
+ content = "...";
|
|
|
+ } else {
|
|
|
+ if (content.length() > logProperties.getResponseLogJsonMaxSize()) {
|
|
|
+ content = "content too large...";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ params.append(content);
|
|
|
+ }
|
|
|
+ if (i < args.length - 1) {
|
|
|
+ params.append(" | ");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ INTERFACE_LOG.info(String.format("[ControllerAspect][request][%s] - %s, params = %s", method, path, params.toString()));
|
|
|
+ }
|
|
|
+
|
|
|
+ Object ret;
|
|
|
+ try {
|
|
|
+ if (null != httpMethodProcessor) {
|
|
|
+ httpMethodProcessor.beforeMethod(request, args);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行
|
|
|
+ ret = joinPoint.proceed();
|
|
|
+
|
|
|
+ request.setAttribute(HttpServletRequestAttribute.$_EXCEPTION_HAPPENED.name(), new Boolean(false));
|
|
|
+ } catch (Throwable e) {
|
|
|
+ request.setAttribute(HttpServletRequestAttribute.$_EXCEPTION_HAPPENED.name(), new Boolean(true));
|
|
|
+
|
|
|
+ if (null != httpMethodProcessor) {
|
|
|
+ try {
|
|
|
+ httpMethodProcessor.onException(request, args, e);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ INTERFACE_LOG.error("[ControllerAspect] processor1 - " + ex.getMessage(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ INTERFACE_LOG.error(String.format("[ControllerAspect][execute fail][%s] - %s, cost %sms, err is %s"
|
|
|
+ , method, path, System.currentTimeMillis() - startTime, e.getMessage()));
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (null != httpMethodProcessor) {
|
|
|
+ try {
|
|
|
+ httpMethodProcessor.onSuccess(request, args, ret);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ INTERFACE_LOG.error("[ControllerAspect] processor2 - " + ex.getMessage(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (null != ret && BaseResponse.class.isAssignableFrom(ret.getClass())) {
|
|
|
+ BaseResponse baseResponse = (BaseResponse) ret;
|
|
|
+ baseResponse.setCost(System.currentTimeMillis() - startTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (INTERFACE_LOG.isDebugEnabled() && logProperties.isNormalResponseLogEnable()) {
|
|
|
+ if (ret == null) {
|
|
|
+ INTERFACE_LOG.debug("[ControllerAspect] status = 200, responseMsg = void");
|
|
|
+ } else if (ret instanceof ResponseEntity) {
|
|
|
+ ResponseEntity<?> re = (ResponseEntity<?>) ret;
|
|
|
+ Object body = re.getBody();
|
|
|
+
|
|
|
+ String content = null;
|
|
|
+ if (body != null) {
|
|
|
+ boolean jsonSerializable = false;
|
|
|
+ if (body instanceof Collection) {
|
|
|
+ jsonSerializable = true;
|
|
|
+ } else if (body instanceof Map) {
|
|
|
+ jsonSerializable = true;
|
|
|
+ } else if (body instanceof String) {
|
|
|
+ jsonSerializable = true;
|
|
|
+ } else if (body instanceof JsonSerializable) {
|
|
|
+ jsonSerializable = true;
|
|
|
+ }
|
|
|
+ if (jsonSerializable) {
|
|
|
+ content = this.parseContent(body);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (content == null) {
|
|
|
+ content = "...";
|
|
|
+ } else {
|
|
|
+ if (content.length() > logProperties.getResponseLogJsonMaxSize()) {
|
|
|
+ content = "content too large...";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ INTERFACE_LOG.debug(String.format("[ControllerAspect] status = %s, responseMsg = %s",
|
|
|
+ re.getStatusCodeValue(), content));
|
|
|
+ } else {
|
|
|
+ String content;
|
|
|
+ if (ObjectUtil.isBaseDataType(ret.getClass())) {
|
|
|
+ content = ret.toString();
|
|
|
+ } else {
|
|
|
+ content = this.parseContent(ret);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (content == null) {
|
|
|
+ content = "...";
|
|
|
+ } else {
|
|
|
+ if (content.length() > logProperties.getResponseLogJsonMaxSize()) {
|
|
|
+ content = "content too large...";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ INTERFACE_LOG.debug(String.format("[ControllerAspect] status = 200, responseMsg = %s", content));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ HttpServletResponse response = ServletUtil.getResponse();
|
|
|
+ response.setHeader("Trace-Id", ThreadLocalUtil.getTraceId());
|
|
|
+ response.setHeader("cost", String.valueOf(System.currentTimeMillis() - startTime));
|
|
|
+
|
|
|
+ INTERFACE_LOG.info(String.format("[ControllerAspect][response][%s] - %s, cost %sms", method, path, System.currentTimeMillis() - startTime));
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String parseContent(Object obj) {
|
|
|
+ try {
|
|
|
+ if (obj instanceof MultipartFile) {
|
|
|
+ return ((MultipartFile) obj).getOriginalFilename();
|
|
|
+ }
|
|
|
+
|
|
|
+ return new JsonMapper().toJson(obj);
|
|
|
+ } catch (Exception e) {
|
|
|
+ INTERFACE_LOG.warn("[ControllerAspect] parseContent " + e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|