全部產品
Search
文件中心

Simple Log Service:通用參考

更新時間:Jun 30, 2024

本文介紹SPL在各種情境的不同用法。

SPL在不同情境的功能定義

Log Service在不同情境中使用SPL,其功能定義存在差異,細節如下:

SPL功能的入口

Logstore索引過濾結果作為輸入

欄位名大小寫敏感

全文欄位__line__

即時消費

  • 調用PullData API消費資料,比如自訂消費組、Flink計算等

  • 使用Kafka協議消費Logstore

不支援。使用星號(*)表示Logstore全部未經處理資料作為輸入。比如

* | where msg like '%wrong%'

敏感

不支援

掃描查詢

  • 調用GetLogs API查詢資料

  • 控制台Scan模式互動式查詢資料

支援。先執行索引過濾,過濾結果再執行SPL處理。比如

error and msg:wrong | project level, msg

不敏感

支援

Logtail採集

Logtail採集配置

不支援。使用星號(*)表示Logtail採集的全部未經處理資料作為輸入,比如

* | parse-json content

敏感

不支援

特殊欄位處理

時間欄位

在SPL執行過程中,SLS日誌時間欄位類型始終保持為數實值型別INTEGER或者BIGINT。SLS日誌欄位包括資料時間戳記欄位__time__和資料時間納秒部分欄位__time_ns_part__

如需更新資料時間,須使用extend指令操作,且確保新實值型別為INTEGER或者BIGINT。其他指令均不可操作時間欄位,其行為如下:

  • project、project-away、project-rename:預設保留時間欄位,不可將其重新命名,也不可將其覆蓋。

  • parse-regexp、parse-json:如果提取結果包含時間欄位,則將其忽略。

樣本

從已有的時間字串中提取時間欄位值。

  • SPL語句

    * 
    | parse-regexp time, '([\d\-\s:]+)\.(\d+)' as ts, ms
    | extend ts=date_parse(ts, '%Y-%m-%d %H:%i:%S')
    | extend __time__=cast(to_unixtime(time_s) as INTEGER)
    | extend __time_ns_part__=cast(ms as INTEGER) * 1000000
    | project-away ts, ms
  • 輸入資料

    time: '2023-11-11 01:23:45.678'
  • 輸出結果

    __time__: 1699637025
    __time_ns_part__: 678000000
    time: '2023-11-11 01:23:45.678'

欄位名包含特殊字元

如果日誌中出現帶空格或者特殊字元的欄位,可以通過加雙引號的方式來引用。例如日誌中有欄位名為A B,其中包含空格,

那麼在SPL中可以通過"A B"來使用。使用例子如下:

* | where "A B" like '%error%'

欄位名大小寫不敏感

在SLS掃描查詢中,使用SPL時,SPL指令中引用的欄位名大小寫是不敏感的。例如日誌中欄位名為Method,在SPL中可以通過methodMETHOD等來過濾該欄位。

重要

涉及Log Service掃描查詢功能。更多資訊,請參見掃描(Scan)查詢

樣本

where中使用大小寫不敏感欄位名。

  • SPL語句

    * | where METHOD like 'Post%'
  • 輸入資料

    Method: 'PostLogstoreLogs'
  • 輸出結果

    Method: 'PostLogstoreLogs'

欄位名稱衝突處理

在日誌上傳或者SPL運行期間,基於大小寫敏感的處理可能會涉及欄位名的衝突,如原始日誌中同時存在Method和method欄位名;針對不同的情境SPL會使用不同的方式進列欄位衝突解決。

為了避免以下情況,建議在原始日誌中規範輸入的欄位。

輸入資料中存在衝突

在原始日誌中包含大小寫不敏感重複的欄位時,如某一條日誌同時存在Statusstatus兩個欄位,SPL會隨機選取其中一個欄位作為輸入,捨棄另一列;舉例如下:

  • SPL語句

    * | extend status_cast = cast(status as bigint)
  • 輸入資料

    Status: '200'
    status: '404'
  • 處理結果

    • 第一種可能結果,保留Status欄位值

      Status: '200' -- 保留第1列,捨棄第2列
      status_cast: '200'
    • 第二種可能結果,保留status欄位值

      status: '404' -- 保留第2列,捨棄第1列
      Status_cast: '404'

運行結果中存在衝突

情境1:未經處理資料欄位衝突

在SPL運行過程中,可能產生大小寫不敏感的同名的欄位,這種情況SPL會隨機播放其中一列作為輸出;例如日誌欄位中包含一個JSON字串類型的欄位,在使用parse-json的過程中,可能將同名欄位暴露出來,舉例如下:

  • SPL語句

    * | parse-json content
  • 輸入資料

    content: '{"Method": "PostLogs", "method": "GetLogs", "status": "200"}'
  • 輸出結果

    • 第一種可能結果,保留Method欄位

      content: '{"Method": "PostLogs", "method": "GetLogs", "status": "200"}'
      Method: 'PostLogs' -- 保留Method
      status: '200'
    • 第二種可能結果,保留method欄位

      content: '{"Method": "PostLogs", "method": "GetLogs", "status": "200"}'
      method: 'GetLogs' -- 保留method
      status: '200'

