<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>

          如何使用注解優(yōu)雅的記錄操作日志

          共 6334字,需瀏覽 13分鐘

           ·

          2021-11-08 01:34

          寫(xiě)在開(kāi)頭

          本文討論如何優(yōu)雅的記錄操作日志,并且實(shí)現(xiàn)了一個(gè)SpringBoot Starter(取名log-record-starter),方便的使用注解記錄操作日志,并將日志數(shù)據(jù)推送到指定數(shù)據(jù)管道(消息隊(duì)列等)

          本文靈感來(lái)源于美團(tuán)技術(shù)團(tuán)隊(duì)的文章:如何優(yōu)雅地記錄操作日志?。文中使用的部分定義描述和示例來(lái)源于美團(tuán)原文,請(qǐng)知悉。

          本文作為《萌新寫(xiě)開(kāi)源》的開(kāi)篇,先把項(xiàng)目成品介紹給大家,之后的文章會(huì)詳細(xì)介紹,如何一步步將個(gè)人項(xiàng)目做成一個(gè)大家都能參與的開(kāi)源項(xiàng)目(如何寫(xiě)SpringBoot Starter,如何上傳到Maven倉(cāng)庫(kù),如何設(shè)計(jì)和使用注解和切面等),麻煩大家多多點(diǎn)贊支持,這是我更新的動(dòng)力。請(qǐng)大家放心,公眾號(hào)還會(huì)持續(xù)更新,我沒(méi)有忘掉密碼。:)——蠻三刀醬

          本文目錄:

          • 什么是操作日志?
          • Java中常見(jiàn)的操作日志實(shí)現(xiàn)方式
          • 實(shí)戰(zhàn):通過(guò)注解實(shí)現(xiàn)操作日志的記錄

          什么是操作日志?

          定義:操作日志主要是指對(duì)某個(gè)對(duì)象進(jìn)行新增操作或者修改操作后記錄下這個(gè)新增或者修改,操作日志要求可讀性比較強(qiáng),因?yàn)樗饕墙o用戶(hù)看的,比如訂單的物流信息,用戶(hù)需要知道在什么時(shí)間發(fā)生了什么事情。再比如,客服對(duì)工單的處理記錄信息。

          以我們系統(tǒng)內(nèi)部使用的一個(gè)CRM系統(tǒng)舉例,里面每個(gè)聯(lián)系人的資料都會(huì)有操作歷史:

          這些數(shù)據(jù)就是操作系統(tǒng)日志,這些數(shù)據(jù)通常會(huì)以結(jié)構(gòu)化數(shù)據(jù)的形式存儲(chǔ)在數(shù)據(jù)庫(kù)中,對(duì)于開(kāi)發(fā)來(lái)說(shuō),這種日志的代碼邏輯通常是非常規(guī)律,比如讀取變化前和變化后的數(shù)據(jù),獲取當(dāng)前操作人和操作時(shí)間等等。

          常見(jiàn)的操作日志實(shí)現(xiàn)方式

          在小型項(xiàng)目中,這種日志記錄的操作通常會(huì)以提供一個(gè)接口或整個(gè)日志記錄Service來(lái)實(shí)現(xiàn)。那么放到多人共同開(kāi)發(fā)的項(xiàng)目中,除了封裝一個(gè)方法,還有什么更好的辦法來(lái)統(tǒng)一實(shí)現(xiàn)操作日志的記錄?下面就要討論下在Java中,常見(jiàn)的操作日志實(shí)現(xiàn)方式。

          當(dāng)你需要給一個(gè)大型系統(tǒng)從頭到尾加上操作日志,那么除了上述的手動(dòng)處理方式,也有很多種整體設(shè)計(jì)方案:

          1. 使用Canal監(jiān)聽(tīng)數(shù)據(jù)庫(kù)記錄操作日志

          Canal應(yīng)運(yùn)而生,它通過(guò)偽裝成數(shù)據(jù)庫(kù)的從庫(kù),讀取主庫(kù)發(fā)來(lái)的binlog,用來(lái)實(shí)現(xiàn)數(shù)據(jù)庫(kù)增量訂閱和消費(fèi)業(yè)務(wù)需求。可以看我的這篇文章:

          阿里開(kāi)源MySQL中間件Canal快速入門(mén)

          這個(gè)方式有點(diǎn)是和業(yè)務(wù)邏輯完全分離,缺點(diǎn)也很大,需要使用到MySQL的Binlog,向DBA申請(qǐng)就有點(diǎn)困難。如果涉及到修改第三方接口,那么就無(wú)法監(jiān)聽(tīng)別人的數(shù)據(jù)庫(kù)了。所以調(diào)用RPC接口時(shí),就需要額外的在業(yè)務(wù)代碼中增加記錄代碼,破壞了“和業(yè)務(wù)邏輯完全分離”這個(gè)基本原則,局限性大。

          2. 通過(guò)日志文件的方式記錄


          log.info("訂單已經(jīng)創(chuàng)建,訂單編號(hào):{}",?orderNo)
          log.info("修改了訂單的配送地址:從“{}”修改到“{}”,?"金燦燦小區(qū)",?"銀盞盞小區(qū)")


          這種方式,需要手動(dòng)的設(shè)定好操作日志和其他日志的區(qū)別,比如給操作日志單獨(dú)的Logger。并且,對(duì)于操作人的記錄,需要在函數(shù)中額外的寫(xiě)入請(qǐng)求的上下文中。后期這種日志還需要在SLS等日志系統(tǒng)中做額外的抽取。

          3. 通過(guò) LogUtil 的方式記錄日志


          LogUtil.log(orderNo,?"訂單創(chuàng)建",?"小明")
          LogUtil.log(orderNo,?"訂單創(chuàng)建,訂單號(hào)"+"NO.11089999",??"小明")
          String?template?=?"用戶(hù)%s修改了訂單的配送地址:從“%s”修改到“%s”"
          LogUtil.log(orderNo,?String.format(tempalte,?"小明",?"金燦燦小區(qū)",?"銀盞盞小區(qū)"),??"小明")


          這種方式會(huì)導(dǎo)致業(yè)務(wù)的邏輯比較繁雜,最后導(dǎo)致 LogUtils.logRecord() 方法的調(diào)用存在于很多業(yè)務(wù)的代碼中,而且類(lèi)似 getLogContent() 這樣的方法也散落在各個(gè)業(yè)務(wù)類(lèi)中,對(duì)于代碼的可讀性和可維護(hù)性來(lái)說(shuō)是一個(gè)災(zāi)難。

          4. 方法注解實(shí)現(xiàn)操作日志


          @OperationLog(bizType?=?"bizType",?bizId?=?"#request.orderId",?pipeline?=?DataPipelineEnum.QUEUE)
          public?Response?function(Request?request)?{
          ??//?方法執(zhí)行邏輯
          }


          我們可以在注解的操作日志上記錄固定文案,這樣業(yè)務(wù)邏輯和業(yè)務(wù)代碼可以做到解耦,讓我們的業(yè)務(wù)代碼變得純凈起來(lái)。

          美團(tuán)的原文給出了注解記錄日志的詳細(xì)架構(gòu)設(shè)計(jì)方案,并且貼出了部分源碼。但是文中并沒(méi)有完整的開(kāi)源項(xiàng)目,由于自己也很感興趣,并且公司的業(yè)務(wù)正好也有類(lèi)似需求,所以我花了點(diǎn)時(shí)間,實(shí)現(xiàn)了一版最簡(jiǎn)易的版本,支持將操作日志傳遞到消息隊(duì)列中。

          實(shí)戰(zhàn):通過(guò)注解實(shí)現(xiàn)操作日志的記錄

          大樓不是一天建成的,美團(tuán)博客中描述的方案應(yīng)該在公司內(nèi)部已經(jīng)非常成熟了,我也沒(méi)有那么多精力一口氣吃成一個(gè)胖子,我們從最基礎(chǔ)的版本寫(xiě)起。

          我給自己的這個(gè)項(xiàng)目,或者說(shuō)依賴(lài)包起名為log-record-starter,一方面遵循springboot-starter命名規(guī)范,一方面也表明項(xiàng)目的用處,記錄日志。

          開(kāi)啟項(xiàng)目之前,先問(wèn)問(wèn)自己

          Q:你這個(gè)依賴(lài)包,又是一個(gè)冗余的造輪子吧?市面上這種東西是不是已經(jīng)夠多了?

          A:本著有現(xiàn)成輪子絕不造輪子的原則,我在Github和其他網(wǎng)站進(jìn)行了一系列的相關(guān)搜索,Github有幾個(gè)類(lèi)似的實(shí)現(xiàn)項(xiàng)目,不過(guò)都以個(gè)人實(shí)現(xiàn)為主,沒(méi)有一個(gè)具有一定影響力的成熟項(xiàng)目。基于我在自己的業(yè)務(wù)項(xiàng)目中擁有實(shí)際的場(chǎng)景需求,并且目前還沒(méi)有滿(mǎn)足我需求的現(xiàn)成可接入依賴(lài),我才開(kāi)始這個(gè)依賴(lài)包的代碼編寫(xiě)。

          Q:我用了你這個(gè)依賴(lài)包,是不是很復(fù)雜?之后你不維護(hù)了的話(huà),是不是坑我們這些吃螃蟹的?

          A:依賴(lài)包的維護(hù)問(wèn)題一直是一個(gè)大問(wèn)題,本著最小依賴(lài),盡量可擴(kuò)展的原則。本庫(kù)特點(diǎn)如下:

          • 使用SpringBoot Starter,接入只需要簡(jiǎn)單引入一個(gè)依賴(lài)。
          • 通過(guò)Spring Spel表達(dá)式拿到參數(shù),對(duì)你的業(yè)務(wù)邏輯沒(méi)有侵入性。
          • 默認(rèn)使用RabbitMq傳遞日志消息,日志操作解耦。
          • 之后會(huì)引入其他數(shù)據(jù)源,例如Kafka等(畢竟還要給三歪的項(xiàng)目用,我沒(méi)有被三歪綁架,嗯,絕對(duì)沒(méi)有)。

          好了,這就是我想說(shuō)在前面的話(huà)。下面就是該項(xiàng)目的使用介紹和應(yīng)用場(chǎng)景介紹。

          Log-record-starter 一句話(huà)介紹

          本項(xiàng)目支持用戶(hù)使用注解的方式從方法中獲取操作日志,并推送到指定數(shù)據(jù)源

          只需要簡(jiǎn)單的加上一個(gè)@OperationLog便可以將方法的參數(shù),返回結(jié)果甚至是異常堆棧通過(guò)消息隊(duì)列發(fā)送出去,統(tǒng)一處理。

          @OperationLog(bizType?=?"bizType",?bizId?=?"#request.orderId",?pipeline?=?DataPipelineEnum.QUEUE)
          public?Response?function(Request?request)?{
          ??//?方法執(zhí)行邏輯
          }

          使用方法

          只需要簡(jiǎn)單的三步:

          第一步:SpringBoot項(xiàng)目中引入依賴(lài)


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

          這里先打斷一下,由于Maven公共倉(cāng)庫(kù),是全球唯一托管的,個(gè)人開(kāi)發(fā)的項(xiàng)目要提交上去,需要復(fù)雜的審核流程,我搞了一會(huì)沒(méi)搞定,就先將包傳到了Github Package上(實(shí)際就是Github的私有Maven庫(kù)),所以大家引入依賴(lài)后,是不會(huì)直接拉到包的,需要配置下你的Maven settings.xml文件。(之后我肯定想辦法發(fā)到公共倉(cāng)庫(kù),嗚嗚嗚~

          配置很簡(jiǎn)單,兩步,一步是去Github登錄,到自己的Settings中,申請(qǐng)一個(gè)token,拿到一串字符串。

          image-20211106162359065

          第二步,找到你的settings.xml文件,添加上:

          activeProfiles>
          ????github
          ??

          ??
          ????
          ??????github
          ??????
          ????????
          ??????????central
          ??????????https://repo1.maven.org/maven2
          ????????

          ????????
          ??????????github
          ??????????https://maven.pkg.github.com/OWNER/REPOSITORY
          ??????????
          ????????????true
          ??????????

          ????????

          ??????

          ????

          ??


          ??
          ????
          ??????github
          ??????這里填寫(xiě)你的Github用戶(hù)名
          ??????這里填寫(xiě)你剛才申請(qǐng)的token
          ????

          ??

          還搞不定的同學(xué),這里是Github官方中文教程:

          https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry

          重啟下你的IDEA,能看到下面這個(gè),應(yīng)該你的settings.xml生效了。

          目前我的版本號(hào)是1.0.0,之后會(huì)更新,未來(lái)最新版本號(hào)在我倉(cāng)庫(kù)查詢(xún):

          https://github.com/qqxx6661/logRecord

          第二步:在Spring配置文件中添加RabbitMq數(shù)據(jù)源配置

          在自己公司里由于阿里封裝了自己的MQ叫做MetaQ,并沒(méi)有對(duì)外開(kāi)源,所以這里先接入了RabbitMQ,也算是比較通用,圖個(gè)方便。未來(lái)會(huì)接其他數(shù)據(jù)源。RabbitMq的安裝在這里不展開(kāi)了,實(shí)在是不想把篇幅拉得太大,大家可以自行谷歌下,比如“Docker安裝RabbitMq”類(lèi)似的文章,幾分鐘就可以設(shè)置安裝好。

          log-record.rabbitmq.host=localhost
          log-record.rabbitmq.port=5672
          log-record.rabbitmq.username=admin
          log-record.rabbitmq.password=xxxxxxxx
          log-record.rabbitmq.queue-name=logrecord
          log-record.rabbitmq.routing-key=
          log-record.rabbitmq.exchange-name=logrecord


          第三步:在你自己的項(xiàng)目中,在需要記錄日志的方法上,添加注解。


          @OperationLog(bizType?=?"bizType",?bizId?=?"#request.orderId",?pipeline?=?DataPipelineEnum.QUEUE)
          public?Response?function(Request?request)?{
          ?//?方法執(zhí)行邏輯
          }


          • (必填)bizType:業(yè)務(wù)類(lèi)型
          • (必填)bizId:唯一業(yè)務(wù)ID(支持SpEL表達(dá)式)
          • (必填)pipeline:數(shù)據(jù)管道,目前只有QUEUE一個(gè)數(shù)據(jù)管道,后續(xù)可考慮接入更多數(shù)據(jù)源
          • (非必填)msg:需要傳遞的其他數(shù)據(jù)(支持SpEL表達(dá)式)
          • (非必填)tag:自定義標(biāo)簽

          代碼工作原理

          由于采用的是SpringBoot Starter方式,所以只要你是用的是SpringBoot,會(huì)自動(dòng)掃描到依賴(lài)包中的類(lèi),并自動(dòng)通過(guò)Spring進(jìn)行配置和管理。

          該注解通過(guò)在切面中解析SpEL參數(shù)(啥事SpEL?快去谷歌下,之后要講),將數(shù)據(jù)發(fā)往數(shù)據(jù)源。目前僅支持RabbitMq,發(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)]


          LogDTO是定義的消息結(jié)構(gòu):

          logId:生成的UUID
          bizId:注解中傳遞的bizId
          bizType:注解中傳遞的bizType
          exception:若方法執(zhí)行失敗,寫(xiě)入執(zhí)行的異常信息
          operateDate:操作執(zhí)行的當(dāng)前時(shí)間
          success:方式是否執(zhí)行成功
          msg:注解中傳遞的tag
          tag:注解中傳遞的tag


          我還加上了重復(fù)注解的支持,可以在一個(gè)方法上同時(shí)加多個(gè)@OperationLog,下圖是最終使用效果,可以看到,有幾個(gè)@OperationLog,就能同時(shí)發(fā)送多條日志:

          項(xiàng)目具體的實(shí)現(xiàn)原理和細(xì)節(jié),放在下一篇文章詳細(xì)講。(肯定會(huì)填坑)

          應(yīng)用場(chǎng)景

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

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

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

          三、特定操作更新數(shù)據(jù)表:我的業(yè)務(wù)中,幾個(gè)系統(tǒng)互相吞吐數(shù)據(jù),訂單的一部分?jǐn)?shù)據(jù)存留在外部系統(tǒng)里,我們最終目標(biāo)想要將其中一個(gè)系統(tǒng)替代掉,所以需要攔截他們的數(shù)據(jù),恰好幾個(gè)系統(tǒng)是使用LINK作為網(wǎng)關(guān)的,我們將數(shù)據(jù)請(qǐng)求攔截一層,并將攔截的方法使用該二方庫(kù)進(jìn)行全部參數(shù)的發(fā)送,將數(shù)據(jù)同步寫(xiě)入我們自己的數(shù)據(jù)庫(kù)中,實(shí)現(xiàn)”雙寫(xiě)“。

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

          附錄:Demo

          最后,肯定有小伙伴希望有一個(gè)完整的使用Demo,這就奉上!

          https://github.com/qqxx6661/systemLog

          總結(jié)

          本文帶大家了解了操作日志在Java中的幾種實(shí)現(xiàn)方式,并且初步介紹了自己的實(shí)現(xiàn)代碼,在之后的文章里,我會(huì)把實(shí)現(xiàn)的細(xì)節(jié),包括如何部署到Maven倉(cāng)庫(kù)等一一和大家嘮嘮,記得留下你的點(diǎn)贊和收藏~

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

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


          往期精彩文章:


          老外為了在MacBook上玩原神,讓M1支持了所有iOS應(yīng)用


          老是忘記?Linux常用命令精編匯總


          API網(wǎng)關(guān)才是大勢(shì)所趨?SpringCloud Gateway保姆級(jí)入門(mén)教程


          從吳某凡事件中理解什么是"中間人攻擊"


          誰(shuí)會(huì)拒絕一臺(tái)Win11和MacOS無(wú)縫切換的MacBook呢?Parallels17極速體驗(yàn)

          - END -


          瀏覽 89
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  国产AⅤ无码片毛片一级一区2 | 无码国产精品96久久久久孕妇 | 蜜桃精品无码视频网站 | 成人影视导航 | 日韩高清一级片 |