我们的系统在运行的过程中,每秒钟都要面对几万甚至几十万上百万的请求的时候,很容易会产生一些问题,包括网络阻塞,数据状态错误,更有一些恶意请求。那么如果出现了问题,我们如何及时的发现,并去主动的排查,这是每个开发者都需要考虑的问题,我们不能等到用户反馈给我们,我们再去排查问题,那样会给用户带来极大的不好的用户体验,也会给公司带来不可估量的损失。
这时我们则需要一个日志的监控系统,在监测到系统异常时,能够主动的通知开发人员,进行问题的排查,并能够形成方法调用的链路,能够以方法为节点,更清晰,更准确的反映出问题点,帮助开发人员快速感知并解决问题,提升用户体验,较少公司损失。
大多情况下我们会想到通过自定义注解的方式去实现,将日志进行上报到监控系统,那么日志监控系统如何更加灵活的支持,这将是值得我们思考的问题,如果需要每个介入日志系统的业务应用都去写一个切面,那么就会产生重复造轮子的情况。
在以上的场景的情况下,一个全链路监控系统显得格外重要,如果能同时兼顾http请求与rpc请求的方法使用动态切面,在业务系统中配置相应的切面表达式是一个不错的选择,那么改如何使用动态切面呢?接下来我们看这个例子。
动态切面配置
public class DynamicAspectConfiguration implements BeanFactoryAware { private final Logger log = LoggerFactory.getLogger(DynamicAspectConfiguration.class); private BeanFactory beanFactory; @Value("${logger.expressionPointCut}") private String expressionPointCut; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { //通过接口注入beanFactory容器 this.beanFactory = beanFactory; } @Bean("alarmNotifyPointcut") @ConditionalOnClass(Pointcut.class) public Pointcut alarmNotifyPointcut() { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(expressionPointCut); pointcut.setBeanFactory(beanFactory); return pointcut; } @Bean("alarmNotifyAdvice") @ConditionalOnClass(Advice.class) public MethodInterceptor alarmNotifyAdvice() { return invocation -> { final Method method = invocation.getMethod(); NotReportLog notReportLog = ValueUtil.getDefaultIfNull(AnnotationUtils.findAnnotation(method, NotReportLog.class), AnnotationUtils.findAnnotation(method.getDeclaringClass(), NotReportLog.class)); // 指定不告警 直接执行目标方法 if (notReportLog != null) { return invocation.proceed(); } String className = method.getDeclaringClass().getName(); String methodName = method.getName(); Object[] args = invocation.getArguments(); if (args.length > 0) { // 保留原始请求参数 args = Arrays.copyOf(args, args.length); } Object result = invocation.proceed(); try { // 上报日志 } catch (Exception e) { log.error("DynamicAspectConfiguration alarmNotifyAdvice Exception", e); } return result; }; } @Bean("alarmNotifyAdvisor") @ConditionalOnClass(Advisor.class) public DefaultBeanFactoryPointcutAdvisor alarmNotifyAdvisor() { DefaultBeanFactoryPointcutAdvisor advisor = new DefaultBeanFactoryPointcutAdvisor(); advisor.setPointcut(alarmNotifyPointcut()); advisor.setAdvice(alarmNotifyAdvice()); advisor.setBeanFactory(beanFactory); return advisor; } static final class ValueUtil { static <T> T getDefaultIfNull(T object, T defaultValue) { return object != null ? object : defaultValue; } } }
切面中不进行日志上报的切面
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NotReportLog { }
yml配置
logger:
expressionPointCut: execution(* com.fly.studio.controller. * . * (..))
加载
spring.facories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.fly.monitor.core.config.DynamicAspectConfiguration