弱結構化日誌 Flink SQL 怎麼寫?SLS SPL 來幫忙

發表於2024-02-27

作者:潘偉龍(豁朗)

背景

日誌服務 SLS 是雲原生觀測與分析平臺,為 Log、Metric、Trace 等資料提供大規模、低成本、實時的平臺化服務,基於日誌服務的便捷的資料接入能力,可以將系統日誌、業務日誌等接入 SLS 進行儲存、分析;阿里雲 Flink 是阿里雲基於 Apache Flink 構建的大資料分析平臺,在實時資料分析、風控檢測等場景應用廣泛。阿里雲 Flink 原生支援阿里雲日誌服務 SLS 的 Connector,使用者可以在阿里雲 Flink 平臺將 SLS 作為源表或者結果表使用。

阿里雲 Flink SLS Connector 對於結構化的日誌非常直接,透過配置,SLS 的日誌欄位可以與 Flink SQL 的 Table 欄位列一一對映;然後仍有大量的業務日誌並非完全的結構化,例如會將所有日誌內容寫入一個欄位中,需要正則提前、分隔符拆分等手段才可以提取出結構化的欄位,基於這個場景,本文介紹一種使用 SLS SPL 配置 SLS Connector 完成資料結構化的方案,覆蓋日誌清洗與格式規整場景。

弱結構化日誌處理的痛點

弱結構化日誌現狀與結構化處理需求的矛盾

日誌資料往往是多種來源,多種格式,往往沒有固定的 Schema,所以在資料處理前,需要先對資料進行清洗、格式規整,然後在進行資料分析;這類資料內容格式是不固定的,可能是 JSON 字串、CSV 格式,甚至是不規則的 Java 堆疊日誌。

Flink SQL 是一種相容 SQL 語法的實時計算模型,可以基於 SQL 對結構化資料進行分析,但同時也要求源資料模式固定:欄位名稱、型別、數量是固定;這也是 SQL 計算模型的基礎。

日誌資料的弱結構化特點與 Flink SQL 結構化分析之間有著一道鴻溝,跨越這道鴻溝需要一箇中間層來進行資料清洗、規整;這個中間層的方案有多種選擇可以使用,下面會對不同的方案做簡單對比,並提出一種新的基於 SLS SPL 的方案來輕量化完成解決資料清洗規整的工作。

弱結構化日誌資料

下面是一條日誌示例,日誌格式較為複雜,既有 JSON 字串,又有字串與 JSON 混合的場景。其中:

  • Payload 為 JSON 字串,其中 schedule 欄位的內容也是一段 JSON 結構。
  • requestURL 為一段標準的 URL Path 路徑。
  • error 欄位是前半部分包含 CouldNotExecuteQuery:字串,後半部分是一段 JSON 結構。
  • tag__:__path 包含日誌檔案的路徑,其中 service_a 可能是業務名稱。
  • caller 中包含檔名與檔案行數。
{
  "Payload": "{\"lastNotified\": 1705030483, \"serverUri\": \"http://test.alert.com/alert-api/tasks\", \"jobID\": \"44d6ce47bb4995ef0c8052a9a30ed6d8\", \"alertName\": \"alert-12345678-123456\", \"project\": \"test-sls-project\", \"projectId\": 123, \"aliuid\": \"1234567890\", \"alertDisplayName\": \"\\u6d4b\\u8bd5\\u963f\\u91cc\\u4e91\\u544a\\u8b66\", \"checkJobUri\": \"http://test.alert.com/alert-api/task_check\", \"schedule\": {\"timeZone\": \"\", \"delay\": 0, \"runImmediately\": false, \"type\": \"FixedRate\", \"interval\": \"1m\"}, \"jobRunID\": \"bf86aa5e67a6891d-61016da98c79b-5071a6b\", \"firedNotNotified\": 25161}",
  "TaskID": "bf86aa5e67a6891d-61016da98c79b-5071a6b-334f81a-5c38aaa1-9354-43ec-8369-4f41a7c23887",
  "TaskType": "ALERT",
  "__source__": "11.199.97.112",
  "__tag__:__hostname__": "iabcde12345.cloud.abc121",
  "__tag__:__path__": "/var/log/service_a.LOG",
  "caller": "executor/pool.go:64",
  "error": "CouldNotExecuteQuery : {\n    \"httpCode\": 404,\n    \"errorCode\": \"LogStoreNotExist\",\n    \"errorMessage\": \"logstore k8s-event does not exist\",\n    \"requestID\": \"65B7C10AB43D9895A8C3DB6A\"\n}",
  "requestURL": "/apis/autoscaling/v2beta1/namespaces/python-etl/horizontalpodautoscalers/cn-shenzhen-56492-1234567890123?timeout=30s",
  "ts": "2024-01-29 22:57:13"
}

