Logtail檔案日誌採集之完整正則模式

抱澤發表於2018-12-17

前言

為了簡化檔案日誌的採集過程,Logtail 提供了按行採集的極簡模式:通過換行符來切分日誌,每行作為一條日誌。極簡模式具有高效、配置簡單等優勢,但它將整條日誌的內容作為整體,而不會對單條日誌的內容進行額外解析,在有些場景下無法滿足需求。為此,我們還提供了其他解析方式,例如:分隔符模式、完整正則模式、JSON 模式等。

本文將介紹如何使用完整正則模式來實現對日誌的解析,並且介紹一些使用此模式時的最佳實踐。

完整正則模式

簡介

完整正則模式是通過正規表示式實現的日誌解析。正規表示式是用於匹配字串中字元組合的模式,通俗來講,我們可以通過它來表達我們要什麼樣的日誌。正規表示式具有多個規範,包括 Posix、Perl 等,Logtail 的完整正則模式所支援的語法符合 Perl 正則(PCRE)規範(本文後續內容中所涉及的正規表示式都會採用該規範編寫)。

undefined

如上圖所示,相比極簡模式,完整正則模式增加了以下幾個功能:

  • 採集多行日誌

    • 極簡模式僅通過換行符來切分日誌,所以它無法採集多行日誌(比如 Java 程式的異常堆疊)。
    • 完整正則模式引入了行首正則,補足了採集多行日誌的需求。
  • 提取欄位

    • 極簡模式不對日誌內容進行任何解析,每條日誌都作為一個整體被髮送到服務端。
    • 完整正則模式通過為日誌設定用於匹配的正規表示式,利用括號語法來提取日誌的區域性內容作為欄位值。
  • 指定日誌時間

    • 極簡模式只能使用採集時的系統時間作為每條日誌所關聯的時間。
    • 完整正則模式可以通過指定提取得到的某個欄位以及一個關聯的時間格式,從欄位值中解析得到時間戳,作為該條日誌的時間。

接下來,我們將通過實際的操作去幫助您更好地理解如何使用完整正則模式的這三個功能。

準備工作

建議您可以先行到頁面開通日誌服務,建立必要的 project 和 logstore,這樣您可以跟著本文的後續內容一起操作,加深您對完整正則模式的各個選項的理解。

我們為每個使用者都提供了每月的免費額度,簡單的試用不會給您帶來花費,不必擔心~

在您首次完成新的 project 和 logstore 的建立後,您將會進入到資料嚮導頁面,如下圖所示:

undefined

如果您不小心退出了這個頁面(或者非首次建立),也可以通過如下的步驟進入:

  • 登入日誌服務控制檯,點選您所建立的 project;
  • 在進入的 logstore 列表,找到您所建立的 logstore,點選對應的資料接入嚮導圖示,進入資料嚮導頁面

進入資料嚮導頁面後,請滑動到頁面的最下端,在自定義資料中選擇文字檔案,如下圖所示:

undefined

在點選進入的頁面中,選擇完整正則模式,您會看到如下的介面:

至此,我們的準備工作已經完成,接下來我們將依次為您介紹完整正則模式的三個功能。

單行/多行日誌

一般來說,日誌檔案都是單行日誌,比如 Nginx 日誌、Apache 日誌等,示例如下:

127.0.0.1 - - [10/Sep/2018:12:36:49 +0800] "GET /index.html HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"
127.0.0.1 - - [10/Sep/2018:12:36:50 +0800] "GET /favicon.ico HTTP/1.1" 404 571 "http://127.0.0.1:8080/index.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"

但同樣也存在著多行日誌的情況,比如使用日誌庫所列印的 Java 的異常堆疊日誌等,示例如下:

[2018-10-01T10:30:01,000] [INFO] java.lang.Exception: exception happened
    at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
    at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
    at TestPrintStackTrace.main(TestPrintStackTrace.java:16)
[2018-10-01T10:30:31,000] [INFO] java.lang.Exception: another exception happened
    at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
    at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
    at TestPrintStackTrace.main(TestPrintStackTrace.java:16)

