Fluentd 作為一款開源的日誌收集器,在日誌管理領域扮演著重要角色。其靈活的組態和豐富的外掛生態,使得它能夠有效地處理各種日誌資料。本文將重點介紹如何利用 Fluentd 捕捉、解析和輸出日誌事件,並探討正規表示式(Regex)解析、輸出外掛應用、緩衝機制以及相關最佳實踐。在實際應用中,我們經常需要從非結構化的日誌資料中提取有用的資訊。Fluentd 的 Regex 解析器提供了一個強大的工具,可以根據自定義的模式匹配和提取日誌中的特定內容。同時,Fluentd 支援多種輸出外掛,可以將處理後的日誌資料輸出到不同的目標系統,例如檔案、資料函式庫和訊息佇列等。為了提高系統的吞吐量和穩定性,Fluentd 引入了緩衝機制,可以暫時儲存日誌事件,並在適當的時機批次輸出。

使用 Fluentd 捕捉日誌事件

SYSLOG

Syslog 是由作業系統和基礎設施程式產生的,但應用程式也可以使用這種格式。Syslog 條目的原始非官方結構已被 IETF 正式化為 RFC 3164(https://tools.ietf.org/html/rfc3164)。後來在 2009 年被 RFC 5424(https://tools.ietf.org/html/rfc5424)取代。由於一些硬體可能需要多年時間才會被替換,因此 Fluentd 可以處理兩種標準。預設情況下,Fluentd 將採用原始標準,但您可以透過設定 message_format 屬性(rfc3164、rfc5424、auto)來告訴 Fluentd 使用後來的標準或使用負載來處理。如果您知道將處理哪種格式,最好在組態中明確定義。這樣可以消除在解析之前評估每個事件的開銷。如果單個端點正在消耗兩種型別的事件,您可能不得不接受這個開銷。

該外掛有兩種不同的演算法用於處理事件——字串處理邏輯和正規表示式(regexp)。目前,預設是 regexp,但未來將更改為預設使用 string 選項,這比 regexp 演算法更快。如果您希望強制使用演算法,則需要使用 regexp 或 string 設定 parser_type 屬性。

第三方解析器

除了核心解析器之外,還有一些第三方解析器。以下是可能的選項的子集。然而,這些要麼是經過認證的,要麼是被大量下載的,因此它們很可能受益於廣泛的使用,並且由於程式碼是開源的,受益於 Linus 法則(給定足夠的關注,所有錯誤都是淺顯的——Eric Raymond)。

多格式解析器外掛用於 Fluentd

這嘗試使用定義順序中的不同格式模式來獲得匹配。它可以從 http://mng.bz/wnoO 獲得。

Grok 解析器用於 Fluentd

這使用根據 Grok 的方法從日誌條目中提取詳細資訊。它包括多行支援。它可以從 https://github.com/fluent/fluent-plugin-grok-parser 獲得。使用 Grok 解析器的好處是,Grok 被 Logstash 用作過濾和解析機制;因此,利用 Grok 預定義模式並在 Logstash 和 Fluentd 之間切換是一個相對較小的步驟。

將 Regex 解析器應用於複雜日誌

如前所述,Regex 或正規表示式解析器可能是最強大但最難使用的。在大多數 Regex 應用中,結果通常是單個結果、子字串或字串出現次數的計數。然而,當涉及到 Fluentd 時,我們需要 regex 生成多個值,例如設定日誌事件時間,將負載分解為 JSON 事件主體中的第一級元素。讓我們分解一個 Regex 表示式來突出基礎知識。但在這之前,我們需要使用一個真實的日誌條目。讓我們執行日誌模擬器組態,使用命令:

groovy LogSimulator.groovy Chapter3/SimulatorConfig/jul-log-file2.properties ./TestData/medium-source.txt

注意這與上一個示例的組態略有不同,因此我們可以在 Fluentd 中看到幾種可能性。檢視生成的輸出檔案(仍然是 structuredrolling-log.0.log),現在傳送的有效負載顯示為:

2020-04-30--20:10:52 INFO com.demo (6-1) {"log":"A clean house is the sign of a broken computer"}

在此輸出行中,我們可以看到應用的時間戳記,準確到秒,然後是一個空格,後面跟著日誌級別,再後面是包名,後面跟著編號方案,如前所述。最後,核心日誌條目被包裝為 JSON 結構。當我們解析訊息時,我們需要去掉那個 JSON 表示法,因為它不應該在我們想要的 JSON 結構中。

目標是在 Fluentd 中結束以下結構,以便我們可以使用這些值進行未來的操作:

{
  "level": "INFO",
  "class": "com.demo",
  "line": 33,
  "iteration": 1,
  "msg": "What is an astronauts favorite place on a computer? The Space bar!"
}

以下是正規表示式,在其下方增加了字元位置,以便精確參照每個部分:

(?<time>\S+)\s(?<level>[A-Z]*)\s*(?<class>\S+)[^\d]*(?<line>[\d]*)\-(?<iteration>[\d]*)\)[\s]+\{"log":"(?<msg>.*(?="\}))
1234567890123456789012345678901234567890123456789012345678901234567
0 1 2 3 4 5 6
89012345678901234567890123456789012345678901234567890123
7 8 9 10 11 12

