23 - 建立结构化的 Log (1/4) - Elastic Common Schema 结构化 Log 的规范

建立结构化的 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 技术交流
不论是技术分享的文章、公开线上分享、或是实体课程资讯,都会在粉丝页通知大家哦!


<<:  D3JsDay23 三枪侠的电磁炮,三个变数的气泡—气泡图(上)

>>:  Day24:今天我们来聊一下Azure Sentinel中使用的关注清单

Swift 新手-打造第一个 iOS App

开发前的新手纠结 商学院出身,非资工背景,团队内也没有熟悉 app 开发的人才 决定做 app 接触...

第六章 之五

OK,说好本章结要来说一下页面,那就说一下页面能干嘛,首先要提到的是有页面功能又有文章功能,同时都可...

[Day - 26] - Spring Swagger之我的SeaFood API 手册配制方法

Abstract 我们前面讲了许多Spring应用开发,但当我们开发好一套系统,势必要有一套API手...

[Day 6] 蒐集对话经验

自前两天范例中,我们看到受众目标与假想使用者之重要性。 现在,我们能设身处地的以使用者的角度来设计...

{DAY 1}开始吧!探索data世界

目标 主题是【从资料库到资料分析视觉化】, 希望可以更深入的了解data, 从资料库的架构,资料的...