1、注解定义

@Target({java.lang.annotation.ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Ump {

    public abstract String key() default "";

}

2、配置数据类定义

import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;


@Data
@ConfigurationProperties(prefix = "cms.ump")
public class UmpProperties {

    /**
     * App Name
     */
    private String app;
    
    private String env;

    
    private String prefix;

    /**
     * key值是否需要包含应用名称
     *
     */
    private boolean keyStartWithApp = true;

    /**
     * 心跳key
     */
    private String systemKey;

    /**
     * jvm 监控的key
     */
    private String jvmKey;

    /**
     * 获取ump平台需要的应用名称
     *
     * @return jdos上应用名称
     */
    public String getUmpAppName() {
        if(StringUtils.isBlank(this.app)) {
            return null;
        }else if(this.app.startsWith(UmpConstant.JDOS)) {
            return this.app;
        } else {
            return UmpConstant.JDOS + this.app;
        }
    }


    @Override
    public String toString() {
        return "UmpProperties{" +
                "app='" + app + '\'' +
                ", env='" + env + '\'' +
                ", prefix='" + prefix + '\'' +
                ", keyStartWithApp='" + keyStartWithApp + '\'' +
                ", systemKey='" + systemKey + '\'' +
                ", jvmKey='" + jvmKey + '\'' +
                '}';
    }
}

3、自动配置类定义:

import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
@ConditionalOnClass(UmpAop.class)
@EnableConfigurationProperties(UmpProperties.class)
public class UmpAutoConfigure implements InitializingBean {

    @Autowired
    private UmpProperties umpProperties;

    @PostConstruct
    public void init() {
        UmpKeyBuilder.setUmpProperties(umpProperties);
        UmpUtil.setUmpProperties(umpProperties);

        if(log.isInfoEnabled()) {
            log.info("decorate.ump init. config: [{}]", umpProperties);
        }
    }

    @Bean
    @ConditionalOnMissingBean
    public UmpAop umpAspect() {
        return new UmpAop();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (StringUtils.isNotBlank(umpProperties.getSystemKey())) {
            String key = UmpKeyBuilder.genKey(umpProperties.getSystemKey());
            Profiler.InitHeartBeats(key);
        }

        if (StringUtils.isNotBlank(umpProperties.getJvmKey())) {
            String key = UmpKeyBuilder.genKey(umpProperties.getJvmKey());
            Profiler.registerJVMInfo(key);
        }
    }
}

4、切面的定义:

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Slf4j
@Order(Integer.MIN_VALUE)
@Aspect
@Component("umpAop")
public class UmpAop {

    @Autowired
    private UmpProperties umpProperties;

    @Around("@annotation(com.jd.rcloud.common.ann.Ump)")
    public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Ump ump = method.getAnnotation(Ump.class);
        if (ump == null) {
            return pjp.proceed();
        }

        //必须设置appName ump才能生效
        String appName = umpProperties.getUmpAppName();
        if (StringUtils.isBlank(appName)) {
            if(log.isWarnEnabled()) {
                log.warn("property[deorate.ump.app] not found");
            }
            return pjp.proceed();
        }

        // 获取key
        String key = ump.key();
        if (StringUtils.isBlank(key)) {
            key = UmpKeyBuilder.genKey(pjp);
        } else {
            key = UmpKeyBuilder.genKey(key);
        }


        CallerInfo info = null;
        try {
            info = Profiler.registerInfo(key, appName, ump.heartbeat(), ump.tp());
            return pjp.proceed();
        } catch (Throwable throwable) {
            log.error("{}.{} throw exception", signature.getDeclaringTypeName(), signature.getName(), throwable);

            if (info != null && ump.error()) {
                if(shouldRecord(throwable, ump.excludes())) {
                    Profiler.functionError(info);
                } else {
                    if(log.isDebugEnabled()) {
                        log.debug("Profiler.functionError ignored, key={} clazz={}", key, throwable.getClass().getName());
                    }
                }
            }
            throw throwable;
        } finally {
            if (info != null) {
                Profiler.registerInfoEnd(info);

                if(log.isDebugEnabled()) {
                    log.debug("UmpAspect appName: {}, key: {}, elapsed time: {} ms", appName, key, info.getElapsedTime());
                }

            }
        }
    }

    /**
     * 判断是否需要统计可用率
     *
     * @param throwable
     * @param clazzArr
     * @return
     */
    private boolean shouldRecord(Throwable throwable, Class<? extends Exception>[] clazzArr) {
        if(throwable == null || clazzArr == null || clazzArr.length == 0) {
            return true;
        }

        for(Class<? extends Exception> clzz : clazzArr) {
            if(throwable.getClass().equals(clzz)) {
                return false;
            }
        }

        return true;
    }
}