spring aop實(shí)現(xiàn)日志切面

日志打印AOP配置

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Metrics {
    /**
     * 在方法成功執(zhí)行后打點(diǎn),記錄方法的執(zhí)行時(shí)間發(fā)送到指標(biāo)系統(tǒng),默認(rèn)開(kāi)啟
     *
     * @return
     */
    boolean recordSuccessMetrics() default true;

    /**
     * 在方法成功失敗后打點(diǎn),記錄方法的執(zhí)行時(shí)間發(fā)送到指標(biāo)系統(tǒng),默認(rèn)開(kāi)啟
     *
     * @return
     */
    boolean recordFailMetrics() default true;

    /**
     * 通過(guò)日志記錄請(qǐng)求參數(shù),默認(rèn)開(kāi)啟
     *
     * @return
     */
    boolean logParameters() default true;

    /**
     * 通過(guò)日志記錄方法返回值,默認(rèn)開(kāi)啟
     *
     * @return
     */
    boolean logReturn() default true;

    /**
     * 出現(xiàn)異常后通過(guò)日志記錄異常信息,默認(rèn)開(kāi)啟
     *
     * @return
     */
    boolean logException() default true;

    /**
     * 出現(xiàn)異常后忽略異常返回默認(rèn)值,默認(rèn)關(guān)閉
     *
     * @return
     */
    boolean ignoreException() default false;
}
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MetricsAspect {
    private static final Logger log = LoggerFactory.getLogger(MetricsAspect.class);

    /**
     * 實(shí)現(xiàn)一個(gè)返回Java基本類(lèi)型默認(rèn)值的工具。其實(shí),你也可以逐一寫(xiě)很多if-else判斷類(lèi)型,然后手動(dòng)設(shè)置其默認(rèn)值。這里為了減少代碼量用了一個(gè)小技巧,即通過(guò)初始化一個(gè)具有1個(gè)元素的數(shù)組,然后通過(guò)獲取這個(gè)數(shù)組的值來(lái)獲取基本類(lèi)型默認(rèn)值
     */
    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 -> (Class<?>) clazz, clazz -> Array.get(Array.newInstance(clazz, 1), 0)));

    public static <T> T getDefaultValue(Class<T> clazz) {
        return (T) DEFAULT_VALUES.get(clazz);
    }

    /**
     * @annotation指示器實(shí)現(xiàn)對(duì)標(biāo)記了Metrics注解的方法進(jìn)行匹配
     */
    @Pointcut("@annotation(com.pab.hug.center.aop.Metrics)")
    public void withMetricsAnnotation() {
    }

    @Around("withMetricsAnnotation()")
    public Object metrics(ProceedingJoinPoint pjp) throws Throwable {
        //通過(guò)連接點(diǎn)獲取方法簽名和方法上Metrics注解,并根據(jù)方法簽名生成日志中要輸出的方法定義描述
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Metrics metrics = signature.getMethod().getAnnotation(Metrics.class);

        String name = String.format("【%s】【%s】", signature.getDeclaringType().toString(), signature.toLongString());

        //實(shí)現(xiàn)的是入?yún)⒌娜罩据敵?        if (metrics.logParameters()) {
            log.info(String.format("【入?yún)⑷罩尽空{(diào)用 %s 的參數(shù)是:【%s】", name, JSON.toJSONString(pjp.getArgs())));
        }
        //實(shí)現(xiàn)連接點(diǎn)方法的執(zhí)行,以及成功失敗的打點(diǎn),出現(xiàn)異常的時(shí)候還會(huì)記錄日志
        Object returnValue;
        Instant start = Instant.now();
        try {
            returnValue = pjp.proceed();
            if (metrics.recordSuccessMetrics()) {
                //在生產(chǎn)級(jí)代碼中,我們應(yīng)考慮使用類(lèi)似Micrometer的指標(biāo)框架,把打點(diǎn)信息記錄到時(shí)間序列數(shù)據(jù)庫(kù)中,實(shí)現(xiàn)通過(guò)圖表來(lái)查看方法的調(diào)用次數(shù)和執(zhí)行時(shí)間,在設(shè)計(jì)篇我們會(huì)重點(diǎn)介紹
                log.info(String.format("【成功打點(diǎn)】調(diào)用 %s 成功,耗時(shí):%d ms", name, Duration.between(start, Instant.now()).toMillis()));
            }
        } catch (Exception ex) {
            if (metrics.recordFailMetrics()) {
                log.info(String.format("【失敗打點(diǎn)】調(diào)用 %s 失敗,耗時(shí):%d ms", name, Duration.between(start, Instant.now()).toMillis()));
            }
            if (metrics.logException()) {
                log.error(String.format("【異常日志】調(diào)用 %s 出現(xiàn)異常!", name), ex);
            }

            //忽略異常的時(shí)候,使用一開(kāi)始定義的getDefaultValue方法,來(lái)獲取基本類(lèi)型的默認(rèn)值
            if (metrics.ignoreException()) {
                returnValue = getDefaultValue(signature.getReturnType());
            } else {
                throw ex;
            }
        }
        //實(shí)現(xiàn)了返回值的日志輸出
        if (metrics.logReturn()) {
            log.info(String.format("【出參日志】調(diào)用 %s 的返回是:【%s】", name, returnValue));
        }
        return returnValue;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容