一起進階一起拿高工資盟广!Java開發(fā)進階-log4j2日志脫敏原理分析

大家好筋量,我是tin桨武,這是我的第5篇原創(chuàng)文章

本文講述在考慮對業(yè)務(wù)系統(tǒng)代碼入侵最小的情況下實現(xiàn)日志脫敏的方案原理。文章很長性誉,包括了日志脫敏起由错览、編碼實現(xiàn)倾哺、log4j2.xml文件加載原理、log4j2的插件機制等却邓,最后還抖出注解編譯處理器AbstractProcessor,實現(xiàn)編譯期動態(tài)生成代碼衙耕!有點像撿到寶橙喘,畢竟以前沒關(guān)注過注解編譯處理器饰潜,先上一個目錄:

  • 一彭雾、為什么做日志脫敏

  • 二薯酝、log4j2日志脫敏編碼實現(xiàn)

  • 三、源碼探索log4j2日志脫敏實現(xiàn)原理
    1做葵、什么是slf4j酿矢?
    2棠涮、log4j2又是什么?
    3驳糯、slf4j和log4j2是如何完成綁定的酝枢?
    4、log4j.xml配置文件是如何加載的竣付?
    5古胆、我們定義log4j2的Plugin插件又是如何加載注冊的逸绎?
    6巫糙、AbstractProcessor注解處理器

  • 四曲秉、朋友請留步

一、為什么做日志脫敏

日志打印非常常見且重要亥鸠,這毋庸置疑负蚊,但有這樣一種情況:我們打印的日志包含了用戶的隱私信息家妆,比如做登錄支付的打印用戶賬號和密碼,做金融的打印用戶的卡號等哨坪,這些日志先不說放在磁盤上管理不當可能造成用戶隱私泄露,再者就算是合規(guī)檢查忿偷,它也是不過關(guān)的,必須要做處理整治臊泌。

我們打日志是怎么打的鲤桥?先上一個圖(日志中打印我的用戶名和賬號),看看我們自己就是這么用的:

沒做特殊處理缺虐,不出意外芜壁,日志輸出是這樣的:

image

卡號打印出來了礁凡,隨后這行日志就安詳?shù)靥稍谖覀兎?wù)器磁盤上慧妄。
[圖片上傳失敗...(image-bdaa9d-1611469924727)]

二、log4j2日志脫敏編碼實現(xiàn)

如何借助日志框架實現(xiàn)對賬號打碼脫敏剪芍,而不入侵業(yè)務(wù)代碼塞淹?廢話不多說,先看看我已實現(xiàn)的效果:

image

本文基于slf4j+log4j2實現(xiàn)罪裹,我們代碼日志輸出處沒有任何改動饱普,打打印出來的日志對卡號做了打碼脫敏。

本文實現(xiàn)日志打碼脫敏的方案涉及開發(fā)的地方有兩個:

一是實現(xiàn)log4j2的RewritePolicy接口状共,重寫logEvent套耕;

二是修改log4j2.xml文件。

看看我寫的RewritePolicy實現(xiàn)類:

image

log4j2.xml修改峡继,下面是log4j2配置和rewrite配置:

image

這個文件也非常詳細地把log4j2.xml配置解釋了一遍冯袍,不是很清楚log4j配置的可留圖保存啦。

為了方便復(fù)制碾牌,把log4j2.xml配置的源碼粘貼一份出來:

<?xml version="1.0" encoding="UTF-8"?>
<configuration monitorInterval="5">
<!--變量配置-->
<Properties>
    <!-- 格式化輸出:%date表示日期康愤,HH:mm:ss.SSS表示日期格式,%thread表示線程名舶吗,%-5level表示級別從左顯示5個字符寬度征冷,
    %C{1.}表示類全限定名輸出精度,%-4L輸出日志所在行行號誓琼,%msg代表日志消息检激,%n是換行符-->
    <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %C{1.} %-4L : %msg%n"/>
    <!-- 定義日志存儲的路徑.${web:rootDir}表示當前工程目錄, -->
    <property name="FILE_PATH" value="../log/tin-example"/>
    <property name="FILE_NAME" value="tin-example"/>
</Properties>

