數據加工服務簡介
數據加工服務是阿里雲SLS推出的面向日誌ETL處理的服務,主要解決數據加工過程中轉換、過濾、分發、富化等場景。
接下來,我們以nginx日誌解析為例, 幫助大家快速入門阿里雲日誌服務的數據加工。
用於實驗的Nginx日誌
假設我們通過極簡模式採集了Nginx默認日誌。默認的nginx 日誌format如下
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
通過使用極簡模式採集Nginx日誌,樣例如下:
數據加工交互界面
點擊 準備數據加工的Logstore,在查詢欄的上方,有一個“數據加工”的開關,打開
數據加工的代碼編輯、預覽、發佈相關界面如下圖:
一般情況下,我們都可以使用快速預覽模式,如果我們的數據加工涉及到使用mysql、oss等維表,可以使用高級預覽做實際預覽測試。
對Nginx日誌進行數據加工 (Step by Step)
Step1. 使用正則抽取基礎字段
對於極簡模式採集的日誌,內容都在一個字段叫content的字段裡,不利於我們做分析。可以通過數據加工正則函數,抽取nginx日誌裡的字段,使用到的加工函數如下:
# 用於將源字段裡的內容,通過正則捕獲組抽取出對應的字段
e_regex("源字段", "正則或有命名捕獲正則", "目標字段名或數組(可選)")
針對Nginx日誌,使用以下語句進行正則抽取
# 通用字段抽取
e_regex("content",'(?<remote_addr>[0-9:\.]*) - (?<remote_user>[a-zA-Z0-9\-_]*) \[(?<local_time>[a-zA-Z0-9\/ :\-\+]*)\] "(?<request>[^"]*)" (?<status>[0-9]*) (?<body_bytes_sent>[0-9\-]*) "(?<refer>[^"]*)" "(?<http_user_agent>[^"]*)"')
通過正則抽取以後,可以看到日誌的字段增加了refer、remote_addr、remote_user、request等字段。
Step2. 處理時間字段
當前提取到的localtime不易讀,我們把它解析成易讀的格式,會用到的以下數據加工函數:
# 用於設置字段值
e_set("字段名", "固定值或表達式函數")
# 將時間字符串解析為日期時間對象
dt_strptime('值如v("字段名")', "格式化字符串")
# 將日期時間對象按照指定格式轉換為字符串
dt_strftime(日期時間表達式, "格式化字符串")
實現思路,先通過 dt_strptime 將local_time的時間轉化為日期時間對象,然後再通過dt_strftime將日期時間對象轉化為標準的日期時間字符串。 針對Nginx local_time的轉化,使用如下數據加工語句:
e_set("local_time", dt_strftime(dt_strptime(v("local_time"),"%d/%b/%Y:%H:%M:%S %z"),"%Y-%m-%d %H:%M:%S"))
實現效果如下:
Step3. 解析request uri
可以看到request字段由 METHOD URI VERSION組成,我們希望對 requst字段進行抽取,獲取到請求的METHOD、URI以及VERSION,並且將請求URI中的請求的參數變成字段,方便後續進行查詢。可以用以下函數來做實現
# 使用正則將request uri抽取
e_regex("源字段名", "正則或有命名捕獲正則", "目標字段名或數組(可選)", mode="fill-auto")
# 進行urldecode
url_decoding('值如v("字段名")’)
# 設置字段值
e_set("字段名", "固定值或表達式函數", ..., mode="overwrite")
# 將request_uri中的key=value的組合抽取成字段 值的模式
e_kv("源字段正則或列表", sep="=", prefix="")
實現語句
e_regex("request", "(?<request_method>[^\s]*) (?<request_uri>[^\s]*) (?<http_version>[^\s]*)")
e_set("request_uri", url_decoding(v("request_uri")))
e_kv("request_uri", prefix="uri_")
實現效果
Step4. http code狀態碼映射
每一個http狀態碼都代表了不同的含義,下面是一份http狀態碼的映射表, 我們可以通過e_table_map 函數來將狀態碼的信息擴展到我們的日誌字段中,方便後續做統計分析。
涉及到的數據加工函數如下:
# 用來做字段富化,類似sql裡join的功能
e_table_map("表格如通過tab_parse_csv(...)解析得到的",
"源字段列表或映射列表如[('f1', 'f1_new'), ('f2', 'f2_new')]",
"目標字段列表")
# 用來把csv文件解析成table對象
tab_parse_csv(CSV文本數據, sep=',', quote='"')
# code的映射關係維度表是一個csv文件,存在oss上,使用res_oss_file
res_oss_file(endpoint="OSS的endpoint", ak_id="OSS的AK_ID",
ak_key="OSS的AK_KEY", bucket="OSS的bucket", file="在OSS中存的文件地址",
change_detect_interval="定時更新時間,默認為0")
實際使用到的DSL語句如下
# http狀態碼映射
e_table_map(
tab_parse_csv(
res_oss_file(endpoint="oss-cn-shanghai.aliyuncs.com",
ak_id='',ak_key='',
bucket="etl-test",
file="http_code.csv", format='text')),
[("status","code")],
[("alias","http_code_alias"),
("description","http_code_desc"),
("category","http_code_category")])
看一下映射後的效果
Step5. 通過UserAgent判斷客戶端操作系統
我們想了解客戶用的是什麼os版本,可以通過user agent裡的字段用正則匹配來判斷,用到dsl語句
# 取某個字段的值
v("字段名")
# 獲取ua相關信息
ua_parse_all("帶useragent信息的內容")
# 展開json字段, 因為ua_parse_all得到的是一個json對象,為了展開到一級字段使用e_json做展開
# 模式有 simple、full、parent、root 參考https://help.aliyun.com/document_detail/125488.html#section-o7x-7rl-2qh
e_json("源字段名", fmt="模式", sep="字段分隔符")
# 丟棄臨時產生的字段
e_drop_fields("字段1", "字段2")
用到的dsl語句
# 通過User Agent解析獲得客戶端信息
e_set("ua",ua_parse_all(v("http_user_agent")))
e_json("ua", fmt='full',sep='_')
e_drop_fields("ua",regex=False)
加工效果
Step6. 非200的日誌投遞到指定logstore
可以使用e_output函數來做日誌投遞,用regex_match做字段匹配
# 條件判斷if
e_if("條件1如e_match(...)", "操作1如e_regex(...)", "條件2", "操作2", ....)
# 判斷是否相等
op_ne(v("字段名1"), v("字段名2"))
# output發送到目標名稱,目標名稱在數據加工保存任務的時候配置對應的logstore信息
e_output(name="指定的目標名稱")
實際的dsl語句
# 分發非200的日誌
e_if(op_ne(v("http_code_alias"),"2xx"), e_output(name="nginx-log-bad"))
在預覽裡看到這個效果。(保存加工的時候,需要設置好對應project、logstore的ak信息)
完整的DSL代碼以及上線流程
好了,通過一步一步的開發調試,現得到完整的DSL代碼如下
# 通用字段抽取
e_regex("content",'(?<remote_addr>[0-9:\.]*) - (?<remote_user>[a-zA-Z0-9\-_]*) \[(?<local_time>[a-zA-Z0-9\/ :\-]*)\] "(?<request>[^"]*)" (?<status>[0-9]*) (?<body_bytes_sent>[0-9\-]*) "(?<refer>[^"]*)" "(?<http_user_agent>[^"]*)"')
# 設置localttime
e_set("local_time", dt_strftime(dt_strptime(v("local_time"),"%d/%b/%Y:%H:%M:%S"),"%Y-%m-%d %H:%M:%S"))
# uri字段抽取
e_regex("request", "(?<request_method>[^\s]*) (?<request_uri>[^\s]*) (?<http_version>[^\s]*)")
e_set("request_uri", url_decoding(v("request_uri")))
e_kv("request_uri", prefix="uri_")
# http狀態碼映射
e_table_map(
tab_parse_csv(
res_oss_file(endpoint="oss-cn-shanghai.aliyuncs.com",
ak_id='',ak_key='',
bucket="etl-test",
file="http_code.csv", format='text')),
[("status","code")],
[("alias","http_code_alias"),
("description","http_code_desc"),
("category","http_code_category")])
# 通過User Agent解析獲得客戶端信息
e_set("ua",ua_parse_all(v("http_user_agent")))
e_json("ua", fmt='full',sep='_')
e_drop_fields("ua",regex=False)
# 分發非200的日誌
e_if(op_ne(v("http_code_alias"),"2xx"), e_coutput(name="nginx-log-bad"))
在頁面提交代碼以後,保存數據加工
配置目標logstore信息,默認走完加工邏輯的日誌都會發送到第一個目標logstore,我們在代碼裡指定了e_output到指定logstore,因此還需要第二個目標,並且目標的名字和e_output裡指定的目標名稱一致。
保存完即完成上線,可以在數據處理-加工下看到該任務,點擊進去可以看到加工延遲等信息。
Nginx日誌查詢、可視化、報警
查詢
假設,我們想對nginx異常的日誌進行查詢,以便做問題定位。我們可以針對需要查詢的字段開啟索引
開啟索引可以使用“自動生成索引”來自動幫助生成字段索引列表(否則需要手工填寫字段)
點擊確定後,即完成索引配置。比如我們要查詢 uri_item_name參數值為“測試商品_89”的錯誤情況,可以使用這樣的query
* and uri_item_name : 測試商品_89
可視化
我們關心每個請求的http請求是否成功,對於異常狀態碼,想做相應的統計,以方便我們做問題發現。 可以使用如下query統計 nginx-log-bad(非2xx類請求的logstore)狀態碼分佈
* | select http_code_alias,count(*) as c
from log where http_code_alias is not null
group by http_code_alias order by c
使用餅圖做可視化
點擊,“添加到儀表盤”,即可完成儀表盤的創建
添加完成後,在左側儀表盤就
報警
針對非2xx的狀態碼,我們不僅要做可視化,還需要對它進行報警。 可以在剛才創建的儀表盤裡,點擊“告警->創建”,根據提示創建相應的告警規則。
總結
本次訓練營,以最常見的Nginx日誌作為例,介紹數據加工在日誌的轉化、處理、富化、轉發等場景下相關的算子使用,幫助大家入門SLS的數據加工。除了文中提到的算子,數據加工還有200+的算子以支持更多的日誌處理的場景,相關內容可以參考如下:
a) SLS數據加工整體簡介:https://help.aliyun.com/document_detail/125384.html
b) SLS數據加工函數總覽:https://help.aliyun.com/document_detail/159702.html
在數據加工之後,針對相關字段建立索引,可以方便地對日誌內容進行各種場景的分析,並且針對有需要的場景設置對應的報警,提升系統的穩定性。