自定義 Spring 通用日誌註解

来源:https://www.cnblogs.com/bloodcolding/archive/2023/03/16/17222913.html
-Advertisement-
Play Games

自定義 Spring 通用日誌註解 1. 註解@Metrics @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Metrics { /** * ...


目錄

自定義 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 = 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 = 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 = "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"
    }
  ]
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 編寫程式過程中,我們有時不希望改變某個變數的值。此時就可以使用關鍵字 const 對變數的類型加以限定。 初始化和const 因為const對象一旦創建後其值就不能再改變,所以const對象必須初始化。一如既往,初始值可以是任意複雜的表達式: const int i = get_size();//正 ...
  • yaml 1.yaml介紹 YAML是 "YAML Ain't a Markup Language" (YAML不是一種標記語言)的遞歸縮寫。在開發這種語言時,YAML的意思其實是:"Yet Another Markup Language"(仍是一種標記語言),是為了強調這種語言以數據為中心,而不是 ...
  • Lombok、Spring-Initializer 1.Lombok 1.1Lombok介紹 Lombok的作用是: 簡化Javabean的開發,可以使用Lombok的註解讓代碼更加簡潔 Java項目中,很多沒有技術含量又必須存在的代碼:比如POJO類的getter、setter、toString方 ...
  • 一、前期準備 1、首先需要安裝並配置好本地JDK(WIN+R輸入cmd,輸入java -version如下圖) 2、下載maven到本地(鏈接Maven – Download Apache Maven) 其他歷史版本在這裡找:Index of /maven/maven-3 (apache.org) ...
  • 進入官網 Dcat Admin - Php後臺開發框架 這裡要選擇1.x 下麵來安裝框架 安裝完laravel之後,需要修改.env文件,設置資料庫鏈接設置正確 安裝 dcat-admin composer require dcat/laravel-admin 然後運行下麵的命令來發佈資源: php ...
  • 1.系統簡介 需求:進入系統顯示系統功能界面,功能如下: 1、添加學員 2、刪除學員 3、修改學員信息 4、查詢學員信息 5、顯示所有學員信息 6、退出系統 系統共6個功能,用戶根據自己需求選取。 2.步驟分析 顯示功能界面 用戶輸入功能序號 根據用戶輸入的功能序號,執行不同的功能(函數) 定義函數 ...
  • 大數據時代,各行各業對數據採集的需求日益增多,網路爬蟲的運用也更為廣泛,越來越多的人開始學習網路爬蟲這項技術,K哥爬蟲此前已經推出不少爬蟲進階、逆向相關文章,為實現從易到難全方位覆蓋,特設【0基礎學爬蟲】專欄,幫助小白快速入門爬蟲,本期為抓包工具的使用。 抓包工具概述 抓包工具,顧名思義,就是抓取網 ...
  • 緩存擊穿是指緩存中沒有的數據,而查詢非常頻繁的數據,導致大量的請求落到了資料庫上,因此很容易導致資料庫連接數暴增,甚至導致宕機。 下麵是 PHP 解決緩存擊穿問題的一般解決方案: // 獲取 Key $key = 'my_key'; // 根據 Key 從 Redis 中獲取數據 $data = $ ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...