作為一個服務百萬機器的日志采集 agent,Logtail 目前已經提供了包括日志切分、日志解析(完整正則、JSON、分隔符)、日志過濾在内的常見處理功能,能夠應對絕大多數場景的處理需求。但有些時候,由于應用的曆史原因或是本身業務日志的複雜性,單一功能可能無法滿足所采集日志的處理需求,比如:
- 日志可能不再是單一格式,有可能同時由 JSON 或者分隔符日志組成。
- 日志格式可能也不固定,不同的業務邏輯所産生的日志具有不同的字段。
- ...
為此,Logtail 引入了 混合模式,一方面借助 Logtail 完善的事件機制來保證資料讀取階段的可靠性,另一方面,依賴于插件系統豐富的插件,來加強 Logtail 對複雜日志的處理能力。
Logtail 采集模式劃分
從整體來看,Logtail 的采集模式可以劃分為以下三種:
- 純 Logtail 模式:提供 文本日志 的采集能力,比如 Nginx 日志、JSON 日志、分隔符日志等。
- 純插件模式:作為文本日志采集的 補充 ,提供對更豐富資料源的采集能力,包括 MySQL Binlog、JDBC Query、Syslog 等,同時還提供了一系列的插件來支撐采集後的資料處理。
- 混合模式:綜合上述兩者的能力,支援通過插件實作對文本日志的多樣處理。
混合模式工作原理