完整正則模式同時支援對以上兩種日誌的解析,接下來我們來看看如何進行配置。

單行配置

當切換到完整正則模式時,預設使用的是單行配置,您只需要將您的實際日誌貼上到日誌樣例文字框中即可,如下圖所示:

undefined

多行配置

對於多行日誌的配置,首先您要關閉單行模式,然後設定行首正規表示式。Logtail 引入行首正規表示式來區分兩條多行日誌之間該如何被切分,所以正確地設定行首正規表示式是切分您的多行日誌的關鍵。

以先前提及的 Java 異常堆疊日誌為例,每條日誌的開頭都會有時間日誌等級,而日誌的後續內容中一般沒有類似的內容,因此,我們可以根據這一點來設定我們的行首正規表示式(同樣地,您需要在日誌樣例中貼上您的實際日誌,最好兩條以上)。

自動生成行首正規表示式

為了簡化您的操作,我們為您提供了自動生成正規表示式的功能,您可以在貼上日誌後,點選自動生成,如下圖所示:

undefined

上圖示例中有幾點值得我們注意:

  • 我們關閉了單行模式的選項,這樣介面上才會出現行首正規表示式的輸入框。
  • 自動生成的行首正規表示式 ([d+-d+-w+:d+:d+,d+]s[w+]s.*) 正確包含了時間和日誌等級兩部分:

    • [d+-d+-w+:d+:d+,d+] 表示時間部分。
    • [w+]s 表示日誌等級部分以及其之後的空白符。
    • 兩者中間的 s 表示它們之間的空白符。
    • 末尾的 .* 是固定內容,一定要填寫。
  • 注意: 自動生成的行首正則並不一定完全可用,建議您進行一定的調整和優化。

手動調整行首正規表示式

自動生成行首正規表示式的功能很方便,但有時候它所生成的內容並不一定能夠滿足您的需求,您可以點選手動輸入正規表示式,在自動生成的基礎上,進行修改。當您進入手動輸入正規表示式的狀態時,頁面上將出現一個驗證按鈕,通過它您可以驗證當前輸入的行首正規表示式能夠從日誌樣例中匹配出多少行日誌,方便您就地進行除錯。

如下圖所示,我們對自動生成的行首正則的第二部分(日誌等級)進行了錯誤修改(去掉了 + 號),然後再點選驗證就會發現日誌的匹配數目變為了 0。

undefined

提取欄位

在設定了單行或者多行及其行首正規表示式後,我們能夠將原始檔案中的內容切分為一條條的日誌。如果每條日誌都符合某個模式且能夠使用相同的正規表示式來進行匹配時,我們就可以將每條日誌中的區域性內容提取出來,將日誌轉換為由鍵值對組成。

上述的這個過程我們稱之為提取欄位,在預設情況下,完整正則模式也只會將每條日誌作為一個整體傳送到服務端。為了開啟此功能,您需要在頁面上開啟提取欄位選項,如下圖所示:

undefined

同樣地,為了方便您的使用,您也可以使用自動生成的方式來為各個欄位生成對應的正規表示式。在您開啟了提取欄位的選項後,日誌樣例文字框將變成一個可選擇的區域,您可以在上面選中要提取的內容,隨後點選彈出的正則按鈕為選中的部分生成正規表示式。動圖示例如下:

接下來我們使用自動生成來為先前的單行和多行示例提取欄位。

單行配置

undefined

如上圖所示:

  • 我們從日誌樣例文字框中提取了 6 個欄位,分別將它們的 key 命名為 ip、time、query、status_code、length、user_agent,這樣該日誌在採集到日誌服務時,就會是一個具有 6 個欄位(除特殊欄位外)的日誌。
  • 在正規表示式的輸入框下,我們能看到 6 個使用 + 號連線的區域,它們分別包含了提取這 6 個欄位所使用的正規表示式,其中使用 () 包裹的部分就是匹配對應欄位的值時所使用的子模式。回想一下,我們之前也提到了提取欄位是利用了正規表示式的括號語法實現的。