情境2:新產生資料欄位衝突:

為了避免歧義,對於SPL指令中產生的明確的新欄位名,此類指令包括extend的欄位名和parse-regexpparse-csvas明確指定的欄位名,SPL的輸出結果仍會保持新欄位名大小寫。

舉例:extend一個新欄位Method,結果欄位名仍會保持Method的大小寫。

  • SPL語句

    * | extend Method = 'Post'
  • 輸入資料

    Status: '200'
  • 輸出結果

    Status: '200'
    Method: 'Post'

SLS保留欄位衝突處理

重要

涉及Log Service的即時消費和掃描查詢功能。

Log Service完整保留欄位列表請參見保留欄位。SPL讀取儲存在SLS的LogGroup結構資料作為輸入(LogGroup定義詳情請參見資料編碼方式),如果原始寫入Log Service的資料不符合標準LogGroup編碼規範,即某些保留欄位並未按照標準編碼在對應位置,而是放在LogContent中,則SPL在讀取此類保留欄位策略如下:

  • 對於__source____topic____time____time_ns_part__欄位,SPL會從標準LogGroup編碼結果中讀取值,忽略同名的LogContent欄位。

  • 對於以__tag__: 為首碼的Tag欄位,SPL將優先從標準LogGroup編碼結果中讀取值,未取到再從LogContent中取值。例如對於欄位__tag__:ip,優先從LogTag列表中讀取key為ip的欄位,如果不存在,再從LogContent中的自訂日誌欄位中讀取key為__tag__:ip的 Log 欄位。

全文欄位__line__

重要

涉及SLS掃描查詢功能。

如果在控制台或者GetLogstoreLogs介面中想對原始日誌進行過濾,可以使用__line__欄位。

樣本

  • 在日誌中搜尋關鍵詞error。

* | where __line__ like '%error%'
  • 如果日誌中有欄位名為__line__,需要使用反引號包裹,即 `__line__`來引用日誌中的欄位。

* | where `__line__` ='20'

新舊值保留與覆蓋

在SPL指令執行過程中,其輸出的目標欄位與輸入資料中已有欄位重名時,該欄位的取值策略如下:

重要

欄位值保留與覆蓋策略與extend指令無關,extend指令的重名欄位取值策略為直接使用新值。

新舊實值型別不一致

直接保留輸入欄位原始值。

樣本

  • 樣本1:project重新命名欄位重名。

    • SPL語句

      * 
      | extend status=cast(status as BIGINT) -- status類型轉為BIGINT
      | project code=status -- code舊實值型別為VARCHAR,新值為BIGINT,直接保留舊值
    • 輸入資料

      status: '200'
      code: 'Success'
    • 輸出結果

      code: 'Success'
  • 樣本2:parse-json提取欄位重名。

    • SPL語句

      * 
      | extend status=cast(status as BIGINT) -- status類型轉為BIGINT
      | parse-json content -- status舊實值型別為BIGINT,新值為VARCHAR,直接保留舊值
    • 輸入資料

      status: '200'
      content: '{"status": "Success", "body": "this is test"}'
    • 輸出結果

      content: '{"status": "Success", "body": "this is test"}'
      status: 200
      body: 'this is test'

新舊實值型別一致

如果輸入值為null,直接使用新值填充。否則,由指令中指定的mode參數確定,定義如下表。

重要

如果指令沒有定義mode參數,則其預設值為overwrite

模式

說明

overwrite

使用新值覆蓋舊值作為欄位值。

preserve

保留舊值作為欄位值,捨棄新值。

樣本

  • 樣本1:project重新命名欄位重名,且類型相同,mode預設值為overwrite。

    • SPL語句

    * | project code=status -- code新舊實值型別均為VARCHAR,根據overwrite引用新值
    • 輸入資料

      status: '200'
      code: 'Success'
    • 輸出結果

      code: '200'
  • 樣本2:parse-json提取欄位重名,且類型相同,mode預設值為overwrite。

    • SPL語句

      * | parse-json content -- status新舊實值型別均為VARCHAR,根據overwrite引用新值
    • 輸入資料

      status: '200'
      content: '{"status": "Success", "body": "this is test"}'
    • 輸出結果

      content: '{"status": "Success", "body": "this is test"}'
      status: 'Success'
      body: 'this is test'
  • 樣本3:parse-json提取欄位重名,且類型相同,mode指定為preserve。

    • SPL語句

      * | parse-json -mode='preserve' content -- status新舊實值型別均為VARCHAR,根據preserve保留舊值
    • 輸入資料

      status: '200'
      content: '{"status": "Success", "body": "this is test"}'
    • 輸出結果

      content: '{"status": "Success", "body": "this is test"}'
      status: '200'
      body: 'this is test'

