Fluentd 作為一款開源的日誌收集器,在日誌管理和分析中扮演著重要角色。本文將探討 Fluentd 的核心功能,包括如何根據日期組織日誌檔案、排除特設定檔案、專注於最近修改的檔案以及處理日誌輪替。此外,文章還將介紹 Fluentd 的自我監控功能,以及如何使用 HTTP 介面檢查其執行狀態。最後,文章將詳細介紹 Fluentd 的各種解析器,例如 Apache、Nginx、CSV、JSON、多行解析器和正規表示式解析器,以及如何使用這些解析器來處理不同格式的日誌資料,並提供實用的組態示例和程式碼說明。這些技巧可以幫助開發者和系統管理員更好地利用 Fluentd 進行日誌管理和分析,提高系統的可觀測性和穩定性。

使用 Fluentd 捕捉日誌事件

按日期資料夾進行日誌排序

某些解決方案允許您組態日誌記錄,以便資料夾包含特定時間段內的所有日誌(例如某一天、某個月等)。這使得管理多個涵蓋長時間的日誌更加容易。Fluentd 能夠使用日期元素處理這種檔案路徑結構(例如 Chapter3/2020/08/30/app.log)。要實作這種效果,我們需要修改組態,使其類別似於:

path Chapter3/%Y/%m/%d/*.log

在附錄 B 的“表達日期和時間”一節中,有一個表格描述了不同的轉義序列,例如 %Y%m%d,如前面的例子所示。

使用檔案排除

另一種方法是透過提供一個不應被考慮的檔案列表來排除檔案,使用 exclude_path 屬性。這個屬性與 path 屬性類別似,因此它可以使用萬用字元或逗號分隔的列表。例如:

exclude_path: ./Chapter3/*.zip, ./Chapter3/threadDump*.txt

此宣告將阻止任何具有 .zip 或執行緒轉儲檔案從該資料夾中被收集。這是一種降低意外捕捉不應被捕捉的檔案的風險的好方法,例如,如果應用程式的日誌記錄也可能在與正常日誌相同的檔案位置生成堆積疊轉儲檔案或要傳送給供應商進行分析的檔案。

只考慮最近更改的檔案

我們可以告訴外掛只考慮在一定時間範圍內更改的檔案。因此,我們可以假設在安裝或重新啟動後不久生成/更改的任何檔案都包含值得收集的日誌事件;這是透過設定 limit_recently_modified 屬性為一個時間間隔值來完成的。例如:

limit_recently_modified: 2m

limit_recently_modified 屬性是另一個時間資料型別的例項,因此可以按照剛才描述的方式進行組態。

程式碼範例

# 設定 Fluentd 的 tail 輸入外掛
<source>
  @type tail
  path /path/to/log/%Y/%m/%d/*.log
  exclude_path /path/to/log/*.zip, /path/to/log/threadDump*.txt
  limit_recently_modified 2m
  tag log.events
</source>

內容解密:

  1. @type tail 指定使用 Fluentd 的 tail 輸入外掛來監視日誌檔案的變化。
  2. path 指定了要監視的日誌檔案路徑,支援日期格式化的萬用字元。
  3. exclude_path 用於排除不需要監視的檔案,如 .zip 檔案和特定的執行緒轉儲檔案。
  4. limit_recently_modified 設定只處理最近 2 分鐘內被修改的檔案,以提高效率和減少無效日誌處理。
  5. tag 為處理的日誌事件增加了一個標籤,以便於後續的處理和過濾。

Fluentd 組態錯誤處理

Fluentd 在其組態中指出錯誤,通常以警告的形式在 Fluentd 輸出中發布,並附帶說明。例如,將 %B 新增到檔名中將導致 ./Chapter3/structured-rolling-logMay.0.log not found. Continuing without tailing it.,這是有道理的,因為該檔案不存在。

日誌輪替處理

日誌輪替是一種常見的解決方案,用於允許收集大量的日誌記錄,而不會使日誌檔案變得太大或無限制地佔用空間。Fluentd 的 tail 輸入外掛可以處理日誌輪替。預設情況下,透過明確指定輪替中的主要檔案(例如 path ./Chapter3/structured-rolling-log.0.log)來實作這一點。透過從包含日誌的資料夾中排除萬用字元,日誌輪替意味著當檔案被輪替時,它們不會被資料夾重新掃描捕捉。

使用 Fluentd 捕捉日誌事件

讀取滾動日誌檔案

當使用 Fluentd 的 tail 外掛讀取日誌檔案時,需要考慮日誌滾動(rotation)的情況。日誌滾動是指當日誌檔案達到一定大小或時間條件時,將當前日誌檔案重新命名並建立一個新的日誌檔案繼續寫入。為了確保在日誌滾動期間不會丟失任何日誌事件,Fluentd 提供了幾個重要的組態屬性。

rotate_wait 屬性

rotate_wait 屬性指定了在日誌檔案被旋轉後,Fluentd 繼續讀取該檔案的時間間隔。這是必要的,因為日誌寫入器可能尚未完成將內容重新整理到舊檔案中,就已經建立了新的檔案,從而可能導致錯過舊檔案的最後內容。因此,瞭解寫入器完成寫入所需的大致時間非常重要。如果這個時間未知,需要允許足夠的時間,以避免日誌生成的速度超過 Fluentd 的處理能力。

設定 rotate_wait 的建議

每個案例都不同,這取決於日誌寫入機制的工作方式。對於後端伺服器,我們通常將 30 秒視為一個合理的折衷方案。這是根據不想讓 Fluentd 落後於日誌太遠,因為趕上日誌事件可能會在活躍的伺服器上造成工作負載的峰值。如果我們遇到節點問題,很有可能我們已經捕捉了導致災難性事件之前的日誌事件。

組態範例

以下是一個 Fluentd 的組態範例(Listing 3.2),展示瞭如何設定 tail 外掛來讀取滾動日誌檔案:

<source>
  @type tail
  path ./Chapter3/structured-rolling-log.0.log
  rotate_wait 15s
  read_lines_limit 5
  tag simpleFile
  pos_file ./Chapter3/rotating-file-read.pos_file
  read_from_head true
  <parse>
    @type none
  </parse>
</source>

程式碼解析:

  1. @type tail:指定使用 tail 外掛來讀取日誌檔案。
  2. path ./Chapter3/structured-rolling-log.0.log:指定要讀取的日誌檔案路徑。
  3. rotate_wait 15s:設定在日誌檔案被旋轉後,Fluentd 繼續讀取該檔案的時間為 15 秒。
  4. read_lines_limit 5:限制每次讀取的日誌行數為 5 行。
  5. tag simpleFile:為讀取的日誌事件設定標籤 simpleFile
  6. pos_file ./Chapter3/rotating-file-read.pos_file:指定用於記錄日誌檔案讀取位置的檔案路徑。
  7. read_from_head true:設定從檔案的開頭開始讀取。
  8. <parse>@type none</parse>:指定不進行任何解析。

使用萬用字元的替代方法

如果需要在單個資料夾中使用萬用字元,有幾個額外的屬性可以用來控制行為:

  • refresh_interval:控制透過萬用字元檢測到的檔案列表的更新頻率。
  • limit_recently_modified:防止因為萬用字元而被選中的較舊的日誌檔案被使用,只要它們在定義的時間段內沒有被修改過。
  • pos_file_compaction_interval:定義了對位置跟蹤檔案進行整理的時間間隔,以減少冗餘條目。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Fluentd 日誌捕捉與處理技巧

package "正規表示式" {
    package "基本語法" {
        component [字元類 [abc]] as char_class
        component [量詞 * + ?] as quantifier
        component [錨點 ^ $] as anchor
    }

    package "進階功能" {
        component [群組 ()] as group
        component [後向參考 \1] as backref
        component [前瞻後顧] as lookahead
    }

    package "Python re 模組" {
        component [re.match()] as match
        component [re.search()] as search
        component [re.findall()] as findall
        component [re.sub()] as sub
    }
}

char_class --> quantifier : 重複匹配
quantifier --> anchor : 位置定位
group --> backref : 捕獲參考
match --> search : 模式搜尋
search --> findall : 全部匹配
findall --> sub : 取代操作

note right of lookahead
  (?=...) 正向前瞻
  (?!...) 負向前瞻
  (?<=...) 正向後顧
end note

@enduml

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

3.3 自我監控

在前面的章節中,我們討論了使用 Fluentd 處理日誌輪替(log rotation)的相關問題。儘管 Fluentd 提供了靈活的日誌處理機制,但在某些情況下仍有可能丟失日誌事件。幸運的是,Fluentd 像其他優秀的應用程式一樣,會記錄其自身的活動日誌。這意味著我們可以利用 Fluentd 來監控其自身的健康狀態,透過追蹤其日誌事件。

3.3.1 HTTP 介面檢查

Fluentd 提供了一個 HTTP 端點,可以用來取得有關例項組態的資訊。下面是一個組態示例:

<source>
  @type monitor_agent
  bind 0.0.0.0
  port 24220
</source>

使用提供的組態啟動 Fluentd(fluentd -c Chapter3/Fluentd/rotating-file-self-check.conf),然後啟動 Postman(就像第 2 章中一樣)。將地址組態為 0.0.0.0:24220/api/plugins.json

bind 屬性所示,與其他外掛一樣,這與主機的 DNS 或 IP 相關,而 port 屬性則與 URL 的埠部分相匹配。該介面可以描述為 {bind}:{port}/api/plugins.json。與第 2 章不同,這裡的操作需要設定為 GET。完成後,點選傳送按鈕,將看到傳回的執行組態的 HTTP 表示,如圖 3.1 所示。

圖 3.1 Postman 說明呼叫 Fluentd API 的結果

圖中突出顯示了 URL 和結果。如果您希望結果以標籤製表符分隔值(ltsv)表示,只需從 URL 中省略 .json。當輸出設定為 JSON 時,URL 還可以處理多個其他引數。例如,在 URL 後新增 ?debug=1 將產生一系列額外的狀態資訊(注意 debug 的值並不重要,重要的是引數的存在)。表 3.2 中描述了可用於呼叫 monitor_agent API 的完整引數集。

表 3.2 可用於呼叫 monitor_agent API 的 URI 引數

URI 引數描述示例
debug取得額外的外掛狀態資訊包含在回應中。引數的值無關緊要。?debug=0
with_ivars使用此引數足以使 instance_variables 屬性包含在回應中。?with_ivars=false
with_config覆寫預設或顯式設定 include_config。提供的值必須是小寫的 true;其他值被視為 false?with_config=true
with_retry覆寫預設或顯式設定 include_retry。提供的值必須是小寫的 truefalse;其他值被視為 false?with_retry=true
tag將傳回的組態過濾為僅傳回與提供的標籤名稱相關的指令。?tag=simpleFile
@id將回應過濾為特定的指令。如果組態沒有明確的 ID,則該值將是任意的。@id=in_monitor_agent
@type允許按外掛型別過濾結果。@type=tail

Fluentd 自我監控的優勢

透過在源指令中新增 tagemit_interval 屬性,我們可以定期取得外掛的基本狀態報告。下面是一個示例組態:

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

執行此組態後(使用 fluentd -c Chapter3/Fluentd/rotating-file-self-check2.conf),我們將開始每隔 10 秒看到一些狀態資訊,如下所示:

2020-05-01 17:10:04.041641100 +0100 self:
{"plugin_id":"in_monitor_agent","plugin_category":"input","type":"monitor_agent","output_plugin":false,"retry_count":null}

由於這些資訊是帶有標籤的,我們可以將這些流量引導到一個集中的監控點,從而節省了指令碼化 HTTP 輪詢的工作量。

程式碼解說

Fluentd 組態範例

# 以下是一個基本的 Fluentd 組態範例,用於監控其自身的健康狀態。
<source>
  @type monitor_agent
  bind 0.0.0.0
  port 24220
</source>

# 使用上述組態啟動 Fluentd,並透過 Postman 呼叫其 API 以取得執行組態的資訊。

#### 內容解密:

此範例展示瞭如何使用 Fluentd 的 monitor_agent 外掛來監控其自身的執行狀態。透過組態 HTTP 端點並使用 Postman 呼叫該端點,我們可以取得 Fluentd 的執行組態資訊。這種自我監控的能力對於確保 Fluentd 的穩定執行至關重要。

結構化日誌事件

到目前為止,我們只處理了最簡單的日誌檔案;然而,很少有日誌檔案是如此簡單。日誌檔案越結構化,我們就能越好地賦予日誌事件意義,並使其在 Fluentd 或下游解決方案中變得可操作。這意味著將解析器型別從無更改為使用提供的外掛之一。值得注意的是,我們可以將解析器分為兩大類別:

產品特定的解析器

某些產品因被大量使用而開發了特定的解析器,而不是使用具有詳細組態的通用解析器。Apache 和 Nginx 就是這樣的例子。使用這些解析器的好處是組態通常更直接,並且由於程式碼針對特定日誌結構進行了最佳化,因此效能更高。

通用解析器

通用解析器可以根據以下特點進行分組:

  • 支援特定的檔案表示法(例如 CSV、LTSV)
  • 使用高度可組態的解析器技術(例如 Grok、Regex)

這些解析器具有高度可組態性,但同時也更複雜。解析器越可組態和靈活,每個事件的解析效率就越低。

標準解析器

Fluentd 提供了多種標準解析器,包括針對 Apache 和 Nginx 等特定產品的解析器,以及 CSV 和 JSON 等通用解析器。

APACHE 2

Apache 解析器用於處理標準的 Apache 日誌檔案,記錄了請求和回應的詳細資訊,包括主機、使用者、方法、URI、HTTP 請求和回應程式碼、負載大小、參照者和代理。

APACHE_ERROR

除了核心的 Apache 日誌檔案外,還需要捕捉單獨的錯誤和相關的診斷及除錯資訊。這些資訊包括級別(例如警告、錯誤)、PID(程式識別符號)、與錯誤相關的客戶端(例如瀏覽器、應用程式)以及錯誤訊息。

NGINX

Nginx 解析器處理標準的 Nginx 存取日誌,捕捉收到的 HTTP 請求。該外掛的核心是應用正規表示式來捕捉訊息元素。

Nginx 解析器範例程式碼

<parse>
  @type nginx
</parse>

內容解密:

此段落展示瞭如何使用 Nginx 解析器來處理 Nginx 存取日誌。Nginx 解析器透過正規表示式捕捉訊息元素,包括遠端地址、使用者、方法、路徑、程式碼、大小、參照者和代理等。

CSV

CSV 解析器是一種快速解析器,預設使用逗號作為欄位的分隔符。可以透過設定 parser_type 屬性為 fast 來使用最佳化的快速解析器。

CSV 解析器範例程式碼

<parse>
  @type csv
  keys message, trans_id, time, host
  time_key time
  parser_type fast
</parse>

內容解密:

此段落展示瞭如何使用 CSV 解析器來處理 CSV 格式的日誌檔案。CSV 解析器透過指定欄位名稱(keys)和時間戳欄位(time_key)來將 CSV 資料轉換為結構化的日誌事件。

JSON

JSON 解析器將接收到的日誌事件視為 JSON 負載。它尋找一個名為 time 的根元素作為日誌事件的時間戳,並將巢狀的 JSON 結構作為記錄的一部分。

JSON 解析器範例程式碼

{
  "time": "2020/04/15 16:59:04",
  "tag": "myAppTag.Source1",
  "field1": "blah",
  "field2": "more blah",
  "fieldNested": {
    "nested1": "nested blah"
  },
  "fieldn": "enough of the blah"
}

內容解密:

此段落展示瞭如何使用 JSON 解析器來處理 JSON 格式的日誌事件。JSON 解析器自動將 JSON 資料轉換為結構化的日誌事件,並提取時間戳和標籤等資訊。

結構化日誌事件的解析技術

在處理日誌事件時,Fluentd 提供了多種解析器(parser)來幫助使用者將非結構化的日誌資料轉換為結構化的資料,以便於進一步的分析和處理。這些解析器包括 JSON、CSV、TSV、LTSV、MessagePack、多行(Multiline)解析器、正規表示式(Regex)解析器等。

JSON 解析器

JSON 解析器是 Fluentd 的預設解析器,它能夠高效地解析 JSON 格式的日誌資料。如果需要,可以更換為其他 Ruby 實作的 JSON 解析器,但預設的解析器在多個基準測試中表現出最佳效能。

TSV 與 CSV 解析器

TSV(Tab-Separated Values)與 CSV(Comma-Separated Values)解析器非常相似,主要區別在於 TSV 不支援值的轉義和參照,並且預設使用製表符(\t)作為分隔符。這兩種解析器都允許自定義分隔符,並且需要定義 keystime_key 屬性來對映 JSON 欄位和時間戳。TSV 解析器還支援 null_value_pattern 屬性,用於將符合特定模式的值替換為空字串。

LTSV 解析器

LTSV(Label Tab-Separated Value)是一種變體的 TSV 格式,它在每個製表符分隔的值前加上標籤和標籤分隔符(預設為冒號)。這種格式使得值的順序不再重要,並且比 JSON 更高效、更寬容。LTSV 解析器允許自定義分隔符和標籤分隔符。

MessagePack 解析器

MessagePack 是一種開放原始碼的標準和函式庫,它透過內嵌的負載描述來壓縮資料。MessagePack 格式在 Fluentd 中得到支援,特別是在跨網路傳輸日誌事件時,可以有效地減少資料量,從而節省頻寬和降低延遲。

多行(Multiline)解析器

多行解析器用於處理那些跨越多行的日誌事件,例如堆積疊追蹤資訊。它透過定義多個正規表示式來解析日誌行,並提取所需的元素。多行解析器目前僅與 tail 輸入外掛相容。

設定範例

@type multiline
format_firstline /^\d{4}-\d{2}-\d{2}/
format1 /^(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (?<message>.*)/
</parse>

內容解密:

  1. @type multiline 指定使用多行解析器。
  2. format_firstline 定義用於識別多行日誌事件第一行的正規表示式。
  3. format1 定義用於解析第一行之後的日誌內容的正規表示式,其中包含了擷取時間戳和訊息的命名捕捉組。

正規表示式(Regex)解析器

正規表示式解析器是一種功能強大的解析器,但也相對複雜。它允許使用者透過自定義的正規表示式來解析日誌資料,並提取所需的資訊。

無解析器(NONE)

無解析器是一種特殊情況,它不對日誌資料進行任何解析,而是將整個日誌行作為 Fluentd 的記錄。

結語

選擇合適的日誌解析器對於結構化日誌事件至關重要。不同的解析器適用於不同的日誌格式和場景。透過瞭解和使用這些解析器,使用者可以更有效地處理和分析日誌資料,從而更好地監控和除錯系統。