多行配置

undefined

如上圖所示,整個生成的過程基本類似,但需要注意一下最後一個欄位(message)。在生成時,因為我們希望最後一個欄位包含剩餘的所有內容,我們跨越了多行去選擇日誌內容。該欄位對應的正則(僅括號內)為 ([^:]+:sw+sw+s[^:]+:S+s[^:]+:S+sS+)。事實上,這個正則是錯誤的(下文中我們會對此進行驗證):

  • 它無法完整地包含日誌的剩餘部分,最後一個 at 後的內容都沒有被包含,這個從截圖中可以看到。
  • 另外,該正規表示式中有三個冒號(:),因此,如果某個異常日誌只有兩個冒號時,就無法匹配了。

這也體現了自動生成正規表示式的侷限性,對此,您可以通過手動輸入正規表示式來進行一些修改,比如將最後一項直接修改為 ([Ss]+) 這樣一個正規表示式即可將剩餘的內容都包括到該欄位中(包括換行)。

指定日誌時間(可選項)

在日誌服務中,每條日誌都必須包括該日誌發生的時間戳資訊。預設情況下,Logtail 會使用該日誌的採集時間作為它的日誌時間(即啟用使用系統時間選項),但經過欄位提取後,如果您的欄位中有表示日誌時間資訊的欄位,您可以將該欄位的名字指定為 time,然後為其配置時間格式,進而 Logtail 會將該欄位的值解析為時間戳,然後關聯給對應的日誌。

Logtail 的時間格式解析使用的是 UNIX strptime,具體可參考文件配置時間格式

此功能於單行/多行日誌沒有區別,這裡我們就統一介紹下操作過程:

  1. 將帶有日誌時間資訊的欄位名字指定為 time,這個我們在先前的配置中都已操作。
  2. 關閉使用系統時間選項,在出現的時間轉換格式文字框中填寫時間格式。

類似地,我們也提供了自動生成的功能來簡化您的使用,當然,您還是可以通過手動輸入來進行自定義的修改。我們示例中的單行和多行日誌所生成的時間格式分別如下:

  • 單行日誌
    undefined
  • 多行日誌
    undefined

至此,我們已經對完整正則模式的三個功能(單行/多行日誌採集、提取欄位、指定日誌時間)進行了依此的介紹,並給出了在控制檯操作它們的示例。

最佳實踐

1. 如何除錯正規表示式?

如果您希望對您所在日誌服務控制檯所設定的正規表示式進行除錯,您可以直接在介面上使用驗證按鈕所提供的功能來進行檢查:

  • 對於行首正規表示式,檢查一下當前設定能否正確匹配出您期望的日誌數量。
  • 對於提取欄位,檢查一下各個欄位中的值是否是您所希望的。

進一步地,如果您希望進行更多的驗證乃至除錯正規表示式,您可以利用諸如 Regex101RegexTester 之類的線上工具,將控制檯為您自動生成的正規表示式拷貝貼上到這些工具上,然後填充您的實際日誌來進行檢查、除錯。

在之前的提取欄位的示例中,我們有提到過自動生成功能為多行日誌的 message 欄位生成了不合適的正則。在此,我們以 Regex101 為例,對該正則進行一下檢查:

  1. 首先,我們拷貝自動生成的完整正則至 Regex101:[([^]]+)]s[(w+)]s([^:]+:sw+sw+s[^:]+:S+s[^:]+:S+sS+).*。在介面的右側,您還可以看到該正則的含義。
    undefined
  2. 然後,我們向其中貼入我們在日誌樣例中的日誌。
    undefined

匹配,但是 at 之後的內容並沒有被包含到 message 欄位中(注意顏色,橘色和藍色),這也就是我們之前所說的這個正規表示式是錯誤的原因之一。

  1. 接著我們來驗證一下另一個錯誤:如果日誌中只有兩個冒號的情況。
    undefined

匹配失敗。

  1. 最後,我們看看按照之前所說的,將最後一個正規表示式替換為 [Ss]+ 再看看。
    at 後面的內容:
    undefined
    只有兩個冒號的日誌:
    undefined

