Created
April 12, 2018 13:06
-
-
Save eagleon/acc818391125fdb7cff951f1ff2b5cac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.jd.sea.pc.id.item.aspect; | |
import com.alibaba.fastjson.JSON; | |
import com.google.common.base.Strings; | |
import com.jd.epx.alarm.Alarm; | |
import com.jd.sea.pc.id.item.comm.annotation.Watch; | |
import com.jd.sea.pc.id.item.domain.comm.RpcResult; | |
import com.jd.sea.pc.id.item.domain.constant.ItemConstant.Http; | |
import java.lang.reflect.Method; | |
import java.util.Arrays; | |
import java.util.Set; | |
import java.util.UUID; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import javax.validation.ConstraintViolation; | |
import javax.validation.Validation; | |
import javax.validation.ValidatorFactory; | |
import javax.validation.executable.ExecutableValidator; | |
import lombok.extern.slf4j.Slf4j; | |
import org.aspectj.lang.JoinPoint; | |
import org.aspectj.lang.ProceedingJoinPoint; | |
import org.aspectj.lang.annotation.AfterReturning; | |
import org.aspectj.lang.annotation.Around; | |
import org.aspectj.lang.annotation.Aspect; | |
import org.aspectj.lang.annotation.Before; | |
import org.aspectj.lang.annotation.Pointcut; | |
import org.aspectj.lang.reflect.MethodSignature; | |
import org.slf4j.MDC; | |
import org.springframework.core.annotation.AnnotationUtils; | |
import org.springframework.stereotype.Service; | |
import org.springframework.web.context.request.RequestContextHolder; | |
import org.springframework.web.context.request.ServletRequestAttributes; | |
/** | |
* Controller 统一处理日志,报警 Created by weiqingjing on 2018/4/4. | |
*/ | |
@Slf4j | |
@Aspect | |
@Service | |
public class ControllerAspect { | |
@Pointcut("execution(* com.jd.sea.pc.id.item.controller..*.*(..)) && @annotation(com.jd.sea.pc.id.item.comm.annotation.Watch)") | |
public void controllerAspect() { | |
} | |
; | |
@Around("controllerAspect()") | |
public Object controllerAround(ProceedingJoinPoint pjp) { | |
log.debug("-----------------------------controller日志开始-----------------------------"); | |
long startTime = System.currentTimeMillis(); | |
Object o; | |
try { | |
initTrace(); | |
paramValid(pjp); | |
o = pjp.proceed(); | |
} catch (Throwable e) { | |
o = exceptionHandle(pjp, e); | |
} | |
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); | |
log.info("{},耗时:{}毫秒", pjp.getSignature().toShortString(), (System.currentTimeMillis() - startTime)); | |
log.debug("-----------------------------controller日志结束-----------------------------"); | |
return o; | |
} | |
/** | |
* 方法调用之前 | |
*/ | |
@Before("controllerAspect()") | |
private void beforeMethod(JoinPoint point) { | |
String parameters = ""; | |
String shortMethodName = ""; | |
try { | |
shortMethodName = point.getSignature().toShortString(); | |
parameters = Arrays.toString(point.getArgs()); | |
log.info("{},params:{}", shortMethodName, parameters); | |
} catch (Throwable e) { | |
log.error("ControllerAspect.before执行异常,{},params:{}", shortMethodName, parameters, e); | |
Alarm.send("ControllerAspect.before执行异常:" + e.getMessage()); | |
} | |
} | |
/** | |
* 方法调用后 | |
*/ | |
@AfterReturning(returning = "o", pointcut = "controllerAspect()") | |
private void afterMethod(JoinPoint point, Object o) { | |
String result = ""; | |
String shortMethodName = ""; | |
try { | |
shortMethodName = point.getSignature().toShortString(); | |
result = JSON.toJSONString(o); | |
log.info("{},result:{}", shortMethodName, result); | |
returnTrace(); | |
} catch (Exception e) { | |
log.error("ControllerAspect.after执行异常,{},result:{}", shortMethodName, result, e); | |
Alarm.send("ControllerAspect.after执行异常:" + e.getMessage()); | |
} | |
} | |
/** | |
* 初始化trace | |
*/ | |
private void initTrace() { | |
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); | |
// 判断Http header中是否有traceId字段,如果没有,则通过随机数生成 | |
String traceId = null; | |
if (Strings.isNullOrEmpty(request.getHeader(Http.TRACE_HEADER))) { | |
traceId = request.getHeader(Http.TRACE_HEADER); | |
} | |
if (Strings.isNullOrEmpty(traceId)) { | |
traceId = UUID.randomUUID().toString(); | |
} | |
MDC.put(Http.TRACE_HEADER, traceId); | |
} | |
/** | |
* resp 中包含trace | |
*/ | |
private void returnTrace() { | |
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); | |
response.setHeader(Http.TRACE_HEADER, MDC.get(Http.TRACE_HEADER)); | |
MDC.clear(); | |
} | |
/* | |
* 发送自定义报警&记录日志 | |
*/ | |
private Object exceptionHandle(ProceedingJoinPoint point, Throwable ex) { | |
try { | |
String shortMethodName = point.getSignature().toShortString(); | |
log.error("{} throw Exception", shortMethodName, ex); | |
String methodName = point.getSignature().getName(); | |
Class<?> targetClass = point.getTarget().getClass(); | |
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes(); | |
Method method = targetClass.getMethod(methodName, parameterTypes); | |
Watch watch = AnnotationUtils.getAnnotation(method, Watch.class); | |
if (watch.alarm()) { | |
log.error("alarm message:{}|{}|{}", watch.message(), shortMethodName, ex.getMessage()); | |
Alarm.send("Controller|" + watch.message() + "|" + shortMethodName + "|" + ex.getMessage()); | |
} | |
} catch (Exception e) { | |
log.error("ControllerAspect.exceptionHandle 方法执行异常", e); | |
return RpcResult.error("ControllerAspect.aspect 方法执行异常!"); | |
} | |
return RpcResult.error(ex.getMessage() == null ? "服务器处理异常!" : ex.getMessage()); | |
} | |
private void paramValid(JoinPoint point) throws RuntimeException { | |
// 获得切入目标对象 | |
Object target = point.getThis(); | |
// 获得切入方法参数 | |
Object[] args = point.getArgs(); | |
// 获得切入的方法 | |
Method method = ((MethodSignature) point.getSignature()).getMethod(); | |
// 执行校验,获得校验结果 | |
Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, args); | |
if (!validResult.isEmpty()) { | |
StringBuilder message = new StringBuilder(); | |
validResult.forEach(constraintViolation -> { | |
message.append(constraintViolation.getMessage()).append(","); | |
}); | |
throw new RuntimeException(message.toString().substring(0, message.length() - 1)); | |
} | |
} | |
private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object[] params) { | |
return validator.validateParameters(obj, method, params); | |
} | |
private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); | |
private final ExecutableValidator validator = factory.getValidator().forExecutables(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment