我們已經不用AOP做操作日誌了很久了!

往期熱門文章:

1、往期精選優秀博文都在這裏了!

2、強烈推薦 16 款 IDEA 插件,讓你的開發速度飛起來!

3、Java中當對象不再使用時,不賦值爲null會導致什麼後果 ?

4、終於找全啦!一二線城市知名互聯網公司名單!對着找就對了

5、IntelliJ IDEA 更新後,電腦卡成球,該如何優化?





前言

用戶在操作我們系統的過程中,針對一些重要的業務數據進行增刪改查的時候,我們希望記錄一下用戶的操作行爲,以便發生問題時能及時的找到依據,這種日誌就是業務系統的操作日誌。

本篇我們來探討下常見操作日誌的實現方案和可行性

常見的操作日誌類型

  • 用戶登錄日誌

  • 重要數據查詢日誌 (但電商可能不重要的數據也做埋點,比如在淘寶上你搜索什麼商品,即使不買,一段時間內首頁也會給你推薦類似的東西)

  • 重要數據變更日誌 (如密碼變更,權限變更,數據修改等)

  • 數據刪除日誌

  • ......

總結來說,就是重要的增刪改查根據業務的需要來做操作日誌的埋點。

實現方案對比

基於AOP(切面)傳統的實現方案

  • 優點:實現思路簡單;

  • 缺點:增加數據庫的負擔,強依賴前端的傳參,不方便拓展,不支持批量操作,不支持多表關聯;

基於數據庫Binlog

  • 優點:解除了數據新舊變化的耦合,支持批量操作,方便多表關聯拓展,不依賴開發語言;

  • 缺點:數據庫表設計需要統一的約定;

方案實現細節

一、基於AOP切面+註解的傳統方案

傳統的做法就是切面+註解的方式,這種對代碼的侵入性不強,通常記錄ip、業務模塊、操作賬號、操作場景、操作來源等等,一般在註解+攔截器裏這些值都拿得到,如下圖所示:

這種常見的我們在通用方法都可以處理,但是在數據變更方面,一直沒有較好的實現方式,比如數據在變更前是多少,變更後是多少。

以我們以前實現的一套方案來說,基於數據變更的記錄方式不僅要和需求方約定好模板(上百個字段的不可能都做展示和記錄),也要和前端做一些約定,比如在修改之前的值是多少,修改後的值是多少,如下代碼客官請看:

    @Valid
    @NotNull(message = "新值不能爲空")
    @UpdateNewDataOperationLog
    private T newData;

    @Valid
    @NotNull(message = "舊值不能爲空")
    @UpdateOldDataOperationLog
    private T oldData;

存在的問題:

  • 1.舊值如果不多查詢一次數據庫則需要依賴前端把舊值封裝到oldData對象中,很有可能已經不是修改前的值;

  • 2.無法處理批量的List數據;

  • 3.不支持多表操作;

再以一個場景爲例,再刪除之前需要記錄刪除前的值,是不是還得再查一次~

    @PostMapping("/delete")
    @ApiOperation(value = "刪除用戶信息", notes = "刪除用戶信息")
    @DeleteOperationLog(system = SystemNameNewEnum.SYS_JMS_LMDM, module = ModuleNameNewEnum.LMDM_AUTH, table = LogBaseTableNameEnum.TABLE_USER, methodName = "detail")

二、基於數據庫Binlog 方案

系統架構圖如下:

「主要分爲3塊:」

  • 1:業務應用 生成每次操作的traceid,並更新到操作的業務表中,發送1條業務消息,包含當前操作的操作人相關的信息;

  • 2:日誌收集應用 對業務日誌和轉換後的binlog日誌做整合,提供對外的日誌查詢搜索API;

  • 3:日誌處理應用

    • 利用canal採集和解析業務庫的binlog日誌並投遞到kafka中,解析後的記錄中記錄了當前操作的操作類型,如屬於刪除、修改、新增,和新舊值的記錄,格式如下:

{"data":[{"id":"122158992930664499","bill_type":"1","create_time":"2020-04-2609:15:13","update_time":"2020-04-2613:45:46","version":"2","trace_id":"exclude-f04ff706673d4e98a757396efb711173"}],
"database":"yl_spmibill_8",
"es":1587879945200,
"id":17161259,
"isDdl":false,
"mysqlType":{"id":"bigint(20)",
"bill_type":"tinyint(2)",
"create_time":"timestamp",
"update_time":"timestamp",
"version":"int(11)",
"trace_id":"varchar(50)"},
"old":[{"update_time":"2020-04-2613:45:45",
"version":"1",
"trace_id":"exclude-36aef98585db4e7a98f9694c8ef28b8c"}],
"pkNames":["id"],"sql":"",
"sqlType":{"id":-5,"bill_type":-6,"create_time":93,"update_time":93,"version":4,"trace_id":12},
"table":"xxx_transfer_bill_117",
"ts":1587879945698,"type":"UPDATE"}