作為表示式的一部分,我們需要使用 Regex 的能力來定義文字組。組的範圍由開閉括號定義(例如,字元 1 和 12)。要將一些源文字分配給 JSON 元素,我們需要使用 ?<name>,其中名稱是要出現在 JSON 中的元素名稱。在我們的例子中,它應該是 level、class、line、iteration 和 msg 的值。除此之外,我們還需要捕捉日誌事件時間與預設時間值。這可以在字元 2 和 8 之間看到,例如,在字元 16 和 23 之間。緊接著這之後,我們可以使用 Regex 表示法來描述要捕捉的文字。為此,我們使用 \S(字元 9 和 10),它表示非空白字元;透過新增 +(字元 11),我們宣告這應該發生一次或多次。

內容解密:

  1. 正規表示式的組成:表示式 (?<time>\S+)\s(?<level>[A-Z]*)\s*(?<class>\S+)[^\d]*(?<line>[\d]*)\-(?<iteration>[\d]*)\)[\s]+\{"log":"(?<msg>.*(?="\})) 用於捕捉日誌事件的不同部分,包括時間、日誌級別、類別名、行號、迭代次數和訊息。
  2. 命名捕捉組:表示式中使用了命名捕捉組(如 (?<time>\S+)),以便將匹配的文字直接對映到 JSON 結構中的相應欄位。
  3. 字元匹配\S+ 用於匹配一個或多個非空白字元,而 [A-Z]* 用於匹配零個或多個大寫字母。同樣,\d* 用於匹配零個或多個數字。
  4. 日誌結構解析:透過這個正規表示式,可以將具有特定結構的日誌條目解析為 JSON 結構,從而方便進一步處理和分析。
  5. 適用場景:這種技術特別適用於處理具有複雜或非標準格式的日誌檔案,能夠有效地抽取有價值的資訊。

使用 Fluentd 捕捉日誌事件的正規表示式解析

在處理日誌事件時,Fluentd 提供了多種解析器(parser)來幫助我們從日誌中提取有用的資訊。其中,正規表示式(Regular Expression)解析器是一種非常強大且靈活的工具,可以用來匹配和提取日誌中的特定模式。

正規表示式的應用

假設我們有以下格式的日誌事件:

2023-04-01--12:00:00 INFO  com.example.ClassName 123-456 {"log":"This is a log message"}

我們可以使用正規表示式來解析這個日誌事件,提取出時間、日誌級別、類別名稱、行號、迭代次數和日誌訊息等資訊。

正規表示式的組成

我們的正規表示式如下:

/(?<time>\S+)\s(?<level>[A-Z]*)\s*(?<class>\S+)[^\d]*(?<line>[\d]*)\-(?<iteration>[\d]*)\)[\s]+\{"log":"(?<msg>.*(?="\}))/

讓我們逐步解析這個正規表示式:

  1. (?<time>\S+):匹配時間戳,使用 \S+ 匹配一個或多個非空白字元。
  2. \s:匹配一個空白字元。
  3. (?<level>[A-Z]*):匹配日誌級別,使用 [A-Z]* 匹配零個或多個大寫字母。
  4. \s*:匹配零個或多個空白字元。
  5. (?<class>\S+):匹配類別名稱,使用 \S+ 匹配一個或多個非空白字元。
  6. [^\d]*:匹配零個或多個非數字字元。
  7. (?<line>[\d]*):匹配行號,使用 [\d]* 匹配零個或多個數字。
  8. -:匹配一個連字號。
  9. (?<iteration>[\d]*):匹配迭代次數,使用 [\d]* 匹配零個或多個數字。
  10. \):匹配一個右括號。
  11. [\s]+:匹配一個或多個空白字元。
  12. \{"log":":匹配字串 "log":"
  13. (?<msg>.*(?="\})):匹配日誌訊息,使用 .* 匹配任意字元,直到遇到 "} 為止。

組態 Fluentd

要在 Fluentd 中使用這個正規表示式,我們需要在組態檔案中定義一個 regexp 型別的解析器:

<parse>
  @type regexp
  Expression /(?<time>\S+)\s(?<level>[A-Z]*)\s*(?<class>\S+)[^\d]*(?<line>[\d]*)\-(?<iteration>[\d]*)\)[\s]+\{"log":"(?<msg>.*(?="\}))/
  time_format %Y-%m-%d--%T
  time_key time
</parse>

這個組態告訴 Fluentd 使用正規表示式解析器,並定義了時間戳的格式和鍵名。

結果

當 Fluentd 處理日誌事件時,它將使用正規表示式解析器提取出相關資訊,並輸出到控制檯。輸出的結果將是一個 JSON 物件,包含提取出的資訊:

2023-04-01 12:00:00.000000000 +0800 tag: {"level":"INFO","class":"com.example.ClassName","line":"123","iteration":"456","msg":"This is a log message"}

程式碼解密:

  1. (?<time>\S+):此部分用於捕捉時間戳,透過 \S+ 確保至少捕捉一個非空白字元,從而正確提取時間資訊。
  2. (?<level>[A-Z]*):此部分負責捕捉日誌級別,利用 [A-Z]* 可以匹配到由大寫字母組成的級別名稱,如 “INFO” 或 “ERROR”。
  3. (?<class>\S+):此段正規表示式用於捕捉類別名稱,透過 \S+ 可以完整提取出類別名稱,無論其包含多少字元。
  4. (?<line>[\d]*)(?<iteration>[\d]*):這兩部分分別用於捕捉行號和迭代次數,利用 [\d]* 可以提取數字資訊,即使是空值也能妥善處理。
  5. (?<msg>.*(?="\})):此部分負責捕捉日誌訊息主體,透過 .* 可以匹配任意字元,直到遇到指定終止符 "},確保訊息內容被完整提取。

使用 Fluentd 捕捉日誌事件的進階技巧

正規表示式(Regex)在日誌解析中的應用與挑戰

在處理多樣化的日誌格式時,Fluentd 的正規表示式解析功能顯得尤為重要。正確地定義 Regex 模式能夠有效地從非結構化的日誌中提取有價值的資訊。然而,編寫有效的 Regex 模式可能相當具有挑戰性。

Regex 模式驗證工具

為瞭解決這一問題,Fluentd 提供了 UI 組態介面來驗證 Regex 表示式。此外,還有許多第三方工具可供使用,例如:

這些工具可以幫助開發者更直觀地理解和除錯 Regex 模式,從而提高日誌解析的效率和準確性。

在 Fluentd 中應用自定義的 Regex 解析器

以下是一個具體的例子,展示瞭如何在 Fluentd 中使用自定義的 Regex 解析器來結構化日誌事件:

<parse>
  @type regexp
  Expression /(?<time>\S+)\s(?<level>[A-Z]*)\s*(?<class>\S+)[^\d]*(?<iteration>[\d]*)\-(?<line>[\d]*)\][\s]+\{"event":"(?<msg>.*(?="\,))/
  time_format %Y-%m-%d--%T
  time_key time
  keep_time_key true
  types line:integer,iteration:integer
</parse>

程式碼解析

  1. @type regexp:指定使用 Regex 解析器。
  2. Expression:定義用於匹配日誌事件的 Regex 模式。在這個例子中,它提取了時間戳(time)、日誌級別(level)、類別(class)、迭代次數(iteration)、行號(line)以及事件訊息(msg)等資訊。
  3. time_format %Y-%m-%d--%T:指定時間戳的格式。
  4. time_key time:指定包含時間戳的鍵名。
  5. keep_time_key true:將原始時間戳保留在 JSON 載荷中。
  6. types line:integer,iteration:integer:明確指定某些欄位的資料型別,這裡將 lineiteration 定義為整數型別。

透過這種方式,Fluentd 能夠更準確地解析和結構化原本非結構化的日誌資料,為後續的分析和處理提供了便利。

練習與實踐

為了更好地掌握 Fluentd 的日誌解析功能,建議進行以下練習:

  1. 複製提供的 Fluentd 組態檔案,並修改其中的 Regex 表示式,以正確解析目標日誌格式。
  2. 使用提供的日誌模擬器組態檔案執行模擬日誌生成,並觀察 Fluentd 的解析結果。
  3. 利用上述的 Regex 驗證工具最佳化你的 Regex 表示式。

透過實踐,你將能夠更深入地理解 Fluentd 的日誌解析機制,並掌握如何根據具體需求調整和最佳化組態。

使用 Fluentd 輸出日誌事件

在第三章中,我們已經展示瞭如何捕捉日誌事件以及如何使用解析器等輔助外掛。但是,捕捉資料只有在我們能夠對其進行有意義的操作時才具有價值,例如將資料傳遞到端點並格式化,以便日誌事件可以被使用——例如,將事件儲存在日誌分析引擎中或向維運(Ops)團隊傳送訊息以進行調查。本章將展示 Fluentd 如何實作這一點。我們將探討如何使用 Fluentd 輸出外掛將日誌事件寫入檔案,以及 Fluentd 如何與 MongoDB 和 Slack 等協作/社交工具配合使用,以實作快速通知。

本章涵蓋以下內容:

  • 使用輸出外掛將日誌事件寫入檔案、MongoDB 和 Slack
  • 應用不同的緩衝選項與 Fluentd
  • 評估緩衝的好處
  • 處理緩衝區過載和其他緩衝風險
  • 新增格式化程式來結構化日誌事件

檔案輸出外掛

與 tail(檔案輸入)外掛相比,我們較少使用檔案輸出外掛,因為通常我們希望將輸出傳遞到允許查詢、分析和視覺化事件的工具。當然,在生產環境中,檔案輸出有其合理的使用場景。然而,它是逐步過渡到更先進工具的最佳選擇之一,因為它易於檢視結果和各種外掛(如解析器和過濾器)的影響。將重要事件記錄到檔案中也便於將來需要時存檔日誌事件(例如,稽核日誌事件以支援法律要求)。因此,我們將首先介紹檔案輸出,然後再轉向更複雜的輸出。

在使用檔案輸出(以及任何直接或間接寫入物理儲存的輸出)時,我們需要考慮幾個因素:

  • 檔案系統中的寫入位置受儲存容量和許可權的限制
  • 該位置是否具有足夠的容量(既包括分配的容量,也包括物理容量)
  • 物理硬體能夠提供的 I/O 吞吐量是多少
  • 資料存取是否存在延遲(NAS 和 SAN 裝置透過網路存取)

