联调神器,处理你未考虑到的bug。打日志,请求日志
目录:
1、ErrorAopAdvice
package sj.aopLog; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; /** * 处理全局异常,记日志 */ @Slf4j @RestControllerAdvice public class ErrorAopAdvice { /** * global 处理异常 */ @ExceptionHandler(Throwable.class) public Object handleError(HttpServletRequest req, Throwable throwable) { log.info("handleError", throwable); //记日志,接口返回值,状态码500。发生未捕获异常 ErrorResult r = ErrorResult.of(throwable, req); log.info("ErrorResult: " + JSON.toJSONString(r, SerializerFeature.WriteMapNullValue), throwable); return r; } }
2、ErrorResult
package sj.aopLog; import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; import lombok.extern.slf4j.Slf4j; import javax.servlet.http.HttpServletRequest; import java.time.LocalTime; /** * 发生未捕获异常,接口的返回值。 * 序列化排序的注解:@JSONType(orders @JSONField(ordinal = 1) * 指定JSON.toJSONString,属性的输出顺序 */ @Slf4j @Data //@JSONType(orders = {"t", "code", "msg", "url", "queryString", "urlParam", "methodParam", "body"}) public class ErrorResult { @JSONField(ordinal = 1) public int t;//time @JSONField(ordinal = 2) public int code; @JSONField(ordinal = 3) public String msg; @JSONField(ordinal = 4) public String url; @JSONField(ordinal = 5) public String queryString; @JSONField(ordinal = 6) public Object urlParam; //地址的参数 @JSONField(ordinal = 7) public Object reqBody; //请求体,request的payload @JSONField(ordinal = 8) public Object ctrlMethParam; //controller方法的入参,controller,method,parameter public static <E extends Throwable> ErrorResult of(E throwable, HttpServletRequest req) { ErrorResult r = new ErrorResult(); r.t = getTime(); r.code = 500; r.msg = throwable.toString(); r.url = req.getRequestURL().toString(); r.queryString = req.getQueryString(); r.urlParam = ReqUtil.getParam(req); r.reqBody = ReqUtil.getPayload(req); r.ctrlMethParam = req.getAttribute(LogAop.methodParam); return r; } /** * 此刻。hmms。时分秒 * * @return 6166:6点16分6秒 */ public static int getTime() { StringBuilder str = new StringBuilder(); LocalTime t = LocalTime.now(); int h = t.getHour() % 12;//对12取模,12小时制 int m = t.getMinute(); int s = t.getSecond() / 10;//秒数,取第一位 return h * 1000 + m * 10 + s; } }
3、LogAop
package sj.aopLog; import lombok.extern.slf4j.Slf4j; 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.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.Map; /** * 切面日志,记日志 */ @Slf4j @Component @Aspect public class LogAop { public static final String methodParam = "methodParam"; /** * 排除切面日志的切入点 */ @Pointcut("@annotation( sj.aopLog.LogAopExclude)") public void logAopExclude() { } /** * 切入点 */ @Pointcut("execution(public * sj.controller..*.*(..))") public void controller() { } /** * 环绕操作 */ @Around("controller() && !logAopExclude()") public Object doAround(ProceedingJoinPoint point) throws Throwable { HttpServletRequest request = ReqUtil.getHttpServletRequest(); //保存 controller方法的参数值,到req的属性。 Map<String, Object> methodParam = null; try { methodParam = ReqUtil.getMethodParam(point); request.setAttribute(LogAop.methodParam, methodParam); } catch (Exception e) { log.info("Before.error", e); } Object proceedResult = point.proceed(); //记日志,接口返回值,状态码200。 try { RightResultLog rightResultLog = RightResultLog.of(proceedResult, request, methodParam); log.info("RightResult: " + rightResultLog); } catch (Exception e) { log.info("AfterReturning.error", e); } return proceedResult; } }
4、LogAopExclude
package sj.aopLog; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 切面日志的排除注解。不记日志,则在方法上加此注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogAopExclude { }
5、ReqUtil
package sj.aopLog; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.util.StreamUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.Set; @Slf4j public class ReqUtil { /** * 获取当前 request */ public static HttpServletRequest getHttpServletRequest() { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes != null) { return attributes.getRequest(); } else { log.info("上下文获取不到request"); throw new RuntimeException("当前线程的上下文,获取不到request"); } } /** * 简化 ParameterMap。simplify */ public static Map<String, Object> getParam(HttpServletRequest req) { Map<String, String[]> mapStrArr = req.getParameterMap(); Map<String, Object> r = new HashMap<>(mapStrArr.size()); Set<String> keys = mapStrArr.keySet(); for (String key : keys) { String[] strArr = mapStrArr.get(key); if (strArr.length == 1) { r.put(key, strArr[0]); } else { r.put(key, strArr); } } return r; } /** * 获取 payload,req的body */ public static Object getPayload(HttpServletRequest req) { String method = req.getMethod();//请求方式 // POST请求 才有payload if ("POST".equals(method)) { try { String payload = new String(StreamUtils.copyToByteArray(req.getInputStream())); return JSON.parse(payload); } catch (Exception e) { log.info("getPayload", e); return "getPayload Excp"; } } else { return null; } } /** * 获取 controller方法的参数 */ public static Map<String, Object> getMethodParam(JoinPoint joinPoint) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String[] parameterNames = methodSignature.getParameterNames(); Object[] args = joinPoint.getArgs(); Map<String, Object> map = new HashMap<>(); for (int i = 0; i < parameterNames.length; i++) { map.put(parameterNames[i], args[i]); } return map; } }
6、RightResultLog
package sj.aopLog; import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; import lombok.extern.slf4j.Slf4j; import javax.servlet.http.HttpServletRequest; /** * 接口正确返回的日志 */ @Slf4j @Data public class RightResultLog { @JSONField(ordinal = 1) public Object r; //result,返回值,出参 @JSONField(ordinal = 2) public String url; @JSONField(ordinal = 3) public String queryString; @JSONField(ordinal = 4) public Object urlParam; //地址的参数 @JSONField(ordinal = 5) public Object reqBody; //请求体,request的payload @JSONField(ordinal = 6) public Object ctrlMethParam; //controller方法的入参,controller,method,parameter public static RightResultLog of(Object result, HttpServletRequest req, Object ctrlMethParam) { RightResultLog log = new RightResultLog(); log.r = result; log.url = req.getRequestURL().toString(); log.queryString = req.getQueryString(); log.urlParam = ReqUtil.getParam(req); log.reqBody = ReqUtil.getPayload(req); log.ctrlMethParam = ctrlMethParam; return log; } }