<appenders>
    <!--控制臺輸出-->
    <console name="Console" target="SYSTEM_OUT">
        <!--輸出日志的格式-->
        <PatternLayout pattern="${LOG_PATTERN}"/>
        <!--表示輸出level=debug級別及以上日志(onMatch)踊赠,debug級別以下不輸出(onMismatch)-->
        <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
    </console>

    <Rewrite name="rewrite">
        <DataMaskingRewritePolicy/>
        <AppenderRef ref="Console"/>
    </Rewrite>

    <!-- 打印出所有級別的日志信息呵扛,并自動滾動存檔-->
    <RollingFile name="AllLevelRollingFile" fileName="${FILE_PATH}/${FILE_NAME}.log"
                 filePattern="${FILE_PATH}/${FILE_NAME}-ALL-%d{yyyy-MM-dd}_%i.log.gz">
        <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="ACCEPT"/>
        <PatternLayout pattern="${LOG_PATTERN}"/>
        <Policies>
            <!--interval屬性用來指定多久滾動一次,interval=1表示1小時滾動一次-->
            <TimeBasedTriggeringPolicy interval="1"/>
            <!--size=20表示文件大于20M滾動一次-->
            <SizeBasedTriggeringPolicy size="20MB"/>
        </Policies>
        <!-- max=15表示同文件夾下最多10個文件筐带,再多則會覆蓋今穿,DefaultRolloverStrategy如不設(shè)置,則默認為7個-->
        <DefaultRolloverStrategy max="10"/>
    </RollingFile>

    <!-- 打印出所有error及以上級別的信息伦籍,并自動滾動存檔-->
    <RollingFile name="ErrorRollingFile" fileName="${FILE_PATH}/error.log"
                 filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
        <!--輸出level及以上級別的信息(onMatch)蓝晒,level以下直接拒絕(onMismatch)-->
        <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
        <PatternLayout pattern="${LOG_PATTERN}"/>
        <Policies>
            <!--interval屬性用來指定多久滾動一次,interval=1表示1小時滾動一次-->
            <TimeBasedTriggeringPolicy interval="1"/>
            <!--size=20表示文件大于20M滾動一次-->
            <SizeBasedTriggeringPolicy size="20MB"/>
        </Policies>
        <!-- max=15表示同文件夾下最多10個文件帖鸦,再多則會覆蓋芝薇,DefaultRolloverStrategy如不設(shè)置,則默認為7個-->
        <DefaultRolloverStrategy max="10"/>
    </RollingFile>

</appenders>

<!--Logger節(jié)點用來單獨指定日志的形式作儿,可以給不同包配置不同的日志打印策略洛二。-->
<loggers>
    <logger name="com.tin.example.spring.log4j2" level="info" additivity="false">
        <AppenderRef ref="rewrite"/>
    </logger>

    <root level="debug">
        <appender-ref ref="Console"/>
        <appender-ref ref="AllLevelRollingFile"/>
        <appender-ref ref="ErrorRollingFile"/>
    </root>
</loggers>
</configuration>

三、源碼探索log4j2日志脫敏原理

為何上文這么做就能實現(xiàn)日志打碼脫敏?是有什么變法么晾嘶?實現(xiàn)的原理是什么妓雾?背著一大連串疑問,現(xiàn)在我們從slf4j和log4j2原理說起垒迂,來了械姻,搬好凳子了。

1机断、什么是slf4j楷拳?

slf4j全稱simple logging facade for Java。是一個日志接口框架吏奸,配合日志輸出系統(tǒng)實現(xiàn)日志輸出欢揖。slf4j并不是真正輸出日志的系統(tǒng),只是定義了一套日志規(guī)范奋蔚。類似這樣的日志門面還有commons-logging浸颓。

private static final Logger LOGGER = LoggerFactory.getLogger(AccountTest.class);

以上的Logger就是slf4j的類。

2旺拉、log4j2又是什么产上?

log4j2才是一個真正的日志系統(tǒng),它才是我們項目中打印日志的代碼庫實現(xiàn)蛾狗。除了log4j2晋涣,我們常見的日志庫還有l(wèi)og4j、logback沉桌、jdk-logging谢鹊。

slf4j作為連接log和代碼層的中間層,我們只要使用slf4j提供的接口留凭,不用關(guān)心日志的具體實現(xiàn)(想想這樣的好處是我們可以把業(yè)務(wù)系統(tǒng)內(nèi)日志庫比如log4j2換為logback也沒問題)佃扼。說起來有點像jdbc,我們切換不同的數(shù)據(jù)庫蔼夜,jdbc幫我們做好了兼容兼耀。

