前言
OpenTelemetry作为目前可观察性的标准方案,近年来的发展速度非常快,目前OpenTelemetry Trace规范v1.0版本已经发布,Metrics v1.0也将在几个月后发布。相对最慢的当属Log规范,但在众多公司的参与下,Log规范的第一个版本已经在半年前发布,且经过了几次更新,也在朝着v1.0的方向快速前进。
本文主要介绍OpenTelemetry Log规范,这一规范来自于Google、Microsoft、AWS、Splunk、DataDog、ES、Fluntd等众多优秀的公司和项目成员,其中有很多点是我们在平时开发、运维需要关注的知识和经验,值得大家一观。
初衷&宗旨
官方定义的宗旨如下:
- 日志模型能够表达各种来源的日志:应用程序、机器事件、系统日志、容器日志等。
- 能够将现有的大部分日志格式映射到日志模型,并且日志模型也可以很容易的转换成各种日志格式。
- 通过日志数据模型和语义的定义,能够指导日志系统如何记录、传输、存储和理解日志。
从OpenTelemetry的顶层宗旨来看,日志模型的定义最主要还是规范化,能够将Metrics、Tracing、Logging的common schema统一,这样可以做到3者的无缝打通。当然,为了尽可能的通用,模型的定义会参考众多的日志格式,表达信息的方式需要尽肯能灵活。
日志架构演进
传统架构下Logs、Traces、Metrics都是单独产生并收集的,在OpenTelemetry的规划中,所有的数据都会通过OpenTelemetry的Collector进行采集并传输到统一的后端进行关联。好处非常明显:
- 应用只需要一个SDK就可以做所有可观测性上的事情,依赖更少、资源消耗也更小
- 采集端只需要一个,部署和运维代价更低
- 数据格式统一,数据关联更容易
上面的图展示的是终极目标,但目前OpenTelemetry Collector对于Log的支持还比较弱,估计未来1-2年还是要用其他的日志采集去支撑。
特性
- LogModel(日志模型)应该支持任意类型的日志能够明确的转换成该类型,对于两种相同含义不同格式的日志,转换到LogModel后应该是完全等价的。
- 从其他日志类型映射到LogModel是有一定意义的,LogModel必须能够表示出其他日志类型的语义。
- 将类型A的日志换成到LogModel,然后再由LogModel转换到类型B。这种场景应该和从类型A直接转换到类型B是一致的,即转换过程中不应该丢失/增加数据。
- LogModel在传输和存储的效率上需尽可能的高,主要是对于CPU和内存的使用尽可能低,即序列化/反序列化尽可能高效以及存储空间尽可能低。
从表达能力上看,LogModel需要能够表达至少以下3种的日志/Event:
- 系统日志:即操作系统/硬件产生的日志,一个最典型的代表是Syslog。
- 三方应用日志:一些流行的三方软件的日志格式,例如Apache日志、MySQL慢日志等。
- 应用日志:业务应用产生的日志,这些日志一般由程序员打印,在需要的时候可以适当修改源码来适应新的LogModel。
字段类型
LogModel只定义日志(Record)的逻辑表现形式,与具体的物理格式和编码形式无关,每个Record包含两种字段类型:
- 具有特定类型和含义的*字段(Top-Level)
- 具体的字段,通常以KeyValue对的形式,根据不同的Top-Level名称,对应的字段会有不同的类型
LogModel定义
字段名 | 描述 | 必选 |
---|---|---|
Timestamp | 日志时间戳 | 是 |
TraceId | 关联请求的TraceId | 否 |
SpanId | 关联请求的SpanId | 否 |
TraceFlags | W3C trace flag. | 否 |
SeverityText | 日志等级的可读描述. | 否 |
SeverityNumber | 日志等级. | 否 |
ShortName | 用于标识日志类型的短语. | 否 |
Body | 具体的日志内容. | 否 |
Resource | Log关联的Resource. | 否 |
Attributes | 额外关联属性. | 否 |
字段详细解释
Timestamp
uint64,纳秒
TraceId
字节数组,具体可以参考:W3C Trace Context
SpanId
字节数组,如果SpanId出现,则TraceId必须出现。
TraceFlags
单字节,具体可参考:W3C Trace Context
SeverityText
日志等级的可读描述,如果不设置,则按照SeverityNumber
的默认Mapping规则映射。
SeverityNumber
SeverityNumber
和Syslog中的日志等级比较像,OpenTelemetry定义了6大类、24种日志等级,基本上可以包含所有类型日志的等级定义。
SeverityNumber range | 通用等级 | 含义 |
1-4 | TRACE | A fine-grained debugging event. Typically disabled in default configurations. |
5-8 | DEBUG | A debugging event. |
9-12 | INFO | An informational event. Indicates that an event happened. |
13-16 | WARN | A warning event. Not an error but is likely more important than an informational event. |
17-20 | ERROR | An error event. Something went wrong. |
21-24 | FATAL | A fatal error such as application or system crash. |
SeverityNumber
和SeverityText
可通过Mapping的方式自动映射,所以在日志产生的时候,可以不填写SeverityText
字段,以介绍序列化/反序列化和传输代价。映射关系如下:
SeverityNumber | Short Name |
---|---|
1 | TRACE |
2 | TRACE2 |
3 | TRACE3 |
4 | TRACE4 |
5 | DEBUG |
6 | DEBUG2 |
7 | DEBUG3 |
8 | DEBUG4 |
9 | INFO |
10 | INFO2 |
11 | INFO3 |
12 | INFO4 |
13 | WARN |
14 | WARN2 |
15 | WARN3 |
16 | WARN4 |
17 | ERROR |
18 | ERROR2 |
19 | ERROR3 |
20 | ERROR4 |
21 | FATAL |
22 | FATAL2 |
23 | FATAL3 |
24 | FATAL4 |
ShortName
一般用一个特定的词来标识日志类型,通常不要超过50个字节,例如:ProcessStarted
Body
日志内容为Any类型,Any类型,可以是 int、string、bool、float,也可以是一个数组或是Map。
Resource
Key/Value对列表,参考OpenTelemetry的通用Resource定义。
这里包括主机名、进程号、服务名等信息,可以用于关联Metrics、Tracing。
Attributes
Key/Value对列表,Key始终是 string,Value是Any类型。具体可以参考Tracing中Attributes的定义。
LogModel示例
Example 1
{ "Timestamp": 1586960586000, // JSON needs to make a decision about // how to represent nanoseconds. "Attributes": { "http.status_code": 500, "http.url": "http://example.com", "my.custom.application.tag": "hello", }, "Resource": { "service.name": "donut_shop", "service.version": "semver:2.0.0", "k8s.pod.uid": "1138528c-c36e-11e9-a1a7-42010a800198", }, "TraceId": "f4dbb3edd765f620", // this is a byte sequence // (hex-encoded in JSON) "SpanId": "43222c2d51a7abe3", "SeverityText": "INFO", "SeverityNumber": 9, "Body": "20200415T072306-0700 INFO I like donuts" }
Example 2
{ "Timestamp": 1586960586000, "Body": { "i": "am", "an": "event", "of": { "some": "complexity" } } }
Example 3
{ "Timestamp": 1586960586000, "Attributes":{ "http.scheme":"https", "http.host":"donut.mycie.com", "http.target":"/order", "http.method":"post", "http.status_code":500, "http.flavor":"1.1", "http.user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36", } }
总结
从上述的规范可以看出,OpenTelemetry的Log模型主要追求以下几点:
- 作为Metrics、Traces、Logs三大可观测性数据的终极细节性数据,Log需要能够表达足够详细的信息
- 如果Log在Trace场景中,需要包裹TraceID、SpanID,以便和Traces关联,同时借助Resource信息可以更好的和Traces、Metrics关联
- Log的性能需要尽可能的高,因为日志是数据量最大的可观测性数据,而且不会做采样,一定要确保对应用的性能影响尽可能的小
- 兼容性要好,因为目前有太多的日志系统,且都存在了很久也有很明确的存在意义,所以需要能够保证这些不同类型的Log可以和OpenTelemetry Log进行无缝转换
整体上这个模型还是非常适合现代的IT系统,但想要把这套标准成功实施下来还有很多工作要做,包括日志采集、解析、传输工具,兼容其他很多已有系统、环境等。不过好在Fluentd也在CNCF项目下,未来可能会成为OpenTelemetry的日志采集内核,配合Collector一起工作。
参考
- https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/overview.md
- https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md
- https://www.w3.org/TR/trace-context/#trace-id
- https://docs.datadoghq.com/tracing/connect_logs_and_traces/java/?tab=log4j2
- https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/overview.md#resources
- https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/trace/semantic_conventions