<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          log-record正式版本發(fā)布:自定義函數(shù)、手動傳遞上下文 、本地監(jiān)聽支持

          共 6579字,需瀏覽 14分鐘

           ·

          2022-01-12 15:40

          前言

          之前寫了兩篇文章,來介紹我的log-record開源項目(優(yōu)雅記錄操作日志)是如何誕生的。

          前文:

          萌新寫開源01 | 如何使用注解優(yōu)雅的記錄操作日志

          萌新寫開源02 | 如何提交自己的項目到Maven公共倉庫

          這兩周我又迭代了幾個版本,正式把自定義函數(shù),手動傳遞上下文,本地監(jiān)聽等基礎功能做了支持,并且重新給項目編了個易于人類閱讀的文檔

          這算是正兒八經(jīng)的一次介紹了,現(xiàn)在該庫已經(jīng)正式可以在Maven倉庫拉取了。

          大家有興趣的話,建議多多使用體驗一下,給我一些反饋或者Pr,我會加油好好迭代項目。

          倉庫地址:

          https://github.com/qqxx6661/logRecord

          項目背景

          大家一定見過下圖的操作日志:

          在代碼層面,如何優(yōu)雅的實現(xiàn)上面的日志記錄呢?

          你可能會一下子想到最簡單的方式,通過封裝一個操作日志記錄類(例如LogUtil)。使用一個用戶修改配送地址的操作舉例:

          String?template?=?"用戶%s修改了訂單的配送地址:從“%s”修改到“%s”"
          LogUtil.log(orderNo,?String.format(tempalte,?"小明",?"金燦燦小區(qū)",?"銀盞盞小區(qū)"),??"小明")

          這種方式會導致業(yè)務邏輯被記錄日志的代碼侵入,對于代碼的可讀性和可維護性來說是一個災難。

          這個方式顯然不優(yōu)雅,讓我們試試使用注解:

          @OperationLog(bizType?=?"addressChange",?bizId?=?"20211102001",?msg?=?"用戶?小明?修改了訂單的配送地址:從?金燦燦小區(qū)?修改到?銀盞盞小區(qū)")
          public?Response?function(Request?request)?{
          ??//?業(yè)務執(zhí)行邏輯
          }

          可以看到,這樣日志的記錄被放到了注解,對業(yè)務代碼沒有了侵入。但是新的問題來了,我們該如何把訂單ID,用戶信息,數(shù)據(jù)庫里的舊地址,函數(shù)入?yún)⒌男碌刂穫鬟f給注解呢?

          Spring的SpEL表達式(Spring Expression Language)可以幫助我們,通過引入SpEL表達式,我們可以獲取函數(shù)的入?yún)?。這樣我們就可以對上面的注解進行修改:

          • 訂單ID:#request.orderId
          • 新地址"銀盞盞小區(qū)":#request.newAddress
          @OperationLog(bizType?=?"addressChange",?bizId?=?"#request.orderId",?msg?=?"'用戶?小明?修改了訂單的配送地址:從?金燦燦小區(qū)?修改到'?+?#request.newAddress")
          public?Response?function(Request?request)?{
          ??//?業(yè)務執(zhí)行邏輯
          }

          這樣依賴,訂單ID和地址的新值就可以通過解析入?yún)討B(tài)獲取了,但是事情還沒有結束,我們的用戶信息,以及老的配送地址,是需要業(yè)務代碼去獲取的,入?yún)⒗锊⒉粫@些數(shù)據(jù)。

          解決方案也不是沒有,我們創(chuàng)建一個日志上下文LogRecordContext,讓用戶手動傳遞代碼中計算出來的值,再交給SpEL解析。

          @OperationLog(bizType?=?"addressChange",?bizId?=?"#request.orderId",?msg?=?"'用戶'?+?#userName +?'修改了訂單的配送地址:從'?+?#oldAddress +?'修改到'?+?#request.newAddress")
          public?Response?function(Request?request)?{
          ??//?業(yè)務執(zhí)行邏輯
          ??...
          ??//?手動傳遞日志上下文:用戶信息?地址舊值
          ??LogRecordContext.putVariables("userName",?queryUserName(request.getUserId()));
          ??LogRecordContext.putVariables("oldAddress",?queryOldAddress(request.getOrderId()));
          }

          什么?你說這不就又侵入了業(yè)務邏輯了么?

          確實是的,雖然能用,但是對于有“強迫癥”的同學,這樣的實現(xiàn)還是不夠優(yōu)雅,接下來我們用自定義函數(shù),解決這個問題。

          SpEL支持在表達式中傳入用戶自定義函數(shù),我們將queryUserName和queryOldAddress這兩個函數(shù)傳遞給SpEL,SpEL在解析表達式時,會自動執(zhí)行對應函數(shù)。

          最終,我們的注解變成了這樣,并且最終記錄了日志:

          @OperationLog(bizType?=?"addressChange",?bizId?=?"#request.orderId",?msg?=?"'用戶'?+?#queryUserName(#request.userId)?+?'修改了訂單的配送地址:從'?+?#oldAddress +?'修改到'?+?#queryOldAddress(#request.orderId)")
          public?Response?function(Request?request)?{
          ??//?業(yè)務執(zhí)行邏輯
          }

          用戶 小明 修改了訂單的配送地址:從 金燦燦小區(qū) 修改到 銀盞盞小區(qū)

          項目介紹

          本倉庫幫助你通過注解優(yōu)雅地聚合項目中的操作日志,對業(yè)務代碼無侵入。

          此外,你可以方便地將所有日志推送到下列數(shù)據(jù)管道:

          1. 本地監(jiān)聽處理
          2. 發(fā)送至RabbitMQ
          3. 發(fā)送至RocketMQ

          日志內(nèi)包含:

          logId:生成的UUID
          bizId:業(yè)務唯一ID
          bizType:業(yè)務類型
          exception:函數(shù)執(zhí)行失敗時寫入異常信息
          operateDate:操作執(zhí)行時間
          success:函數(shù)是否執(zhí)行成功
          msg:注解中傳遞的msg(支持JSON)
          tag:用戶自定義標簽
          returnStr:?方法執(zhí)行成功后的返回值(JSON化)

          本項目特點:

          • 方便接入:使用Spring Boot Starter實現(xiàn),用戶直接在pom.xml引入依賴,快速接入
          • SpEL解析:直接寫表達式解析入?yún)?/section>
          • 自定義上下文:支持手動傳遞鍵值對,通過SpEL進行解析
          • 自定義函數(shù):支持注冊自定義函數(shù),通過SpEL進行解析

          使用方法

          接入方式

          只需要簡單的三步:

          第一步: SpringBoot項目中引入依賴(最新版本號請查閱Maven公共倉庫)


          ????cn.monitor4all
          ????log-record-starter
          ????1.0.4

          第二步: 添加數(shù)據(jù)源配置

          支持推送日志數(shù)據(jù)至:

          1. 本地應用監(jiān)聽
          2. RabbitMQ
          3. RocketMQ

          1. 本地應用監(jiān)聽

          若只需要在同一應用內(nèi)處理日志信息,只需要繼承抽象類CustomLogListener,便可對日志進行處理。

          @Slf4j
          @Component
          public?class?TestCustomLogListener?extends?CustomLogListener?{

          ????@Override
          ????public?void?createLog(LogDTO?logDTO)?throws?Exception?{
          ????????log.info("TestCustomLogListener?本地接收到日志?[{}]",?logDTO);
          ????}
          }

          2. RabbitMQ

          配置好RabbitMQ的發(fā)送者

          log-record.data-pipeline=rabbitMq
          log-record.rabbit-mq-properties.host=localhost
          log-record.rabbit-mq-properties.port=5672
          log-record.rabbit-mq-properties.username=admin
          log-record.rabbit-mq-properties.password=xxxxxx
          log-record.rabbit-mq-properties.queue-name=logRecord
          log-record.rabbit-mq-properties.routing-key=
          log-record.rabbit-mq-properties.exchange-name=logRecord

          3. RocketMQ

          配置好RocketMQ的發(fā)送者

          log-record.data-pipeline=rocketMq
          log-record.rocket-mq-properties.topic=logRecord
          log-record.rocket-mq-properties.tag=
          log-record.rocket-mq-properties.group-name=logRecord
          log-record.rocket-mq-properties.namesrv-addr=localhost:9876

          第三步: 在你自己的項目中,在需要記錄日志的方法上,添加@OperationLog注解。

          @OperationLog(bizType?=?"orderCreate",?bizId?=?"#request.orderId",?msg?=?"#request")
          public?Response?function(Request?request)?{
          ??//?業(yè)務執(zhí)行邏輯
          }
          • (必填)bizType:業(yè)務類型
          • (必填)bizId:唯一業(yè)務ID(支持SpEL表達式)
          • (非必填)msg:需要傳遞的其他數(shù)據(jù)(支持SpEL表達式)
          • (非必填)tag:自定義標簽

          自定義傳遞上下文

          直接引入類LogRecordContext,放入鍵值對。

          @OperationLog(bizType?=?"addressChange",?bizId?=?"#request.orderId",?msg?=?"'用戶'?+?#userName +?'修改了訂單的配送地址:從'?+?#oldAddress +?'修改到'?+?#request.newAddress")
          public?Response?function(Request?request)?{
          ??//?業(yè)務執(zhí)行邏輯
          ??...
          ??//?手動傳遞日志上下文:用戶信息?地址舊值
          ??LogRecordContext.putVariables("userName",?queryUserName(request.getUserId()));
          ??LogRecordContext.putVariables("oldAddress",?queryOldAddress(request.getOrderId()));
          }

          自定義函數(shù)

          將@LogRecordFunc注解申明在需要注冊到SpEL的自定義函數(shù)上。

          注意,需要在類上也聲明@LogRecordFunc,否則無法找到該函數(shù)。

          @LogRecordFunc
          public?class?MyFuction?{

          ????private?static?TestService?testService;

          ????@LogRecordFunc
          ????public?static?String?testFunc(String?str)?{
          ????????if?(testService?==?null)?{
          ????????????testService?=?SpringContextUtils.getBean(TestService.class);
          ????????}
          ????????return?testService.testServiceFunc2(str);
          ????}
          }
          @OperationLog(bizType?=?"addressChange",?bizId?=?"#request.orderId",?msg?=?"'用戶'?+?#queryUserName(#request.userId)?+?'修改了訂單的配送地址:從'?+?#oldAddress +?'修改到'?+?#queryOldAddress(#request.orderId)")
          public?Response?function(Request?request)?{
          ??//?業(yè)務執(zhí)行邏輯
          }

          重復注解

          @OperationLog(bizId?=?"#testClass.testId",?bizType?=?"testType1",?msg?=?"#testFunc(#testClass.testId)")
          @OperationLog(bizId?=?"#testClass.testId",?bizType?=?"testType2",?msg?=?"#testFunc(#testClass.testId)")
          @OperationLog(bizId?=?"#testClass.testId",?bizType?=?"testType3",?msg?=?"'用戶將舊值'?+?#old?+?'更改為新值'?+?#testClass.testStr")

          我們還加上了重復注解的支持,可以在一個方法上同時加多個@OperationLog,下圖是最終使用效果:

          實現(xiàn)原理

          由于采用的是SpringBoot Starter方式,會自動掃描到依賴包中的類,并自動通過Spring進行配置和管理。

          該注解通過在切面中解析SpEL參數(shù),將數(shù)據(jù)發(fā)往數(shù)據(jù)源。發(fā)送的消息體如下:

          方法處理正常發(fā)送消息體:

          [LogDTO(logId=3771ff1e-e5ff-4251-a534-31dab5b666b3,?bizId=str,?bizType=testType1,?exception=null,?operateDate=Sat?Nov?06?20:08:54?CST?2021,?success=true,?msg={"testList":["1","2","3"],"testStr":"str"},?tag=operation)]

          方法處理異常發(fā)送消息體:

          [LogDTO(logId=d162b2db-2346-4144-8cd4-aea900e4682b,?bizId=str,?bizType=testType1,?exception=testError,?operateDate=Sat?Nov?06?20:09:24?CST?2021,?success=false,?msg={"testList":["1","2","3"],"testStr":"str"},?tag=operation)]

          應用場景

          以下羅列了一些實際的應用場景,包括我業(yè)務中實際使用,并且已經(jīng)上線使用的場景。

          一、操作日志:如最上面一張CRM系統(tǒng)的圖描述的那樣,在用戶進行了編輯操作后,拿到用戶操作的數(shù)據(jù),執(zhí)行日志寫入。

          二、通知觸發(fā):由于我的業(yè)務是接手了好幾個倉庫,并且這幾個倉庫的操作串成了一條完成鏈路,我需要在鏈路的某個節(jié)點觸發(fā)給用戶的提醒,如果寫硬編碼也可以實現(xiàn),但是遠不如在方法上使用注解發(fā)送消息來得方便。例如下方在下單方法調用后發(fā)送消息。

          三、數(shù)據(jù)表雙寫:我的業(yè)務中,幾個系統(tǒng)互相吞吐數(shù)據(jù),訂單的一部分數(shù)據(jù)存留在外部系統(tǒng)里,我們最終目標想要將其中一個系統(tǒng)替代掉,所以需要攔截他們的數(shù)據(jù),將數(shù)據(jù)請求攔截一層,并將攔截的方法使用該二方庫進行全部參數(shù)的發(fā)送,將數(shù)據(jù)同步寫入我們自己的數(shù)據(jù)庫中,實現(xiàn)”雙寫“。

          四、跨應用數(shù)據(jù)聚合:和”三“類似,在多個應用中,如果需要做行為相同的業(yè)務邏輯,完全可以在各個系統(tǒng)中將數(shù)據(jù)發(fā)送到同一個消息隊列中,再進行統(tǒng)一處理。

          附錄:Demo

          最后,肯定有小伙伴希望有一個完整的客戶端Demo,這就奉上!

          完整Demo項目:

          https://github.com/qqxx6661/systemLog

          - END -


          我是目前在阿里搬磚的工程師蠻三刀醬。

          持續(xù)的創(chuàng)作離不開你的點贊和轉發(fā)分享!



          瀏覽 73
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本swag在线观看 | 天天操天天操天天操天天操 | 欧美三级精品网站 | 国产看片视频 | 成人无码一级A片在线 |