結構化資料處理需求

對於這樣的日誌提取出更有價值的資訊需要進行資料清洗,首先需要提取重要的欄位,然後對這些欄位進行資料分析;本篇關注重要欄位的提取,分析仍然可以在 Flink 中進行。

假設提取欄位具體需求如下:

  • 提取 error 中的 httpCode、errorCode、errorMessage、requestID。
  • 提取 tag__:__path 中的 service_a 作為 serviceName。
  • 提取 caller 中的 pool.go 作為 fileName,64 作為 fileNo。
  • 提取 Payload 中的 project;提取 Payload 下面的 schedule 中的 type 為 scheuleType。
  • 重新命名 source 為 serviceIP。
  • 其餘欄位捨棄。

最終需要的欄位列表如下,基於這樣一個表格模型,我們可以便捷的使用 Flink SQL 進行資料分析。

圖片

解決方案

實現這樣的資料清洗,有很多種方法,這裡列舉幾種基於 SLS 與 Flink 的方案,不同方案之間沒有絕對的優劣,需要根據不同的場景選擇不同的方案。

資料加工方案: 在 SLS 控制檯建立目標 Logstore,透過建立資料加工任務,完成對資料的清洗。

圖片

Flink 方案: 將 error 和 payload 指定為源表欄位,透過 SQL 正則函式、JSON 函式對欄位進行解析,解析後的欄位寫入臨時表,然後對臨時表進行分析。

圖片

SPL 方案: 在 Flink SLS Connector 中配置 SPL 語句,對資料進行清洗,Flink 中源表欄位定義為清洗後的資料結構。

圖片

從上述三種方案的原理不難看出,在需要資料清洗的場景中,在 SLS Connector 中配置 SPL 是一種更輕量化的方案,具有輕量化、易維護、易擴充套件的特點。

在日誌資料弱結構化的場景中,SPL 方案既避免了方案一中建立臨時中間 Logstore,也避免了方案二中在 Flink 中建立臨時表,在離資料來源更近的位置進行資料清洗,在計算平臺關注業務邏輯,職責分離更加清晰。

如何在 Flink 中使用 SPL

接下來以一段弱結構化日誌為例,來介紹基於 SLS SPL 的能力來使用 Flink。為了便於演示,這裡在 Flink 控制檯配置 SLS 的源表,然後開啟一個連續查詢以觀察效果。在實際使用過程中,僅需修改 SLS 源表配置,即可完成資料清洗與欄位規整。

SLS 準備資料

  • 開通 SLS,在 SLS 建立 Project,Logstore,並建立具有消費 Logstore 的許可權的賬號 AK/SK。
  • 當前 Logstore 資料使用 SLS SDK 寫入模擬資料,格式使用上述日誌片段,其中包含 JSON、複雜字串等弱結構化欄位。

圖片

預覽 SPL 效果

在 Logstore 可以可以開啟掃描模式,SLS SPL 管道式語法使用分隔符分割不同的指令,每次輸入一個指令可以即時檢視結果,然後增加管道數,漸進式、探索式獲取最終結果。

圖片

對上圖中的 SPL 進行簡單描述:

* | project Payload, error, "__tag__:__path__", "__tag__:__hostname__", caller 
 | parse-json Payload 
 | project-away Payload 
 | parse-regexp error, 'CouldNotExecuteQuery : ({[\w":\s,\-}]+)' as errorJson 
 | parse-json errorJson 
 | parse-regexp "__tag__:__path__", '\/var\/log\/([\w\_]+).LOG' as serviceName 
 | parse-regexp caller, '\w+/([\w\.]+):(\d+)' as fileName, fileNo 
 | project-rename serviceHost="__tag__:__hostname__" 
 | extend scheduleType = json_extract_scalar(schedule, '$.type') 
 | project httpCode, errorCode,errorMessage,requestID,fileName, fileNo, serviceHost,scheduleType, project
  • 1 行:project 指令:從原始結果中保留 Payload、error、__tag__:__path__、caller 欄位,捨棄其他欄位,這些欄位用於後續解析。
  • 2 行:parse-json 指令:將 Payload 字串展開為 JSON,第一層欄位出現在結果中,包括 lastNotified、serviceUri、jobID 等。
  • 3 行:project-away 指令:去除原始 Payload 欄位。
  • 4 行:parse-regexp 指令:按照 error 欄位中的內容,解析其中的部分 JSON 內容,置於 errorJson 欄位。
  • 5 行:parse-json 指令:展開 errorJson 欄位,得到 httpCode、errorCode、errorMessage 等欄位。
  • 6 行:parse-regexp 指令:透過正規表示式解析出 tag__:__path 種的檔名,並命名為 serviceName。
  • 7 行:parse-regexp 指令:透過正規表示式捕獲組解析出 caller 種的檔名與行數,並置於 fileName、fileNo 欄位。
  • 8 行:project-rename 指令:將 tag__:__hostname 欄位重新命名為serviceHost。
  • 9 行:extend 指令:使用 json_extract_scalar 函式,提取 schedule 中的 type 欄位,並命名為 scheduleType。
  • 10 行:project 指令:保留需要的欄位列表,其中 project 欄位來自於 Payload。

