数据统计埋点需求

背景

随着公司代码的迭代,有一些垃圾代码逻辑冗余在项目中,导致消耗了资源又不好维护。为了保险,需要在线上统计代码使用的频率,剔除无用代码。

描述

方法便可分为如下几种:

  1. 对于确定没用的代码,可以先注释掉,并替换为error日志,保证遇到问题及时发现。
  2. 对疑似无用代码,可以使用统计方法调用次数,运行一段时间,判断去留。

设计

第一种方法就是,注释代码添加日志,所以我们着重描述第二种情况:

使用 spring aop 进行拦截,注意只能拦截被 ioc 管理的对象

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CountInvokeTimes {
}
@Aspect
@Slf4j
@Component
public class CountInvokedTimesAspect {
    private final RedisUtils redisUtil;

    public CountInvokedTimesAspect(RedisUtils redisUtil) {
        this.redisUtil = redisUtil;
    }

    @Pointcut("@within(com.tao.annotation.CountInvokeTimes) || @annotation(com.tao.annotation.CountInvokeTimes)")
    public void countInvokeTimes() {
    }

    @Around(value = "countInvokeTimes()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Class<?>[] argTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            argTypes[i] = args[i].getClass();
        }
        try {
            String methodName = joinPoint.getSignature().getName();
            Method method = joinPoint.getTarget().getClass().getMethod(methodName, argTypes);
            Class<?> aClass = joinPoint.getTarget().getClass();
            String className  = aClass.getName();
            boolean isClassAnnotationPresent = aClass.isAnnotationPresent(CountInvokeTimes.class);
            boolean isMethodAnnotationPresent = method.isAnnotationPresent(CountInvokeTimes.class);
            if (isClassAnnotationPresent || isMethodAnnotationPresent) {
                // 此处也可以发送mq消息给统计服务
                redisUtil.getRedisTemplate().opsForZSet().incrementScore(RedisConstant.METHOD_STATISTICS, className + "." + methodName, 1);
            }
        } catch (Exception e) {
            log.error("CountInvokedTimesAspect error", e);
        }

        Object object = null;
        try {
            object = joinPoint.proceed();
        } catch (Throwable throwable) {
            log.error("CountInvokedTimesAspect error", throwable);
        }
        return object;
    }
}
 
    @RequestMapping("/getMethodStatistics")
    public String getMethodStatistics() {
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisUtils.getRedisTemplate().opsForZSet().rangeWithScores(RedisConstant.METHOD_STATISTICS, 0, -1);
        return JSON.toJSONString(typedTuples);
    }
posted @ 2024-02-27 23:51  帅气的涛啊  阅读(8)  评论(0编辑  收藏  举报