iT邦幫忙

2021 iThome 鐵人賽

DAY 23
2
DevOps

喬叔帶你上手 Elastic Stack - 探索與實踐 Observability系列 第 23

23 - 建立結構化的 Log (1/4) - Elastic Common Schema 結構化 Log 的規範

這系列文章有在我的網站整理成 GitBook 的格式,較易閱讀,提供大家參考。


建立結構化的 Log 系列文章


本篇學習重點

  • Elastic Common Schema (ECS) 的基本介紹
  • Elastic Common Schema 的設計重點

Elastic Stack 廣泛的被使用在收集 Logs、Metrics、Traces、Uptime 等資料,其中一個最大的目的,就是為了讓散落在各處的資料,能集中化的收集,可以更輕易的存取這些資料,而當資料收集在一起的時候,混亂的格式,就會是遇到的下一個問題,Elastic Common Schema (ECS) 就是被設計出來解決這件事。

什麼是 Elastic Common Schema (ECS)

Elastic Common Schema (ECS) 是一個規範 (Specification),同時這個規範也是 Open Source 的,是在 Elastic 使用者社群支持之下所開發的,主要定義了存放在 Elasticsearch 之中的 Event (事件) 類型資料常用的欄位,這些欄位的型態、描述、使用與呈現方式,而所謂的 Events 即包含就像是 Logs 或是 Metrics 這樣的資料。

ECS 設計的目的,是為了鼓勵使用 Elasticsearch 存放 Event 類型資料的使用者們,能夠將這些 event 的資料『正規化』,透過正規化之後的資料,在後續的資料分析、資料視覺化呈現、顯示與 event 有關聯的資訊上都能更容易的使用。

ECS 的規範橫跨了以下的範圍:

  • Event Sources (事件來源): Event 資料的來源,不論是 Elastic 的產品、第三方的產品或工具、甚至是使用者自己開發的應用程式。
  • Ingestion Architectures (資料注入的架構): 資料注入 (Ingestion) 的架構,不論有沒有使用 Beats、Logstash、Elasticseach 的 Ingest Pipeline,都有包含在內。
  • Consumers (使用端): 不論是透過 API 查出資料、Kibana Query、Dashboard、應用程式等方式來使用。

Elastic Common Schema 能做到什麼事

讓查詢簡單化

透過資料正規化,讓查詢可以變得簡單,舉例來說,一般大型架構中,可能有各種的服務元件、第三方產品或工具,每個產生的 Logs 的格式都不同,同樣是針對 IP 的地址,欄位都不一樣,在沒有正規化之前,為了要從 srcclient_ipapache.access.remote_ipcontext.user.ip 等各種服務所定義的 IP 欄位查詢是否有存在 10.42.42.42 這個地址,KQL (Kibana Query Language) 的查詢會長成這樣:

src:10.42.42.42 OR client_ip:10.42.42.42 OR apache.access.remote_ip:10.42.42.42 OR
context.user.ip:10.42.42.42 OR src_ip:10.42.42.42

但透過正規化之後,會將這些欄位全部存放至 source.ip 的欄位,查詢就變成:

source.ip:10.42.42.42

簡單,能加快查詢的速度,也能減少犯錯的機會。

統一的視覺化呈現

透過資料正規化,在製作圖表,讓資料以視覺化方式來呈現時,也會變得更簡單,而且在分析上也有更好的能力,例如透過同一個 IP 的位置,在同一個欄位之中,輕易的就能將各種來源 (如:Web Server、IDS/IPS 裝置、防火牆) 所收集到的資料,透過圖表呈現出時間歷程中的變化,又或是能將資料要進行深入的分析時的資料呈現方式,像是樞紐分析。

原始資料的轉換

在 Data Ingestion Pipeline 的過程之中,能將原始的資料透過 ECS 的定義,轉換成正規化之後的結果,同時 Elastic Stack 中已經在 Beats、Logstash 等 Data Ingest 的工具,實作了許多第三方產品及工具的整合模組,能將這些 Logs 的格式轉換到 ECS 之中。

例如 Apache Logs 原始的內容如下:

10.42.42.42 - - [15/Jul/2020:20:48:32 +0000] "GET /content HTTP/1.1" 200 2571 "-"
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/83.0.4103.106 Safari/537.36"

首先會將這些原始的內容轉換到 ECS 定義的欄位之中:

Field Name Value
@timestamp 2020-07-15T20:48:32.000Z
event.original 10.42.42.42 - - [15/Jul/2020:20:48:32 +0000] "GET /content HTTP/1.1" 200 2571 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36
http.request.method GET
http.response.body.bytes 2571
http.response.status_code 200
http.version 1.1
message GET /content HTTP/1.1" 200 2571 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36
source.address 10.42.42.42
source.ip 10.42.42.42
url.original /content
user_agent.original Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36