處理完binlon日誌轉換後的操作日誌,如下:

  {
  "id":"120716921250250776",
  "relevanceInfo":"XX0000097413282,",
  "remark":"簽收財務網點編碼由【】改爲【380000】,
  簽收網點名稱由【】改爲【泉州南安網點】,簽收網點code由【】改爲【2534104】,運單狀態code由【204】改爲【205】,簽收財務網點名稱由【】改爲【福建代理區】,簽收網點id由【0】改爲【461】,簽收標識,1是,0否由【0】改爲【1】,簽收時間由【null】改爲【2020-04-24 21:09:47】,簽收財務網點id由【0】改爲【400】,",
  "traceId":"120716921250250775"
  }

庫表設計

  • 1:所有業務系統表需要添加trace_id字段,每次操作生成一個隨機字符串並保存到業務表中;

  • 2:日誌收集應用庫表設計

    CREATE TABLE `table_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `database_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '數據庫名',
  `table_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ' 數據庫表名',
  PRIMARY KEY (`id`),
  UNIQUE KEY `unq_data_name_table_name` (`database_name`,`table_name`) USING BTREE COMMENT '數據庫名錶名聯合索引'
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='數據庫配置表';

CREATE TABLE `table_field_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `table_config_id` bigint(20) DEFAULT NULL,
  `field` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字段 數據庫',
  `field_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字段 中文名稱',
  `enum_flag` tinyint(2) DEFAULT NULL COMMENT '是否枚舉字段(1:是,0:否)',
  `relevance_flag` tinyint(2) DEFAULT NULL COMMENT '是否是關聯字段(1:是,0否)',
  `sort` int(11) DEFAULT NULL COMMENT '排序',
  PRIMARY KEY (`id`),
  KEY `idx_table_config_id` (`table_config_id`) USING BTREE COMMENT '表ID索引'
) ENGINE=InnoDB AUTO_INCREMENT=2431 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='數據庫字段配置表';

CREATE TABLE `table_field_value` (
  `id` bigint(20) NOT NULL,
  `field_config_id` bigint(20) DEFAULT NULL,
  `field_key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ' 枚舉',
  `filed_value` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '枚舉名稱',
  PRIMARY KEY (`id`),
  KEY `ids_field_config_id` (`field_config_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='數據字典配置表';

效果

image

基於binlog實現方案未來規劃

  1. 優化發送業務消息的實現,使用切面攔截減少對業務代碼的侵入;

  2. 目前暫時不支持對多表關聯操作日誌記錄,需要拓展;

總結

本文以操作日誌爲題材討論了操作日誌的實現方案和可行性,並且都已經在功能上進行實現,其中使用aop方案也是大部分中小企業的首選實現方案,但是在一些金融領域以及erp相關係統,對操作日誌記錄明細要求極高,常見技術方案很難滿足,即使能夠滿足也會帶來一些代碼強侵入以及性能問題,所以我們又討論了基於binlog實現的方案,該方案雖然比對aop來說增強了技術的複雜性,但是對於有一定技術積累的團隊來說不算什麼難事,並且該方案我們都實現了上線,並且解決了代碼層面上的侵入,屬於跨語言級別的,相信對讀者還是有一定的啓發。

最後的最後,如果你覺得本文有收穫,來個點贊轉發可好~

往期熱門文章:
1、《歷史文章分類導讀列表!精選優秀博文都在這裏了!》
2、圖解Spring循環依賴,看過之後面試再也不用慌了!
3、他來了!IDEA 2020.1 新版介紹!不過升級前請注意避坑!
4、七個略火的Spring Boot+Vue開源項目!
5、Linux 11個炫酷的終端命令!你知道幾個?
6、十個你可能不曾用過的Linux命令!巨好用!
7、分庫分表  PK NewSQL數據庫!
8、快給你的Spring Boot做個埋點監控吧!
9、一入職!就遇到上億(MySQL)大表的優化....
10、驚呆了,Spring Boot居然這麼耗內存!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章