1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 6334字,需瀏覽 13分鐘

         ·

        2021-11-08 01:34

        寫在開頭

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

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

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

        本文目錄:

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

        什么是操作日志?

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

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

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

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

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

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

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

        Canal應(yīng)運(yùn)而生,它通過偽裝成數(shù)據(jù)庫的從庫,讀取主庫發(fā)來的binlog,用來實(shí)現(xiàn)數(shù)據(jù)庫增量訂閱和消費(fèi)業(yè)務(wù)需求??梢钥次业倪@篇文章:

        阿里開源MySQL中間件Canal快速入門

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

        2. 通過日志文件的方式記錄


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


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

        3. 通過 LogUtil 的方式記錄日志


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


        這種方式會導(dǎo)致業(yè)務(wù)的邏輯比較繁雜,最后導(dǎo)致 LogUtils.logRecord() 方法的調(diào)用存在于很多業(yè)務(wù)的代碼中,而且類似 getLogContent() 這樣的方法也散落在各個業(yè)務(wù)類中,對于代碼的可讀性和可維護(hù)性來說是一個災(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ù)代碼變得純凈起來。

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

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

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

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

        開啟項(xiàng)目之前,先問問自己

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

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

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

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

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

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

        Log-record-starter 一句話介紹

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

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

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

        使用方法

        只需要簡單的三步:

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


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

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

        配置很簡單,兩步,一步是去Github登錄,到自己的Settings中,申請一個token,拿到一串字符串。

        image-20211106162359065

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

        activeProfiles>
        ????github
        ??

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

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

        ????????

        ??????

        ????

        ??


        ??
        ????
        ??????github
        ??????這里填寫你的Github用戶名
        ??????這里填寫你剛才申請的token
        ????

        ??

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

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

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

        目前我的版本號是1.0.0,之后會更新,未來最新版本號在我倉庫查詢:

        https://github.com/qqxx6661/logRecord

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

        在自己公司里由于阿里封裝了自己的MQ叫做MetaQ,并沒有對外開源,所以這里先接入了RabbitMQ,也算是比較通用,圖個方便。未來會接其他數(shù)據(jù)源。RabbitMq的安裝在這里不展開了,實(shí)在是不想把篇幅拉得太大,大家可以自行谷歌下,比如“Docker安裝RabbitMq”類似的文章,幾分鐘就可以設(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ù)類型
        • (必填)bizId:唯一業(yè)務(wù)ID(支持SpEL表達(dá)式)
        • (必填)pipeline:數(shù)據(jù)管道,目前只有QUEUE一個數(shù)據(jù)管道,后續(xù)可考慮接入更多數(shù)據(jù)源
        • (非必填)msg:需要傳遞的其他數(shù)據(jù)(支持SpEL表達(dá)式)
        • (非必填)tag:自定義標(biāo)簽

        代碼工作原理

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

        該注解通過在切面中解析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í)行失敗,寫入執(zhí)行的異常信息
        operateDate:操作執(zhí)行的當(dāng)前時間
        success:方式是否執(zhí)行成功
        msg:注解中傳遞的tag
        tag:注解中傳遞的tag


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

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

        應(yīng)用場景

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

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

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

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

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

        附錄:Demo

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

        https://github.com/qqxx6661/systemLog

        總結(jié)

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

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

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


        往期精彩文章:


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


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


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


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


        誰會拒絕一臺Win11和MacOS無縫切換的MacBook呢?Parallels17極速體驗(yàn)

        - END -


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

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            美女直播一区二区三区 | 国产菊爆刚交视频在 | 特黄A级毛片免费视频 | 美日本大片一区二区福利片亚洲 | 中文一级电影 | 开心激情网站 | 狠狠穞A片一區二區三區 | 安娜荒淫牲艳史 | 成人无码A级毛片 | 国产精品极品美女观看免费 |