目錄
- 自定義 Spring 通用日志注解
- 1. 注解@Metrics
- 2. 切面MetricsAspect
- 3. 自動注入AutoConfiguration
- 4. 組態檔MetricsProperties
- 5. 其它配置
- 配置自動注入
- 組態檔提示
自定義 Spring 通用日志注解
1. 注解@Metrics
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Metrics {
/**
* 在方法成功執行后打點,記錄方法的執行時間發送到指標系統,默認開啟
*/
boolean recordSuccessMetrics() default true;
/**
* 在方法成功失敗后打點,記錄方法的執行時間發送到指標系統,默認開啟
*/
boolean recordFailMetrics() default true;
/**
* 通過日志記錄請求引數,默認開啟
*/
boolean logParameters() default true;
/**
* 通過日志記錄方法回傳值,默認開啟
*/
boolean logReturn() default true;
/**
* 出現例外后通過日志記錄例外資訊,默認開啟
*/
boolean logException() default true;
/**
* 出現例外后忽略例外回傳默認值,默認關閉
*/
boolean ignoreException() default false;
2. 切面MetricsAspect
@Aspect
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MetricsAspect {
/**
* 讓Spring幫我們注入ObjectMapper,以方便通過JSON序列化來記錄方法入參和出參
*/
@Resource
private ObjectMapper objectMapper;
/**
* 實作一個回傳Java基本型別默認值的工具,其實,你也可以逐一寫很多if-else判斷型別,然后手動設定其默認值,
* 這里為了減少代碼量用了一個小技巧,即通過初始化一個具有1個元素的陣列,然后通過獲取這個陣列的值來獲取基本型別默認值
*/
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 -> clazz, clazz -> Array.get(Array.newInstance(clazz, 1), 0)));
public static <T> T getDefaultValue(Class<T> clazz) {
//noinspection unchecked
return (T) DEFAULT_VALUES.get(clazz);
}
/**
* 標記了Metrics注解的方法進行匹配
*/
@Pointcut("@annotation(com.common.config.metrics.annotation.Metrics)")
public void withMetricsAnnotationMethod() {
}
/**
* within指示器實作了匹配那些型別上標記了@RestController注解的方法
* 注意這里使用了@,標識了對注解標注的目標進行切入
*/
@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
public void controllerBean() {
}
@Pointcut("@within(com.common.config.metrics.annotation.Metrics)")
public void withMetricsAnnotationClass() {
}
@Around("controllerBean() || withMetricsAnnotationMethod() || withMetricsAnnotationClass()")
public Object metrics(ProceedingJoinPoint pjp) throws Throwable {
// 通過連接點獲取方法簽名和方法上Metrics注解,并根據方法簽名生成日志中要輸出的方法定義描述
MethodSignature signature = (MethodSignature) pjp.getSignature();
Metrics metrics = signature.getMethod().getAnnotation(Metrics.class);
String name = String.format("【%s】【%s】", signature.getDeclaringType().toString(), signature.toLongString());
if (metrics == null) {
@Metrics
final class InnerClass {
}
metrics = InnerClass.class.getAnnotation(Metrics.class);
}
// 嘗試從請求背景關系獲得請求URL
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
name += String.format("【%s】", request.getRequestURL().toString());
}
// 入參的日志輸出
if (metrics.logParameters()) {
log.info(String.format("【入參日志】呼叫 %s 的引數是:【%s】", name, objectMapper.writeValueAsString(pjp.getArgs())));
}
// 連接點方法的執行,以及成功失敗的打點,出現例外的時候記錄日志
Object returnValue;
Instant start = Instant.now();
try {
returnValue = https://www.cnblogs.com/bloodcolding/archive/2023/03/16/pjp.proceed();
if (metrics.recordSuccessMetrics()) {
// 在生產級代碼中,應考慮使用類似Micrometer的指標框架,把打點資訊記錄到時間序列資料庫中,實作通過圖表來查看方法的呼叫次數和執行時間,
log.info(String.format("【成功打點】呼叫 %s 成功,耗時:%d ms", name, Duration.between(start, Instant.now()).toMillis()));
}
} catch (Exception ex) {
if (metrics.recordFailMetrics()) {
log.info(String.format("【失敗打點】呼叫 %s 失敗,耗時:%d ms", name, Duration.between(start, Instant.now()).toMillis()));
}
if (metrics.logException()) {
log.error(String.format("【例外日志】呼叫 %s 出現例外!", name), ex);
}
if (metrics.ignoreException()) {
returnValue = https://www.cnblogs.com/bloodcolding/archive/2023/03/16/getDefaultValue(signature.getReturnType());
} else {
throw ex;
}
}
// 回傳值輸出
if (metrics.logReturn()) {
log.info(String.format("【出參日志】呼叫 %s 的回傳是:【%s】", name, returnValue));
}
return returnValue;
}
3. 自動注入AutoConfiguration
@AutoConfiguration
@Slf4j
@EnableConfigurationProperties(MetricsProperties.class)
@ConditionalOnProperty(prefix = "common.metrics", name = {"keep-alive"}, havingValue = "https://www.cnblogs.com/bloodcolding/archive/2023/03/16/true", matchIfMissing = true)
public class AspectAutoConfiguration {
public AspectAutoConfiguration() {
log.info("AspectAutoConfiguration initialize.");
}
@Bean
public MetricsAspect metricsAspect() {
return new MetricsAspect();
}
}
4. 組態檔MetricsProperties
@ConfigurationProperties(prefix = "common.metrics")
public class MetricsProperties {
public Boolean getKeepAlive() {
return keepAlive;
}
public void setKeepAlive(Boolean keepAlive) {
this.keepAlive = keepAlive;
}
private Boolean keepAlive = true;
}
5. 其它配置
配置自動注入
配置resource.META-INF.spring.org.springframework.boot.autoconfigure.AutoConfiguration.imports檔案,增加RunsOnProfilesAutoConfiguration類路徑,
組態檔提示
{
"groups": [],
"properties": [
{
"name": "common.metrics.keepAlive",
"type": "java.lang.Boolean",
"sourceType": "com.common.config.metrics.properties.MetricsProperties"
}
]
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/547121.html
標籤:其他
上一篇:buaa面向物件第一單元
下一篇:php解決快取擊穿的問題