除此之外,ECS 還有定義其他的欄位,也是在轉換過程中會增加的資訊:

  • ecs.version:ECS 的版本
  • event.datasetevent.module:記錄這個 event 是從哪裡來的,是透過哪個模組所處理的。
  • event.kindevent.cateogryevent.typeevent.outcome:包含 ECS Categorization Fields (分類欄位),透過定義好的類別,描述這個 event 是什麼樣的 event。

Elastic Common Schema 的設計重點

ECS 定義了一些準則與最佳實踐的規範,接下來我們將介紹這些規範。

ECS Guideline (凖則)

ECS 欄位類型

ECS 的欄位定義成以下兩種:

  • Core Fields (核心欄位): 指適用於大部份的主要使用情境的欄位,這些欄位也就會在資料分析時、或是製作圖表時,廣泛的被使用在橫跨各種情境的設計之中,這部份的欄位會是比較固定的,不太會隨時間變化。
  • Extended Fileds (延伸欄位): 只要不存在 Core Fields 的欄位,就是 Extended Fields,這些欄位只有在特定的使用情境中才會使用,並且隨著時間也較容易發生變化。

ECS 一般準則

  • 每個文件必須包含 @timestamp 欄位。
  • 需依照 Elasticsearch 的資料型態來定義每個 ECS 的欄位。
  • 必須記錄所使用的 ECS 版本號在 ecs.version 的欄位之中。
  • 盡可能的將資料對應到 ECS 已定義的欄位之中。

欄位名稱的準則

  • 欄位名必須是小寫
  • 字與字之間使用底線來連接。
  • 除了底線之外,不能使用其他的特殊字元
  • 英文的文法請使用現在式,除非欄位是用來記錄歷史的資料。
  • 正確的使用文法的單、複數來反映欄位的內容。
  • 除了最基礎的欄位之外,所有的欄位應該都要加上前輟 (Prefix),例如:host 相關的欄位,都要加上 host. 的前綴,當成是 host 類的欄位集 (field sets),進行分類管理。
  • 巢狀的資料結構 (Nested JSON objects) 也要使用欄位集 (field sets) 進行分類,並使用 . 而不是使用 _ 來描述。
  • 盡量具體的管理及分類欄位,如果能具體的安排在某個欄位集 (field sets) 之中,就不要讓他是在一般化的欄位裡,例如 host.name 就不要去一般化變成 name,這樣太一般化會導致使用時不知如何解讀,甚至會發生許多衝突。
  • 避免命名時單字重覆,例如 host.host_ip 應該取名 host.ip,不過也有例外,如果 hostname 就是一個一般認知的名字,host.hostname 就應該保留使用 hostname,不要刻意改掉。
  • 盡可能的避免縮寫,為了讓解讀時,清楚的知道欄位的用途,不過也可以有例外,如果縮寫已經非常普遍時,例如 ipgeoos 等。

ECS Convension (公約)

整數的數值型態

除非有特別的備註,否則所有的整數的數值型態,應該定義成 long

IDs 或是某些編碼 (codes) 應該使用 keywords,而不是數值型態

IDs 指的是識別字串,例如 User IdProduct Id,而 Codes 指的是編碼,例如 Error Code,這些都應該使用 keyword 的資料型態。

不過有一些特別的 Codes 只要是大家都共識為數字的,就應該使用數字類型,例如 HTTP Status Code,這個就應該使用數字。

字串的預設型態

Elasticsearch 預設的 Dynamic Mapping 會將文字類型的欄位,指定成 text 的資料型態,並且包含 keyword 的子欄位。

這部份 ECS 和 Elasticsearch 相反,預設的文字類型欄位,會指定成 keyword 的資料型態,而另外定義子欄位 text

原因是 ECS 處理的資料大部份都是 Logs 與 Metrics,在這樣的應用情境中,大部份的文字欄位都會較適用 keyword 的方式來處理,才能支援較快速的完整比對、Aggregation、Sorting、prefix search…等。

不過也有例外,就是 Logs 當中一般會存放大量文字,要用來做全文檢索的 messageerror.message 欄位,這兩個欄位預設就是指定 text 並且不會另外宣告 keyword 的子欄位。

客製化欄位

在實務的使用上,我們往往會因為實際的需求與情境,會要在結構化的 Logs 之中定義自己的欄位,由於 ECS 還持續在發展中,以下會有一些自訂欄位的使用建議,減少與未來 ECS 新版本發生衝突的機會。

使用 labels 欄位

一些簡單的 keyword 類型的資料,可以直接定義在 labels 欄位之中,例如:

{ "labels": { "foo_id": "beef42", "env": "production" },
  "message": "...",
  "event": { ... }
}

labels 裡的定義,就是完全依照使用者自己來管理。

了解 ECS 的命名方式

