自制單機(jī)日志解決方案 4. 擴(kuò)展日志查詢功能
上一篇文章主要介紹的是 Talog 的日志查詢,但是其核心查詢能力較為簡單,只能根據(jù)標(biāo)簽找到對應(yīng)的日志文件,然后將所有日志全部輸出。為了滿足更加復(fù)雜的日志查詢場景,我為 Talog 擴(kuò)展了日志查詢能力。
名詞解釋
為了縮短篇幅,后文會用到幾個縮略詞,在此先進(jìn)行說明。
正則字段:通過正則表達(dá)式對日志文本進(jìn)行解析,獲得的若干個字段。例如:
json 字段:若日志索引時是 json 格式,則對日志進(jìn)行 json 反序列化所獲得的復(fù)雜對象的字段即為 json 字段
標(biāo)簽字段:指日志的標(biāo)簽屬性
使用表達(dá)式字符串來進(jìn)一步篩選日志
由于 V.Talog.Core 核心包的日志查詢功能較為簡單,因此很難滿足許多日志查詢的應(yīng)用場景,比如想要查詢某個用戶在某個時間段(起止日期在同一天)的操作日志。對于這種場景,個人建議使用用戶編號以及日期(yyyy-MM-dd 格式)作為標(biāo)簽,此時查詢某個用戶的目標(biāo)可以通過設(shè)置userId=xxx
?的查詢表達(dá)式來實現(xiàn),但是時間段就無法通過核心包來篩選了。此時,我們只能先獲取到該用戶當(dāng)天的所有日志(userId=xxx&date=yyyy-MM-dd
),然后再對日志數(shù)據(jù)進(jìn)一步篩選。
Talog 的做法就是對每一條日志都進(jìn)行解析,解析得到正則字段或者 json 字段,然后再根據(jù)這些字段做日志篩選。但是由于我希望基于字段的篩選能夠支持復(fù)雜表達(dá)式,因此很難設(shè)計出一個比較簡潔易用的流式 api,所以我最決定終只支持使用表達(dá)式字符串的方式來實現(xiàn)字段篩選。而且日志查詢的使用場景一般也都是使用瀏覽器排查業(yè)務(wù)日志,應(yīng)該很少人會在程序中有查詢?nèi)罩镜男枨?,因此只支持使用表達(dá)式字符串篩選日志的方式應(yīng)該是合理的。
表達(dá)式字符串解析
Talog 使用?V.QueryParser?來解析表達(dá)式字符串,具體解析邏輯可以翻閱文檔。值得一提的是,V.QueryParser 對表達(dá)式格式要求較為嚴(yán)格,例如key1>value1&&key2==value2||key3 like value3
,關(guān)鍵詞與關(guān)鍵詞之間、關(guān)鍵詞與運(yùn)算符之間都需要有空格,否則會解析失敗。
擴(kuò)展查詢能力
接下來,將使用以下接口來具體解釋 V.Talog.Extension 是如何擴(kuò)展 Talog 的查詢能力的。
在該擴(kuò)展接口中,首先會調(diào)用Searcher.SearchLogs(query)
?查詢出所有日志,需要特別說明的是 Query 在 V.Talog.Extension 擴(kuò)展包中可以使用表達(dá)式字符串生成(使用 V.QueryParser 解析),這個可以通過調(diào)用以下接口實現(xiàn)。
說回上一個接口,在調(diào)用 SearchLogs 獲取到所有日志數(shù)據(jù)之后,接口會根據(jù) regex 參數(shù)將每一條日志都解析成 ParsedLog(ParsedLog 包含正則字段)。隨后,會使用 V.QueryParser 將 regexQuery 參數(shù)轉(zhuǎn)換成 QueryExpression 對象,然后解析、執(zhí)行表達(dá)式結(jié)構(gòu),表達(dá)式中所使用到的字段名均需要在 ParsedLog 中可以找到,否則將會報錯。最后,接口還支持傳入排序表達(dá)式 sort,格式為:type ascthendate desc
,每個單詞之間必須用一個空格隔開。排序表達(dá)式中的字段可以是正則字段也可以是標(biāo)簽字段。
有了擴(kuò)展查詢接口,就可以靈活得滿足各種日志查詢場景了,還是用剛剛查詢操作日志的例子,可以使用以下代碼來達(dá)到目的。
配置字段類型
在使用 regexQuery 篩選日志以及使用 sort 排序日志時,都會涉及到字段數(shù)據(jù)的比較,但是 Talog 大部分情況下是無法知道字段的類型的(除了 json 字段),因此 Talog 需要用戶預(yù)先告知字段類型,V.Talog.Extension.TaloggerExtension 提供了 SetIndexMapping 接口,接口僅需要傳入一個接口參數(shù) IIndexMapping,該接口定義了兩個方法分別用于獲取標(biāo)簽字段類型以及獲取 json/正則字段類型。
IIndexMapping 接口并不需要返回所有字段的類型,你只需要配置那些會用于篩選、排序的 json/正則字段以及標(biāo)簽字段。當(dāng)使用 Talog 的擴(kuò)展方法查詢?nèi)罩緯r,Talog 無法獲取對應(yīng)字段的類型,則會拋出異常,異常信息大致為:index{index name}未配置{字段}的數(shù)據(jù)類型
性能提醒
V.Talog.Extension 擴(kuò)展包雖然為 Talog 帶來了更靈活的查詢能力,但日志的解析、篩選、排序都是需要實時計算的,并沒有任何預(yù)先處理,因此可以想象性能是難以得到保障的,特別是隨著日志量的增加,效率會明顯下降。
以我自己線上的 nginx 日志為例,目前已積累了 30W+ 條日志。

僅通過標(biāo)簽查詢時,耗時為 400ms,雖然不快,但也不慢。

但是當(dāng)我使用到了正則字段查詢時,耗時變成了 13s,可以看到查詢效率變得特別低,這個就是因為需要對 30W+ 條日志進(jìn)行實時解析、對比帶來了高額的時間成本。這個例子也算是一個使用 Talog 的反面教材吧,因為這 30W+ 條日志是放在同一個 Bucket 下的,如果能打上更合理的標(biāo)簽,應(yīng)該不會出現(xiàn)這樣的情況。

上一篇文章為大家展示了標(biāo)簽過于細(xì)致化,導(dǎo)致產(chǎn)生了大量的 Bucket 所帶來的性能問題,本文又為大家展示了標(biāo)簽過于籠統(tǒng),導(dǎo)致一個 Bucket 存放過多日志所帶來的性能問題,這兩個例子都說明了只有合理地為日志打上標(biāo)簽才能夠發(fā)揮出 Talog 的性能。
最后
到此為止,Talog 的核心原理基本就介紹完畢了,下一篇文章會為大家介紹一下基于 Talog 開發(fā)的應(yīng)用 V.Talog.Server。