資料類型轉換

初始類型

除日誌時間欄位外,資料處理SPL的輸入欄位的初始資料類型均為VARCHAR。在後續的處理邏輯中,如果涉及到強資料類型時,需要進行資料類型轉換。

樣本

篩選出狀態代碼為5xx的訪問日誌時,需要將status欄位的類型轉為BIGINT之後再進行比較。

* -- status欄位的初始類型為VARCHAR
| where cast(status as BIGINT) >= 500 -- 將status欄位的類型轉為BIGINT,再進行比較

類型保持

在SPL處理資料過程中,使用extend指令對欄位進行資料類型轉換後,後續的處理邏輯將沿用轉換後的資料類型。

樣本

* -- Logstore作為輸入資料,除時間欄位外,所有欄位初始類型為VARCHAR
| where __source__='127.0.0.1' -- 對__source__欄位進行過濾
| extent status=cast(status as BIGINT) -- 將status欄位的類型轉為BIGINT
| project status, content
| where status>=500 -- status欄位的類型保持為BIGINT,可以直接與數字500做比較

SPL運算式null值處理

產生null值

SPL處理資料過程中,如下兩個情境將產生null值:

  1. SPL運算式中使用到的欄位在輸入資料中不存在時,則將其值視為null值進行計算。

  2. SPL運算式計算過程中出現異常,其計算結果即為null值。比如cast類型轉換失敗、數組越界等。

樣本

  1. 欄位不存在時,計算代入null值。

  • SPL語句

  • * | extend withoutStatus=(status is null)
  • 輸入資料

  • # 條目1
    status: '200'
    code: 'Success'
    
    # 條目2
    code: 'Success'
  • 輸出結果

  • # 條目1
    status: '200'
    code: 'Success'
    withoutStatus: false
    
    # 條目2
    code: 'Success'
    withoutStatus: true
  1. 計算過程異常,計算結果為null值。

  • SPL語句

    *
    | extend code=cast(code as BIGINT) -- code欄位轉為BIGINT失敗
    | extend values=json_parse(values)
    | extend values=cast(values as ARRAY(BIGINT))
    | extend last=arr[10] -- 數組越界
  • 輸入資料

    status: '200'
    code: 'Success'
    values: '[1,2,3]'
  • 輸出結果

    status: '200'
    code: null
    values: [1, 2, 3]
    last: null

消除null值

為了消除計算過程中的null值,需使用COALESCE運算式將多個值按優先順序聯合,擷取第一個非null值作為最終計算結果。在所有運算式計算結果都為null時,也可以設定最終預設值。

樣本

讀取數組最後一個元素,如果數組為空白,預設值為0。

  • SPL語句

    *
    | extend values=json_parse(values)
    | extend values=cast(values as ARRAY(BIGINT))
    | extend last=COALESCE(values[3], values[2], values[1], 0)
  • 輸入資料

    # 條目1
    values: '[1, 2, 3]'
    
    # 條目2
    values: '[]'
  • 輸出結果

    # 條目1
    values: [1, 2, 3]
    last: 3
    
    # 條目2
    values: []
    last: 0

錯誤處理

語法錯誤

SPL語法錯誤指使用者在編寫SPL語句時不符合文法結構,比如指令名稱錯誤、關鍵詞引用錯誤、類型設定錯誤等,SPL語法錯誤發生時,SPL不會對資料進行處理,需要根據報錯,修改對應的錯誤。

資料錯誤

資料錯誤,指在SPL啟動並執行過程中,函數或者轉換出現錯誤,SPL會將結果欄位置為null值,由於每一行資料都有可能出現錯誤,SPL會隨機採樣部分錯誤返回,可以根據實際資料內容忽略或者修改SPL語句。

資料錯誤不會影響SPL整個執行過程,SPL語句仍會返回處理的結果,出錯的欄位的值為null值。可以根據實際情況忽略此類錯誤。

運行逾時

SPL語句中包含不同的指令,不同的指令在不同的資料情境下消耗的時間不同。當SPL整個語句的執行時間超過預設逾時時間後(預設逾時時間在Scan查詢、即時消費、Logtail採集可能有所不同),SPL語句會停止執行,並返回逾時錯誤,這種情況下SPL語句執行得到的結果為空白。

遇到此類錯誤,建議調整SPL語句,降低語句的複雜度(例如Regex)和管道數。

記憶體超限

SPL語句中包含不同的指令,不同的指令在不同的資料情境下消耗的記憶體不同,SPL語句執行時會限制一定的記憶體Quota(預設記憶體Quota在Scan查詢、即時消費、Logtail採集可能有所不同),超過記憶體Quota後,SPL會執行失敗,並返回記憶體超限錯誤,這種情況下SPL語句執行得到的結果為空白。

遇到此類錯誤,建議調整SPL語句,降低語句的複雜度和通道數,並查看未經處理資料是否過大。