日志打印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ù)。