類似地,您也可以按照如上的方法來對您的正規表示式進行除錯、修改,最終應用於控制檯。

2. 日誌裡包含多種格式怎麼辦?

完整正則模式要求日誌必須採用統一的格式,但有些時候日誌中可能會包含多種格式的日誌,如何來處理這種情況呢?

[2018-10-01T10:30:31,000] [WARNING] java.lang.Exception: another exception happened
    at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
    at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
    at TestPrintStackTrace.main(TestPrintStackTrace.java:16)
[2018-10-01T10:30:32,000] [INFO] info something
[2018-10-01T10:30:33,000] [DEBUG] key:value key2:value2

以上面的 Java 日誌為例,作為一個程式日誌,它一般既包含正常資訊,也會包含一些錯誤資訊(比如異常棧等):

  • WARNING 型別的多行日誌
  • INFO 型別的簡單文字日誌
  • DEBUG 型別的鍵值日誌

對此,有兩種方案可以考慮:

  • Schema-On-Write:為同樣的一份日誌應用多個採集配置,每個採集配置具有不同的正則配置,從而能夠正確地實現欄位提取。注:Logtail 不支援對同一個檔案同時應用多個採集配置,您需要為該檔案所在的目錄建立多個軟連結,每個配置作用於不同的軟連結目錄來間接實現多個配置同時採集一個檔案。
  • Schema-On-Read:使用它們共同的正規表示式來採集。比如說採用多行日誌採集,將時間和日誌等級作為行首正則,剩餘的部分都作為 message。如果希望進一步分析 message 的話,您可以為該欄位建立索引,然後利用日誌服務的查詢分析功能,比如使用正則提取來從 message 欄位提取需要的內容,基於該內容進行分析。此方案比較受限,僅推薦作用在同時分析的日誌數量較小的場景下(比如千萬級)。

3. 正規表示式的效能優化

如果您非常地關注採集的效能,那您可以多花一些功夫在提高正規表示式的效能上。這裡有一些建議可供您參考:

  1. 使用更為精確地字元,不要盲目地使用 .* 來匹配欄位,這個表示式包含了很大的搜尋空間,很容易就發生了誤匹配或者導致匹配效能下降。比如您要提取的欄位只由字母組成,那麼使用 [A-Za-z] 即可。
  2. 使用正確的量詞,而不是盲目地使用 +, *。比如您使用 d 來匹配 IP 地址,那麼相比 d+d{1,3} 可能會具有更高的價效比。
  3. 除錯。類似於排查錯誤,您同樣可以在 Regex101 上對您的正規表示式所花費的時間進行除錯,一旦發現大量的回溯,及時地優化它。

4. 時間格式配置技巧

日誌服務的時間戳只支援到秒,所以時間格式只需配置到秒,無需配置毫秒、微秒等資訊。
只需time欄位中前面能解析出時間的部分即可,後面無需配置
常見日誌格式配置示例如下:

自定義1 2017-12-11 15:05:07
%Y-%m-%d %H:%M:%S
自定義2 [2017-12-11 15:05:07.012]
[%Y-%m-%d %H:%M:%S
RFC822     02 Jan 06 15:04 MST
%d %b %y %H:%M
RFC822Z    02 Jan 06 15:04 -0700
%d %b %y %H:%M
RFC850      Monday, 02-Jan-06 15:04:05 MST
%A, %d-%b-%y %H:%M:%S
RFC1123     Mon, 02 Jan 2006 15:04:05 MST
%A, %d-%b-%y %H:%M:%S
RFC3339     2006-01-02T15:04:05Z07:00
%Y-%m-%dT%H:%M:%S
RFC3339Nano 2006-01-02T15:04:05.999999999Z07:00
%Y-%m-%dT%H:%M:%S

更多閱讀

  1. Logtail從入門到精通(四):正規表示式Java日誌採集實戰
  2. Python 日誌收集
  3. 線上正規表示式分析
  4. 加入分析交流群


相關文章