如上圖中間部分所示,純 Logtail 的核心處理部分由日志切分(Splitter)和日志解析(Parser)組成,根據選擇的日志采集模式,日志切分把讀取的檔案内容切割成為一條條日志(比如單行基于換行符、多行基于行首正則),然後交由日志解析從單條日志中提取字段。由此可見,日志的采集模式固定了處理行為,比如完整正則模式要求日志必須完全符合設定的正規表達式,否則會報錯。這種基于采集模式的固定行為,擁有更好的性能,但犧牲了靈活性。
相比之下,混合模式則是犧牲一定的性能和計算資源來換取靈活性,以應對更為複雜的場景。如上圖所示,在混合模式下,Logtail 會将日志切分的結果直接送出給插件進行處理,在後者中,我們可以組合多種處理插件,來滿足我們的需求。
使用及限制說明
- 混合模式依舊依賴于 Logtail 完成日志切分,是以對于多行日志,依舊需要配置行首正則。
- 出于性能考慮,Logtail 送出給插件部分的資料并非是單條日志,而是日志的組合,是以插件處理配置的開始需要配置特定插件進行二次切分,具體下文将介紹。
- 性能開銷:視配置而定,混合模式下 Logtail 會消耗更多的資源(主要是 CPU)來完成處理任務,是以需要根據實際情況調整 Logtail 的 資源限制 。如果采集節點的資源有限,但确實有處理需求,可以仍舊使用純 Logtail 模式進行采集,之後使用 資料加工 來實作資料處理。
- Logtail 的版本需要在 0.16.28 及以上。
- 一些文本檔案的功能在混合模式下無法使用,包括過濾器配置、上傳原始日志、機器時區、丢棄解析失敗日志、接受部分字段(分隔符模式)等,但其中部分功能可通過相關插件來實作。
- 由于處理方式的改變,使用插件處理後會将檔案級别的 meta(比如 path)放到每條日志中,這會影響上下文、LiveTail等功能。
插件處理配置文法
插件處理配置使用 JSON 對象進行表示,配置的 key 為
processors
,value 是 JSON 對象的數組,數組内的每一個 JSON 對象表示一個處理插件的配置,處理時将按照數組内的定義順序依次執行。數組内的每個 JSON 對象包含兩個字段:
type
和
detail
,
type
表示處理插件的類型(JSON string),
detail
表示該插件的詳細參數(JSON 對象,key 為參數名,value 為參數值)。
{
"processors": [
{
"type": "processor_regex",
"detail": {
"SourceKey": "content",
"Regex": "...",
"Keys": [
"time",
"short_msg",
"main_msg",
]
}
},
{
"type": "processor_regex",
"detail": {
"SourceKey": "main_msg",
"Regex": "...",
"Keys": [
...
]
}
}
]
}
如上示例表示使用兩個
processor_regex
插件進行日志處理,第一個插件根據配置的
Regex
參數對日志中的
content
字段進行正則提取,結果為
Keys
參數指定的三個字段,而第二個插件對上一步提取得到的
main_msg
字段再次進行正則提取,得到更多的字段。
支援的插件清單
以下是目前所支援的處理插件,關于具體插件如何使用可參考文檔
處理采集資料。
插件類型(type) | 功能 |
---|---|
processor_add_fields | 向日志中添加強定的一些字段 |
processor_rename | 重命名指定字段名 |
processor_drop | 根據字段名丢棄日志中的一些字段 |
processor_drop_last_key | 當日志中存在指定的一些字段(名)時,丢棄特定字段,一般用于解析類型的處理插件後,當存在解析後的字段時,表示解析成功,可以丢棄原始字段 |
processor_filter_key_regex | 判斷字段名是否符合設定的正規表達式,進而決定是否保留該字段 |
processor_filter_regex | 判斷字段值是否符合設定的正規表達式,進而決定是否保留該字段 |
processor_geoip | 對指定字段值(IP)進行地理位置分析,需要自行提供資料庫 |
processor_gotime | 對指定字段值使用 Go 語言的時間格式進行解析,可将解析結果設定為日志時間 |
processor_strptime | 對指定字段值使用 strptime 的時間格式進行解析,可将解析結果設定為日志時間 |
processor_md5 | 對指定字段值進行 MD5 |
processor_base64_decoding | 對指定字段值進行 base64 解碼 |
processor_base64_encoding | 對指定字段值進行 base64 編碼 |
processor_anchor | 可以配置 anchor 指定 start/stop 子串,然後對指定字段值進行處理,提取 start/stop 子串之間的内容作為新字段 |
processor_regex | 對指定字段值進行正則提取 |
processor_json | 對指定字段值進行 JSON 解析,可将結果展開為日志内容 |
processor_packjson | 将指定的多個字段以 JSON 對象的格式打包至一個目标字段 |
processor_split_char | 對指定字段值進行單字元分隔符(支援設定引用符)解析 |
processor_split_string | 對指定的字段值進行多字元分隔符解析 |
processor_split_key_value | 對指定的字段值進行鍵值對解析,如飛天日志 |
processor_split_log_regex | 對指定字段值使用行首正規表達式進行切分,結果将分裂為多條日志,一般用于混合模式下對接 Logtail 多行日志 |
processor_split_log_string | 對指定字段值使用多字元進行切分,結果将分裂為多條日志,一般用于混合模式下對接 Logtail 單行日志(使用換行符作為分隔) |
使用特定插件完成日志的二次切分
前文的說明中曾經提到,出于性能考慮,Logtail 送出給插件部分的資料并非是單條日志,而是日志的組合,需要在插件處理配置的開始增加特定插件進行二次切分。
總的來說,切分時所需要考慮的情況有單行日志和多行日志兩種,以下将分别介紹。
注意:此配置僅在混合模式下需要,如果使用的純自定義插件的采集配置,可以忽略。
單行日志
假設單行日志的内容是
2019-09-23 14:08:16.952 [INFO] hello world
,則 Logtail 送出給插件部分的資料内容可能是:
"content": "2019-09-23 14:08:16.952 [INFO] hello world\n2019-09-23 14:08:16.952 [INFO] hello world1\n2019-09-23 14:08:16.952 [INFO] hello world2"
可以看到,多條單行日志被一次性輸入到插件進行中,是以,我們需要配置一個針對單行日志的切分插件,即先前清單中最後的
processor_split_log_string
。對于單行日志,可以直接複用如下配置:
{
"type": "processor_split_log_string",
"detail": {
"SplitKey": "content",
"SplitSep": "\n"
}
}
多行日志
類似地,多行日志在送出給插件部分時也需要使用
processor_split_log_regex
進行基于行首正則的切分,配置如下(假設日志開頭為常見的 [] 包裹時間):
{
"type": "processor_split_log_regex",
"detail": {
"SplitKey": "content",
"SplitRegex": "\\[\\d+-\\d+\\d+\\s\\d+:\\d+:\\d+\\].*"
}
}
參考此配置時需要根據實際情況調整行首正規表達式(
SplitRegex
)。
混合模式使用步驟
- 根據日志是單行還是多行,确定插件處理配置中需要引入的切分插件及其配置,如果是多行,确認行首正規表達式。
- 根據日志的格式組合之前提及的處理插件,完善插件處理配置。
- 應用插件處理配置:兩種途徑,API/SDK 或控制台。
建立混合模式采集配置
從先前的介紹中可以看出,混合模式的采集配置實質上是文本模式的采集配置附加上額外的插件處理配置,是以,配置它的入口依舊是建立文本模式的采集配置:如果是單行日志,建立 極簡模式 的采集配置,如果是多行日志,建立 完整正則模式 的采集配置,并切換至多行模式,設定行首正規表達式。
API/SDK
在完成文本模式的采集配置建立後,我們可以通過 API/SDK 的方式添加建構好的插件處理配置。此處,我們借助
CLI來實作這個過程,代碼如下:
# add_plugin_config.py
# -*- coding:utf-8 -*-
import commands
import json
import os
import sys
if len(sys.argv) != 4:
print 'Usage: add_plugin_config.py <your_project_name> <your_config_name> <plugin_config_file>'
sys.exit(1)
your_project_name = sys.argv[1]
your_config_name = sys.argv[2]
your_plugin_config_file = sys.argv[3]
status, output = commands.getstatusoutput(
'aliyunlog log get_logtail_config --project_name={} --config_name={}'.format(your_project_name, your_config_name))
if status != 0:
print '[ERR] can not find specified config, please check your CLI configuration'
print 'ErrMsg:', output
sys.exit(1)
config = json.loads(output)
print config
plugin = json.load(open(your_plugin_config_file, 'r'))
config["inputDetail"]["plugin"] = plugin
with open('logtail_config.json', 'wb') as f:
f.write(json.dumps(config))
update_cmd = 'aliyunlog log update_logtail_config --project_name="{}" --config_detail="$(cat logtail_config.json)"'.format(your_project_name)
print update_cmd
print os.system(update_cmd)
确定你需要修改的配置所屬的
project_name
、
config_name
,把插件處理配置儲存到任意檔案(比如
plugin_config.json
)後,調用腳本傳入這三個參數即可。
控制台
在 SLS 控制台上,我們可以通過 進階選項-啟用插件處理 來啟用混合模式。
在 API/SDK 的方式下,我們需要根據所選模式(JSON、分隔符等)來對二次切分的插件進行配置。而在控制台上使用混合模式時,頁面将會根據所選的模式,自動地生成相應的預設配置,我們隻需要配置針對單條日志的處理即可。通過 顯示/隐藏預設配置,我們可以檢視插件配置的完整内容。
以下示例在極簡模式下使用
processor_add_fields
插件為每條日志增加兩個固定的字段。從兩張截圖的差別可以發現,頁面在預設配置中自動地填充了
processor_split_log_string
來實作按行二次切分。
示例
原始日志
假設我們所采集的是單行文本日志,原始日志内容如下:
"content": "2019-09-23 14:08:16.952 cip>->1.1.1.1_'_sip>->2.2.2.2_'_scheme>->http:POST_'_uri>->/v1/a/b/c_'_rt.all>->21_'_rt.p0>->19_'_\t{\"errcode\":10000,\"errmsg\":\"OK\",\"errdetail\":null,\"data\":{}}"
該日志由 時間、多組鍵值對、JSON 對象 三部分組成,分别使用 空格 和 制表符 進行分隔,其中多組鍵值對部分使用
_'_
分隔鍵值對,
>->
分隔鍵和值。
預期結果
為了友善分析,我們希望将日志的内容提取成如下的字段:
"time": "2019-09-23 14:08:16.952"
"cip": "1.1.1.1"
"sip": "2.2.2.2"
"scheme": "http:POST"
"uri": "/v1/a/b/c"
"rt.all": "21"
"rt.p0": "19"
"errcode": "10000"
"errmsg": "OK"
"errdetail": "null"
插件處理配置
{
"processors": [
{
"type": "processor_split_log_string",
"detail": {
"SplitKey": "content"
}
},
{
"type": "processor_regex",
"detail": {
"SourceKey": "content",
"Regex": "(\\d+-\\d+-\\d+\\s\\d+:\\d+:\\d+\\.\\d+)\\s(.*)_'_\t(.*)",
"Keys": [
"time",
"main_msg",
"json_msg"
],
"NoKeyError": true,
"NoMatchError": true,
"KeepSource": true
}
},
{
"type": "processor_json",
"detail": {
"SourceKey": "json_msg",
"KeepSource": true
}
},
{
"type": "processor_split_key_value",
"detail": {
"SourceKey": "main_msg",
"Delimiter": "_'_",
"Separator": ">->",
"KeepSource": true
}
}
]
}
如上是我們所建立的混合模式采集配置中的插件處理配置部分,由 4 個處理插件組成:
- processor_split_log_string:用于完成之前【使用特定插件完成日志的二次切分】提及的日志切分。
- processor_regex:将日志内容切分為三部分,三個字段分别為
time
main_msg
json_msg
- processor_json:對上一步提取得到的
進行解析,将其中的内容展開為日志字段。json_msg
- processor_split_key_value:對
字段進行進一步的解析,将其中的各個鍵值對展開為日志字段。main_msg