收記錄時常遇到一種狀況:某套應用程式送進 Graylog 的 syslog,source 欄位全部都是 localhost.localdomain。原因通常是那台主機的主機名稱沒設定好,或應用程式自己組 syslog 時寫死了主機名稱。
結果就是在 Graylog 裡完全分不出是誰送的,依 source 做的串流規則、看板、告警也都對不上。這篇用 Pipeline Rule 依訊息內容改寫 source,順便整理幾種其他的處理方式。
情境
以某套應用程式為例(以下用 MyApp 代稱),它的記錄長這樣:
<134>Mar 25 10:15:32 localhost.localdomain MyApp[2381]: user login success, uid=1001
訊息裡有 MyApp[ 這段程式名稱與 PID,但 source 被設成 localhost.localdomain。目標是:只要訊息裡出現 MyApp[,就把 source 改成 MyApp。
Pipeline Rule
基本寫法
rule "Set source to MyApp"
when
contains(to_string($message.message), "MyApp[", true)
then
set_field("source", "MyApp");
end
contains(值, 要找的字串, 忽略大小寫)
第三個參數true是不分大小寫to_string()
確保欄位是字串再比對,欄位不存在時也不會出錯set_field()
直接覆寫source
較嚴謹的寫法
只在 source 真的是 localhost.localdomain 時才改,避免影響其他已經正確的記錄(例如另一台主機名稱設對的同一套程式),同時把原本的值留下來:
rule "Set source to MyApp"
when
contains(to_string($message.message), "MyApp[", true)
&& to_string($message.source) == "localhost.localdomain"
then
set_field("original_source", $message.source);
set_field("source", "MyApp");
end
保留 original_source 的好處是日後追查時,還看得出這筆原本是怎麼送進來的。
同一台主機有多套程式時
如果同一個 source 底下有好幾套程式,可以改用正規表示式把程式名稱抓出來,一條規則處理全部:
rule "Set source from app name"
when
to_string($message.source) == "localhost.localdomain"
&& regex("^(MyApp|OtherApp)\\[", to_string($message.message)).matches == true
then
let m = regex("^(MyApp|OtherApp)\\[", to_string($message.message));
set_field("original_source", $message.source);
set_field("source", m["0"]);
end
regex() 的群組從 "0" 開始編號。這裡比對訊息開頭,所以前提是 message 欄位已經拿掉 syslog 的表頭(Graylog 的 Syslog input 解析成功時就是如此)。
注意:contains() 比 regex() 省資源,能用 contains() 就不要用 regex()。Pipeline 會對每一筆進來的訊息執行,記錄量大的環境差異很明顯。
設定步驟
- 建立規則
「System → Pipelines」→「Manage rules」→「Create Rule」,貼上規則內容並儲存 - 建立 Pipeline
「Manage pipelines」→「Add new pipeline」,在 Stage 0 加入剛才的規則 - 連結串流
在 Pipeline 的「Edit connections」連結到這些記錄會進入的串流(不確定的話先連 Default Stream) - 模擬測試
Pipeline 頁面有「Simulate processing」,貼一筆原始訊息就能看到規則套用後的結果,上線前先測過
處理順序
Pipeline 有沒有在正確的時機執行,要看「System → Configurations → Message Processors」的順序。預設是:
- Message Filter Chain(extractor、串流規則)
- Pipeline Processor
這代表串流規則判斷時,source 還沒被 Pipeline 改掉。如果有依 source 分流的串流規則,要嘛把串流規則改成依訊息內容判斷,要嘛在 Pipeline 裡用 route_to_stream() 直接分流:
rule "Set source to MyApp and route"
when
contains(to_string($message.message), "MyApp[", true)
then
set_field("source", "MyApp");
route_to_stream(name: "MyApp Logs");
end
其他做法
從來源修正
最根本的做法還是讓送記錄的那一端送對:
- 設好主機名稱(
hostnamectl set-hostname app01.example.com),並確認/etc/hosts一致 - 如果記錄是透過 rsyslog 轉送,可以在 rsyslog 的範本裡指定主機名稱
- 應用程式本身有 syslog 設定的話,看有沒有可以填主機名稱的欄位
能從來源改就從來源改,Graylog 端的規則是在沒辦法動來源(例如廠商設備、封閉的商用軟體)時才用。
Input 的 Override source
Syslog input 的設定裡有 Override source 欄位,填了之後這個 input 收到的所有訊息 source 都會被改成那個值。適合專門給一台設備使用的 input,例如開一個 UDP 1515 的 input 只收這套程式的記錄。
缺點是要多開 input、多開防火牆,而且整個 input 的訊息都會被改,沒辦法依內容判斷。
比較
| 做法 | 優點 | 缺點 |
|---|---|---|
| 從來源修正 | 最根本,Graylog 不用額外處理 | 不一定改得到 |
| Input 的 Override source | 設定簡單,不耗效能 | 要專用 input,無法依內容判斷 |
| Pipeline Rule | 最有彈性,可依內容判斷、保留原值 | 每筆訊息都要執行,規則要寫得精簡 |
結論
source 是 Graylog 裡最常用來篩選的欄位之一,它錯了,後面的串流、看板、告警都會跟著錯。我的建議是:能從來源改就從來源改,改不了再用 Pipeline Rule,而且規則要加上條件只處理需要的那幾筆,並把原值留下來。
參考資料
- Graylog 文件:Pipelines
https://go2docs.graylog.org/current/making_sense_of_your_log_data/pipelines.html - Graylog 文件:Functions
https://go2docs.graylog.org/current/making_sense_of_your_log_data/functions.html - Graylog 文件:Syslog Inputs
https://go2docs.graylog.org/current/getting_in_log_data/syslog_inputs.html