log4j2的依賴包有3個,slf4j和log4j2的幾個jar包關(guān)系作用如下:

image

3求冷、slf4j和log4j2是如何完成綁定的瘤运?

從上面圖都看到了,slf4j-api和log4j相關(guān)的包根本不在一起匠题,那么它們之間是通過什么關(guān)聯(lián)的拯坟?

答案是:

slf4j指定路徑進行類加載,log4j必然有橋接實現(xiàn)類韭山。 還是從這行定義和初始化Logger的代碼開始看起:

private static final Logger LOGGER = LoggerFactory.getLogger(AccountTest.class);

從LoggerFactory.getLogger一直進入到LoggerFactory類的bind方法郁季,找到staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet()冷溃,這里即是slf4j完成綁定log4j2的地方:

image

findPossibleStaticLoggerBinderPathSet()通過指定路徑加載一個StaticLoggerBinder類:

image

指定查找org/slf4j/impl/StaticLoggerBinder.class進行加載。

那么StaticLoggerBinder應(yīng)該在哪里梦裂?

當然是在log4j2包內(nèi)了秃诵!

image

通過StaticLoggerBinder這個類即完成了slf4j和log4j的綁定,看下圖塞琼。

image

綁定完之后即通過getLoggerFactory方法獲取到Log4jLoggerFactory:

image

log4j2和slf4j完成了綁定,那么禁舷,和本文所說的脫敏原理有什么關(guān)系彪杉?

脫敏的實現(xiàn)原理真正在于log4j2,以上只是展開說明日志系統(tǒng)的基本關(guān)聯(lián)原理牵咙,為接下來講述log4j的插件機制打個鋪墊派近。

log4j2 通過使用插件機制加載各種組件,比如appender, logger等洁桌,我們的脫敏方案編碼定義了一個類:

image

實現(xiàn)了log4j的rewrite策略類渴丸,這其實就是一種插件!

要講清楚Plugin原理得分兩部分講另凌。

一是log4j.xml配置文件是如何加載的谱轨;

二是我們定義的Plugin插件又是如何加載注冊的。

4吠谢、log4j.xml配置文件是如何加載的土童?

我們依然是通過斷點看源碼,畢竟工坊,源碼底下無謊言献汗! 還是從下面這行代碼開始看起:

image

上文已經(jīng)提到過Log4jLoggerFactory,它繼承了AbstractLoggerAdapter這個抽象類王污,我們直接進入到getContext方法獲取Logger的地方:

image

anchor中文譯為"錨"罢吃,這里是通過Java反射得到日志類,anchor不為null昭齐,因此進入到后面的語句尿招。

image

進入getContext,我們的Log4jContextFactory又出現(xiàn)了阱驾,它在LogManager中的靜態(tài)代碼塊中已初始化好泊业。

image

我們繼續(xù)到Log4jContextFactory內(nèi)看getContext:

image

已初始化好的selector,內(nèi)部具體如何獲取context有興趣可自行debug啊易,我們進入到ctx.start方法內(nèi):

image

看到reconfigure()方法吁伺,就知道log4j準備開始加載配置了!W馓浮篮奄!再從reconfigure一直往下看:

image

687行獲取得到一個XmlConfiguration捆愁,這是因為我們使用的是xml配置文件!?呷础昼丑!正常來說配置文件除了xml,還有properties夸赫,yaml菩帝,json等。

此處既然已獲得配置文件的內(nèi)容茬腿, 那么我們退回去看ConfigurationFactory.getInstance().getConfiguration(this,contextName,configURI,cl)呼奢。

image

看看XmlConfigurationFactory類

image

指定了xml后綴,getConfiguration實際返回XmlConfiguration

image

根據(jù)configSource的log4jx.xml文件切平,進行配置內(nèi)容加載握础。

image

到這里xml配置就算是加載完成啦。xml里面定義的<DataMaskingRewritePolicy/>標簽也會被加載悴品。

[圖片上傳失敗...(image-28c8f9-1611469924727)]

接下來禀综,自然而然的我們就會問,這個標簽和代碼@Plugin注解定義的插件是如何關(guān)聯(lián)起來的苔严?或者又說Plugin插件是如何加載的定枷?

5、我們定義的Plugin插件又是如何加載注冊的届氢?

log4j中的Plugin注解提供了一種便捷的方法將一個類聲明成 log4j2 的插件依鸥,比如我單測用到的案例:

image

在log4j2加載上下文的時候會加載Plugin,log4j統(tǒng)一用PluginRegistry注冊中心加載和注冊插件悼沈,并由PluginManager來管理贱迟。

進入到PluginManager:

image

注釋都寫得很清楚,從指定的指定文件Log4j2Plugin.dat加載插件絮供,繼續(xù)進入loadFromMainClassLoader方法

image
image

不同模塊不同jar包都有可能存在Log4j2Plugins.dat文件衣吠,比如log4j-core包存在

image

我們自己編寫代碼定義的插件則被編譯到target目錄下(因為我的是mac,在控制臺的看得壤靶,win系統(tǒng)也一樣找到編譯產(chǎn)生的target文件夾即可)

image

我們編譯生成的Log4j2Plugins.dat里面的內(nèi)容又是什么呢缚俏?

image

文件記錄了插件分類、全限定類名等信息贮乳。

說到這里忧换,產(chǎn)生新的一個疑問,我們自己的Log4j2Plugins.dat 文件究竟是如何被生成到target目錄下的向拆?

6亚茬、AbstractProcessor注解處理器

這不得不說我們的注解編譯處理器咯!注解分為兩種類型浓恳,一種是運行時注解刹缝,另一種是編譯時注解。編譯時注解的核心要依賴APT(Annotation Processing Tools)實現(xiàn)梢夯,基本原理就是在類颂砸、方法噪奄、字段等上添加注解,在編譯時人乓,編譯器通過掃描AbstractProcessor的子類,把對應(yīng)合適的注解傳入process函數(shù),然后我們開發(fā)人員可以在編譯期進行相應(yīng)的邏輯處理了温鸽”M停看看log4j實現(xiàn)的注解編譯處理器:

image

我們平常編碼很少會用到注解編譯處理器,有興趣可自行寫單元測試試一試姑尺,這種沒玩過的代碼寫起來還挺有趣的蝠猬。不過自行寫的話需要聲明好javax.annotation.processing.Processor文件,再補一張log4j聲明的文件:

image

四柄粹、朋友請留步

我是tin匆绣,一個在努力讓自己變得更優(yōu)秀的普通攻城獅。閱歷有限堪夭、學(xué)識淺薄拣凹,如你有發(fā)現(xiàn)文章不妥之處嚣镜,非常歡迎加我提出,我一定細心推敲加以修改雕旨。

看到這里請安排個鼓勵再走吧,堅持原創(chuàng)不容易棒搜,你的正反饋是我堅持輸出的最強大動力活箕,謝謝啦。

[圖片上傳失敗...(image-782447-1611469924727)]

總結(jié)克蚂、提升
做一個快樂的攻城獅
構(gòu)筑屬于自己的一方天地

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末埃叭,一起剝皮案震驚了整個濱河市悉罕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌类早,老刑警劉巖嗜逻,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栈顷,死亡現(xiàn)場離奇詭異,居然都是意外死亡屏富,警方通過查閱死者的電腦和手機蛙卤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門颤难,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人已日,你說我怎么就攤上這事栅屏。” “怎么了护奈?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵霉旗,是天一觀的道長厌秒。 經(jīng)常有香客問我,道長檐晕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮橄抹,結(jié)果婚禮上况木,老公的妹妹穿的比我還像新娘。我一直安慰自己苛让,他們只是感情好,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布瘦材。 她就那樣靜靜地躺著仿畸,像睡著了一般错沽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上憔儿,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天放可,我揣著相機與錄音,去河邊找鬼拾氓。 笑死劫樟,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的奶陈。 我是一名探鬼主播附较,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拒课,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了僻肖?” 一聲冷哼從身側(cè)響起卢鹦,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤冀自,失蹤者是張志新(化名)和其女友劉穎熬粗,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驻呐,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡含末,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年答渔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宋雏。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡磨总,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娶牌,到底是詐尸還是另有隱情馆纳,我是刑警寧澤鲁驶,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站径荔,受9級特大地震影響脆霎,放射性物質(zhì)發(fā)生泄漏睛蛛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一菠红、第九天 我趴在偏房一處隱蔽的房頂上張望难菌。 院中可真熱鬧郊酒,春花似錦键袱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至坐梯,卻和暖如春刹帕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹋辅。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工晕翠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留砍濒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓樊卓,卻偏偏與公主長得像碌尔,于是被迫代替她去往敵國和親券敌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容