ECS 在命名時,會使盡量使用概念的名字,而不是工具的名字或是專案的名字,一般在資料的整理時,就會先使用通用的方式來歸類,剩下的才會用特定方式來描述,例如 HAProxy 的 log,屬於 HTTP 相關的資訊,就會先定義在 httpurl 的 field sets 之中,而剩下的才會放在 haproxy 裡:

{ "http": { "request": { "method": "get", ... },
            "response": { "status_code": 200, ... } },
  "url": { "original": "/favicon.ico", ... },
  "haproxy": { "frontend_name": "myfrontend", "backend_name": "mybackend_prod",
               "backend_queue": 0, ... }
}

使用大寫

如果真的要避免與未來的 ECS 版本發生衝突,有一個做法,雖然醜醜的,但是可以考慮,就是打破 ECS 的命名規則,使用大寫開頭的方式來命名欄位:

{ "http": { "request": { "method": "get", ... } },
  "url": { "original": "/favicon.ico", ... },
  "Proxy": { "FrontendName": "myfrontend", "BackendName": "mybackend_prod" },
  "event": { "module": "haproxy" }
}

在上述官方文件提出的例子,Proxy 就是一個自訂的欄位,並且為了避免未來與 proxy 這樣的 ECS 欄位發生衝突,所以使用大寫開頭。

備註:我自己是覺得這部份有點醜,如果真的要使用,以上述的子來說,會命名成 Proxy,一定是因為想要在自己的領域中定義一個通用的欄位,而剛好這個欄位還沒有被定義在 ECS 之中,如果真的是夠通用,可以接受未來 ECS 推出時再進行轉換,可以這樣考慮,否則是自己領域當中的應用的欄位定義時,最好還是能定義更明確的名字,避免發生衝突。

如何使用 ECS

基本上若是使用我們這系列文章前面介紹到的 Heartbeat、Metricbeat、Filebeat 當中的各種 Modules,所收集到的資料就已經是依照 ECS 的格式存入 Elasticsearch 當中,若是有需要增加客製的欄位用,可以使用先前介紹過 Beats 裡的 properties 來進行設定,以下會先針對 ECS 產生的欄位進行簡介,若是要想查看 Beats 實際產生哪些 ECS 的欄位,可以直接使用 _search 或是 Kibana Discover 的功能來查看。

ECS 的欄位定義

ECS 欄位的參考,可以參考 官方文件 - ECS Field Reference 裡的定義,包含非常多已經收錄的各種欄位集 (field sets),這部份在這邊就不細部說明。

若是想要一覽 ECS 所有欄位的總表的話,可以查看 ECS GitHub - fields.csv [2]。

ECS Categorization 欄位

ECS Categorization (分類) 欄位,目的是透過一組事先定義的值,用來描述這個欄位是什麼樣的欄位,其中包含了:

  • event.kind:用來描述這個 event 包含什麼樣的資訊,可使用的值有 alertenrichementeventmetricstatepipeline_errorsignal
  • event.category:定義了 ECS 中的主要分類,可使用的值包含 authenticationconfigurationdatabasedriverfilehostiamintrusion_detectionmalwarenetworkpackageprocessregistrysessionthreatweb
  • event.type:這裡面定義的,是基於 cateogry 底下的子分類,可使用的值包含 accessadminallowedchangeconnectioncreationdeletiondeniedenderrorgroupindicatorinfoinstallationprotocolstartuser
  • event.outcome:這是定義 event 代表的狀態,可使用的值包含 failuresuccesunknown

透過這些事先定義好的分類,可以協助我們將 events 有效的正規劃,每個欄位可設定的值的說明,可以參考 官方文件 - ECS Categorization Field 的細節說明。


這篇文章所介紹的 Elastic Common Schema,除了讓我們了解 ECS 的能力以及裡面所包含的定義,更重要的一個參考價值,就是 Elastic 發展出這份 Common Schema 的設計重點,相信在許多領域之中,也應該會有類似定義領域內通用 Schema 的需求,這裡所介紹的做法就非常值得參考。

參考資料

  1. 官方文件 - Elastic Common Schema
  2. ECS GitHub - fields.csv

查看最新 Elasticsearch 或是 Elastic Stack 教育訓練資訊: https://training.onedoggo.com
歡迎追蹤我的 FB 粉絲頁: 喬叔 - Elastic Stack 技術交流
不論是技術分享的文章、公開線上分享、或是實體課程資訊,都會在粉絲頁通知大家哦!


上一篇
22 - Traces - 觀察應用程式的效能瓶頸 (6/6) - 透過真實使用者監控 RUM 來改善使用者體驗
下一篇
24 - 建立結構化的 Log (2/4) - Elasticsearch Ingest Pipeline 資料 Index 前的轉換好幫手 - 基本介紹
系列文
喬叔帶你上手 Elastic Stack - 探索與實踐 Observability31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言