建立 SQL 作業

在阿里雲 Flink 控制檯建立一個空白的 SQL 的流作業草稿,點選下一步,進入作業編寫。

圖片

在作業草稿中輸入如下建立臨時表的語句:

CREATE TEMPORARY TABLE sls_input_complex (
  errorCode STRING,
  errorMessage STRING,
  fileName STRING,
  fileNo STRING,
  httpCode STRING,
  requestID STRING,
  scheduleType STRING,
  serviceHost STRING,
  project STRING,
  proctime as PROCTIME()
) WITH (
  'connector' = 'sls',
  'endpoint' ='cn-beijing-intranet.log.aliyuncs.com',
  'accessId' = '${ak}',
  'accessKey' = '${sk}',
  'starttime' = '2024-02-01 10:30:00',
  'project' ='${project}',
  'logstore' ='${logtore}',
  'query' = '* | project Payload, error, "__tag__:__path__", "__tag__:__hostname__", caller | parse-json Payload | project-away Payload | parse-regexp error, ''CouldNotExecuteQuery : ({[\w":\s,\-}]+)'' as errorJson | parse-json errorJson | parse-regexp "__tag__:__path__", ''\/var\/log\/([\w\_]+).LOG'' as serviceName | parse-regexp caller, ''\w+/([\w\.]+):(\d+)'' as fileName, fileNo | project-rename serviceHost="__tag__:__hostname__" | extend scheduleType = json_extract_scalar(schedule, ''$.type'') | project httpCode, errorCode,errorMessage,requestID,fileName, fileNo, serviceHost,scheduleType,project'
  );
  • 其中 ${ak},${sk},${project},${logstore} 需要替換為有消費許可權的 AK 賬號。
  • query 欄位,替換為上述 SPL,注意在阿里雲 Flink 控制檯需要對單引號使用單引號轉義,並且消除換行符。
  • SPL 最終得到的欄位列表與 TABLE 中欄位對應。

連續查詢及效果

在作業中輸入分析語句,檢視結果資料:

SELECT * FROM sls_input_complex

點選右上角除錯按鈕,進行除錯,可以看到 TABLE 中每一列的值,對應 SPL 處理後的結果。

圖片

總結

為了適應弱結構化日誌資料的需求,Flink SLS Connector 進行了升級,支援直接透過 Connector配置 SPL 的方式實現 SLS 資料來源的清洗下推,特別是需要正則欄位提取、JSON 欄位提取、CSV 欄位提取場景下,相較原資料加工方案和原 Flink SLS Connector 方案更輕量級,讓資料清洗的職責更加清晰,在資料來源端完成資料清洗工作,也可以減少資料的網路傳輸流量,使得到達 Flink 的資料已經是規整好的資料,可以更加專注在 Flink 中進行業務資料分析。

同時為了便於 SPL 驗證測試,SLS 掃描查詢也已支援使用 SPL 進行查詢,可以實時看到 SPL 管道式語法執行結果。

參考連結:

[1] 日誌服務概述

https://help.aliyun.com/zh/sls/product-overview/what-is-log-s...

[2] SPL 概述

https://help.aliyun.com/zh/sls/user-guide/spl-overview

[3] 阿里雲 Flink Connector SLS

https://help.aliyun.com/zh/flink/developer-reference/log-serv...

[4] SLS 掃描查詢

https://help.aliyun.com/zh/sls/user-guide/scan-based-query-ov...


參與體驗贏驚喜大會門票

通義靈碼,是阿里雲出品的一款基於通義大模型的智慧編碼輔助工具,提供行級/函式級實時續寫、自然語言生成程式碼、單元測試生成、程式碼最佳化、註釋生成、程式碼解釋、研發智慧問答、異常報錯排查等能力,並針對阿里雲的雲服務使用場景調優,助力開發者高效、流暢的編碼。

官網連結:https://tongyi.aliyun.com/lingma

本次ArchSummit 架構師峰會期間,通義靈碼聯合InfoQ 策劃發起 AI程式設計體驗活動,儲存小程式卡片,微信掃碼進入小程式,參與通義靈碼體驗抽獎活動,有機會贏全球架構師峰會專屬免費門票(票價5440元)

image.png

相關文章