雖然基礎設施效能不太可能影響開發工作,但在預生產(例如,效能測試環境)和生產環境中卻極為重要。值得注意的是,裝置效能對於檔案外掛至關重要。其他輸出外掛可能會使用包含最佳化 I/O 邏輯的服務(例如,資料函式庫快取、分配的檔案空間最佳化)。

使用輸出外掛時,我們可能已經整合了多個日誌事件源。因此,我們最終可能會得到一個組態,讓 Fluentd 將所有輸入寫入一個檔案或位置。物理效能問題可以透過使用緩衝(我們很快就會看到)和快取來緩解。

基本檔案輸出

讓我們從一個相對基本的檔案輸出組態開始。在之前的所有章節示例中,我們只是將內容寫入控制檯。現在,我們不再寫入控制檯,而是將所有內容寫入檔案。要做到這一點,我們需要在組態中新增一個新的匹配指令,但我們將繼續使用檔案源來生成日誌事件。

為了說明輸出外掛可以處理組態中的多個輸入,除了日誌檔案源之外,我們還包含了上一章中所示的自我監控源組態。要控制 Fluentd 自我監控生成的日誌事件的頻率,我們可以定義另一個屬性 emit_interval,它接受一個持續時間值——例如,10 秒。emit_interval 提供的值是 Fluentd 生成日誌事件之間的時間間隔。自我監控可以包括諸如已處理的事件數量、管理的 worker 程式數量等詳細資訊。

檔案輸出外掛至少需要定義 type 屬性和指向輸出位置的 path 屬性。在下面的列表中,我們可以看到 Chapter4/Fluentd/rotating-file-read-file-out.conf 檔案的相關部分。這個組態的結果可能會讓你感到驚訝,但讓我們看看會發生什麼。

<source>
  @type monitor_agent
  bind 0.0.0.0
  port 24220
  @id in_monitor_agent
  include_config true
  tag self
  emit_interval 10s
</source>

<match *>
  @type file
  path ./Chapter4/fluentd-file-output
</match>

如果使用以下命令執行 LogSimulator 和 Fluentd,則可以看到使用這個新的匹配指令的結果:

  • fluentd -c ./Chapter4/Fluentd/rotating-file-read-file-out.conf
  • groovy LogSimulator.groovy ./Chapter4/SimulatorConfig/jul-log-output2.properties ./TestData/medium-source.txt

簡單的預測是所有內容都被寫入名為 fluentd-file-output 的檔案中。然而,實際發生的情況是建立了一個資料夾,使用路徑的最後一部分作為其名稱(即 fluentd-file-output),並且在該資料夾中可以看到兩個檔案。該檔案將以半隨機名稱出現(以區分不同的緩衝檔案),並且具有相同基本名稱的後設資料檔案。Fluentd 所做的是隱式地使用了緩衝機制。採用預設選項的緩衝在輸出外掛中並不罕見;一些外掛放棄使用緩衝——例如,stdout 外掛。

緩衝基礎

正如您可能從第一章中回憶的那樣,緩衝是 Fluentd 的輔助外掛。輸出外掛需要了解它們可能對 I/O 效能產生的影響。因此,大多數輸出外掛使用可以同步或非同步執行的緩衝外掛。

緩衝內容解密:

  1. 緩衝的作用:緩衝是用於臨時儲存資料的區域,用於平衡生產者和消費者之間的資料處理速度差異。
  2. 同步與非同步緩衝:同步緩衝是指資料寫入緩衝區後立即被處理,而非同步緩衝則允許資料在緩衝區中累積後再被處理。
  3. Fluentd 中的緩衝:Fluentd 使用緩衝外掛來最佳化 I/O 操作,大多數輸出外掛都支援緩衝,以提高效能和可靠性。

透過這種方式,Fluentd 能夠有效地管理和輸出日誌事件,從而滿足不同的應用需求和場景。