@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Metrics {
boolean recordSuccessMetrics() default true;
boolean recordFailMetrics() default true;
boolean logParameters() default true;
boolean logReturn() default true;
boolean logException() default true;
boolean ignoreException() default false;
}
@Aspect
@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MetricsAspect {
private static final Map<Class<?>, Object> DEFAULT_VALUES = Stream
.of(boolean.class, byte.class, char.class, double.class, float.class, int.class, long.class, short.class)
.collect(toMap(clazz -> clazz, clazz -> Array.get(Array.newInstance(clazz, 1), 0)));
public static <T> T getDefaultValue(Class<T> clazz) {
return (T) DEFAULT_VALUES.get(clazz);
}
@Pointcut("within(@com.cdp.gmdata.annotation.Metrics *)")
public void withMetricsAnnotation() {
}
@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
public void controllerBean() {
}
@Around("controllerBean() || withMetricsAnnotation())")
public Object metrics(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Metrics metrics = signature.getMethod().getAnnotation(Metrics.class);
String name = String.format("【%s】【%s】", signature.getDeclaringType().toString(), signature.toLongString());
if (metrics == null) {
@Metrics
final class c {
}
metrics = c.class.getAnnotation(Metrics.class);
}
String join = "";
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
name += String.format("【%s】", request.getRequestURL().toString());
//这一步获取到的方法有可能是代理方法也有可能是真实方法
Method m = ((MethodSignature) pjp.getSignature()).getMethod();
//判断代理对象本身是否是连接点所在的目标对象,不是的话就要通过反射重新获取真实方法
if (pjp.getThis().getClass() != pjp.getTarget().getClass()) {
m = ReflectUtil.getMethod(pjp.getTarget().getClass(), m.getName(), m.getParameterTypes());
}
//通过真实方法获取该方法的参数名称
LocalVariableTableParameterNameDiscoverer paramNames = new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = paramNames.getParameterNames(m);
//获取连接点方法运行时的入参列表
Object[] args = pjp.getArgs();
//将参数名称与入参值一一对应起来
Map<String, Object> params = new HashMap<>();
for (int i = 0; i < parameterNames.length; i++) {
params.put(parameterNames[i], args[i]);
}
//为log实体类的request字段赋值
join = MapUtil.join(params, "&", ":", true);
}
if (metrics.logParameters()) {
log.info(String.format("【入参】调用 %s 的参数是:【%s】", name, join));
}
Object returnValue;
Instant start = Instant.now();
long millis;
try {
returnValue = pjp.proceed();
millis = Duration.between(start, Instant.now()).toMillis();
if (metrics.recordSuccessMetrics()) {
if (millis > 2000) {
log.warn(String.format("【成功】调用 %s 成功,耗时:%d ms", name, millis));
} else {
log.warn(String.format("【成功】调用 %s 成功,耗时:%d ms", name, millis));
}
}
} catch (Exception ex) {
if (metrics.recordFailMetrics()) {
log.warn(String.format("【失败】调用 %s 成功,耗时:%d ms", name, Duration.between(start, Instant.now()).toMillis()));
}
if (metrics.logException()) {
log.error(String.format("【异常】调用 %s 出现异常!", name), ex);
}
if (metrics.ignoreException()) {
returnValue = getDefaultValue(signature.getReturnType());
} else {
throw ex;
}
}
if (metrics.logReturn()) {
log.info(String.format("【出参】调用 %s 的返回是:【%s】", name, returnValue));
}
return returnValue;
}
}