日志数据模型
状态: 稳定
这是一个数据模型和语义约定,用于表示来自各种来源的日志:应用程序日志文件、机器生成的事件、系统日志等。现有日志格式可以无歧义地映射到此数据模型。从该数据模型进行反向映射也是可能的,前提是目标日志格式具有等效的功能。
数据模型的目的是对日志记录的含义、需要由日志系统记录、传输、存储和解释的数据有一个共同的理解。
本提案定义了独立日志的数据模型。
设计注意事项
要求
数据模型的设计旨在满足以下要求:
应能够无歧义地将现有日志格式映射到此数据模型。将日志数据从任意日志格式转换为此数据模型,然后再转换回来,理想情况下应产生相同的数据。
将其他日志格式映射到此数据模型应具有语义意义。数据模型必须保留现有日志格式特定元素的语义。
将日志数据从任意日志格式 A 转换为此数据模型,然后再将数据模型转换为另一个日志格式 B,理想情况下应产生有意义的日志数据转换,其效果不应比直接将日志格式 A 转换为日志格式 B 的合理转换效果差。
应能够有效地在需要存储或传输数据的具体实现中表示数据模型。我们主要关心效率的两个方面:序列化/反序列化的 CPU 使用率和序列化形式的空间需求。这是一个间接的要求,受到数据模型具体表示的影响,而不是数据模型本身,但仍然有必要牢记。
该数据模型旨在成功表示 3 类日志和事件:
系统格式。这些是由操作系统生成的日志和事件,我们无法控制——我们无法更改格式或影响包含哪些信息(除非数据是由我们可以修改的应用程序生成的)。Syslog 是系统格式的一个示例。
第三方应用程序。这些是由第三方应用程序生成的。我们可能对包含哪些信息有一定的控制权,例如自定义格式。Apache 日志文件就是一个例子。
第一方应用程序。这些是我们开发的应用程序,我们对日志和事件的生成方式以及我们在日志中包含哪些信息有一定的控制权。如果需要,我们很可能可以修改应用程序的源代码。
事件
事件是 OpenTelemetry 的日志记录的标准格式。所有为日志定义的语义约定都**应该**格式化为事件。事件格式的要求和详细信息可以在语义约定中找到。
事件旨在由 OpenTelemetry 仪器使用。并非所有日志记录都必须格式化为事件。
本文档使用的定义
在本文档中,我们引用类型 any 和 map<string, any>,定义如下。
类型 any
类型为 any 的值可以是以下之一:
标量值:字符串、布尔值、64 位带符号整数或双精度浮点数 (IEEE 754-1985)
字节数组,
any值的数组(列表),map<string, any>,[自 1.31.0 起] 空值(例如
null)。
类型 map<string, any>
类型为 map<string, any> 的值是字符串键到 any 值的映射。映射中的键是唯一的(不允许重复键)。
允许数组和映射的值进行任意深度嵌套(本质上允许表示等同于 JSON 对象)。
映射的表示是语言相关的。
实现**必须**默认确保导出的映射只包含唯一的键。
实现**可以**提供一个选项来允许导出带有重复键的映射(例如,为了提高性能)。如果提供了此类选项,**必须**说明对于许多接收者来说,处理带有重复键的映射是不可预测的,用户有责任确保键不重复。
字段种类
此数据模型定义了一个日志记录的逻辑模型(与记录的物理格式和编码无关)。每个记录包含 2 种字段:
具有特定类型和含义的命名顶层字段。
存储为
map<string, any>的字段,其中包含任意类型的值。知名字段的键和值遵循键名和可能值的语义约定,允许所有处理该字段的参与方对数据有相同的解释。请参阅Resource和Attributes字段的语义约定参考以及附录 A 中的示例。
拥有这两种字段的原因是:
能够有效地表示命名顶层字段,这些字段几乎总是存在(例如,在使用 Protocol Buffers 等编码时,字段是枚举的,但在传输时没有命名)。
能够强制执行命名字段的类型,这对于具有类型检查的编译型语言非常有用。
灵活地将不常出现的数据表示为
map<string, any>。这包括具有标准化语义的知名数据以及应用程序可能希望包含在日志中的任意自定义数据。
在设计此数据模型时,我们遵循了以下推理过程来决定何时使用顶层命名字段:
字段需要是所有记录的**必需**字段,或者经常出现在知名日志和事件格式(例如
Timestamp)中,或者预计在未来的日志记录系统中经常出现(例如TraceId)。字段的语义对于所有已知的日志和事件格式都必须相同,并且可以被直接且无歧义地映射到此数据模型。
以上两个条件都是让该字段在记录的顶层结构中占有一席之地所必需的。
日志和事件记录定义
附录 A 包含许多示例,展示了现有日志格式如何映射到下面定义的字段。如果对字段的含义有疑问,查看示例可能会有所帮助。
以下是日志记录中字段的列表:
| 字段名称 | 描述 |
|---|---|
| Timestamp | 事件发生的时间。 |
| ObservedTimestamp | 事件被观察到的时间。 |
| TraceId | 请求跟踪 ID。 |
| SpanId | 请求跨度 ID。 |
| TraceFlags | W3C 跟踪标志。 |
| SeverityText | 严重性文本(也称为日志级别)。 |
| SeverityNumber | 严重性的数值。 |
| Body | 日志记录的正文。 |
| Resource | 描述日志的来源。 |
| InstrumentationScope | 描述发出日志的作用域。 |
| Attributes | 有关事件的其他信息。 |
| EventName | 标识事件类别/类型的名称。 |
以下是每个字段的详细描述:
字段:Timestamp
类型:Timestamp,自 Unix 纪元以来的 uint64 纳秒。
描述:事件发生的时间,由原始时钟测量,即源头时间。此字段是可选的,如果源时间戳未知,则可能缺失。
字段:ObservedTimestamp
类型:Timestamp,自 Unix 纪元以来的 uint64 纳秒。
描述:事件被收集系统观察到的时间。对于源自 OpenTelemetry 的事件(例如,使用 OpenTelemetry Logging SDK),此时间戳通常在生成时设置,等于 Timestamp。对于从外部源生成并由 OpenTelemetry 收集的事件(例如,使用 Collector),这是 OpenTelemetry 代码观察到事件的时间,由 OpenTelemetry 代码的时钟测量。一旦事件被 OpenTelemetry 观察到,此字段**应**被设置。
对于将 OpenTelemetry 日志数据转换为仅支持单个时间戳的格式,或当接收者仅在内部支持单个时间戳时接收 OpenTelemetry 日志数据,建议使用以下逻辑:
- 如果
Timestamp存在,则使用它,否则使用ObservedTimestamp。
跟踪上下文字段
字段:TraceId
类型:字节序列。
描述:请求跟踪 ID,如W3C Trace Context中所定义。对于属于请求处理且具有已分配跟踪 ID 的日志,可以设置此字段。此字段是可选的。
字段:SpanId
类型:字节序列。
描述:跨度 ID。对于属于特定处理跨度的日志,可以设置此字段。如果 SpanId 存在,则 TraceId 也**应**存在。此字段是可选的。
字段:TraceFlags
类型:字节。
描述:跟踪标志,如W3C Trace Context规范中所定义。撰写本文时,该规范定义了一个标志——SAMPLED 标志。此字段是可选的。
严重性字段
字段:SeverityText
类型:字符串。
描述:严重性文本(也称为日志级别)。这是严重性的原始字符串表示形式,如在源处所知。如果此字段缺失且 SeverityNumber 存在,则可以将对应于 SeverityNumber 的短名称用作替代。此字段是可选的。
字段:SeverityNumber
类型:数字。
描述:严重性的数值,已标准化为本文档中描述的值。此字段是可选的。
SeverityNumber 是一个整数。数值越小表示事件的严重性越低(例如调试事件),数值越大表示事件的严重性越高(例如错误和严重事件)。
例如,SeverityNumber=17 表示的错误比 SeverityNumber=20 的错误更不严重。
下表定义了 SeverityNumber 值的含义:
| SeverityNumber 范围 | 范围名称 | 含义 |
|---|---|---|
| 1-4 | TRACE | 细粒度的调试事件。通常在默认配置中禁用。 |
| 5-8 | DEBUG | 调试事件。 |
| 9-12 | INFO | 信息性事件。表示发生了某个事件。 |
| 13-16 | WARN | 警告事件。不是错误,但可能比信息性事件更重要。 |
| 17-20 | ERROR | 错误事件。发生了某事。 |
| 21-24 | FATAL | 致命错误,例如应用程序或系统崩溃。 |
SeverityNumber=0**可以**用于表示未指定的值。
SeverityNumber 的映射
现有日志系统和格式的映射(或简称**源格式**)必须定义该特定格式的严重性(或日志级别)如何对应到此数据模型的 SeverityNumber,具体取决于上面表格中每个范围的含义。
如果源格式有多个严重性与此表格中的单个范围匹配,则源格式的严重性必须根据源严重性的重要程度从该范围中分配数值。
例如,如果源格式定义了“Error”和“Critical”作为错误事件,而“Critical”是一个更重要、更严重的状况,那么我们可以为映射选择以下 SeverityNumber 值:“Error”->17,“Critical”->18。
如果源格式只有一个与此范围含义匹配的严重性,则建议将该严重性分配为该范围的最小值。
例如,如果源格式有一个“Informational”日志级别,并且没有其他具有相似含义的日志级别,则建议为“Informational”使用 SeverityNumber=9。
不定义严重性或日志级别概念的源格式**可以**省略 SeverityNumber 和 SeverityText 字段。后端和 UI 可以将缺少严重性信息的日志记录表示为不同的样式,或者可能将缺少 SeverityNumber 和 SeverityText 字段的日志记录解释为 SeverityNumber 设置为 INFO(数值 9)。
反向映射
当执行从 SeverityNumber 到特定格式的反向映射,并且 SeverityNumber 对于该格式没有相应的映射条目时,建议选择同一严重性范围中数值最接近的目标严重性。
例如,Zap 在 INFO 范围只有一个严重性,称为“Info”。进行反向映射时,INFO 范围内的所有 SeverityNumber 值(数值 9-12)都将映射到 Zap 的“Info”级别。
错误语义
如果 SeverityNumber 存在且其值为 ERROR(数值 17)或更高,则表明该日志记录代表一个错误情况。读取此值的接收者可以自行决定如何使用此事实(例如,UI 可以以不同的颜色显示此类错误,或者提供查找所有错误日志记录的功能)。
如果日志记录代表一个错误事件,并且源格式未定义严重性或日志级别概念,则建议在映射过程中将 SeverityNumber 设置为 ERROR(数值 17)。如果日志记录代表一个非错误事件,则可以省略 SeverityNumber 字段,或者将其设置为小于 ERROR(数值 17)的任何数值。在这种情况下,推荐的值是 INFO(数值 9)。有关更多映射示例,请参阅附录 B。
显示严重性
下表定义了每个 SeverityNumber 值的推荐短名称。短名称可用于例如在 UI 中表示 SeverityNumber。
| SeverityNumber | 短名称 |
|---|---|
| 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 |
显示单个日志记录时,建议同时显示 SeverityText 和 SeverityNumber 值。在这种情况下,推荐的组合字符串以短名称开头,后跟括号中的 SeverityText。
例如,Syslog 的“Informational”记录将显示为 **INFO (Informational)**。当某个日志记录定义了 SeverityNumber 但 SeverityText 缺失时,建议仅显示短名称,例如 **INFO**。
当使用下拉列表(或其他旨在表示可能值集的 UI 元素)来表示严重性时,优先在这些 UI 元素中显示短名称。
例如,一个允许按严重性过滤日志记录的严重性下拉列表,如果它包含 SeverityNumber 的短名称(因此具有有限的上限元素),可能比一个列出系统中已知的所有不同的 SeverityText 值(可能数量很多,仅在大小写或缩写上有所不同,例如“Info”与“Information”)的下拉列表更有用。
比较严重性
在需要进行小于/大于比较的严重性上下文中使用 SeverityNumber 字段。对于用于表示未指定严重性的 SeverityNumber=0,**可以**给予特殊处理。
字段:Body
描述:包含日志记录正文的值。例如,可以是描述事件的自由格式的可读字符串消息(包括多行),也可以是由其他值的数组和映射组成的结构化数据。Body **必须**支持 AnyValue 以保留应用程序发出的结构化日志的语义。对于来自同一源的事件的每次发生,它都可能不同。此字段是可选的。
字段:Resource
类型:Resource。
描述:描述日志的来源,也称为资源。来自同一事件源的事件的多次出现可能在时间上发生,它们都具有相同的 Resource 值。例如,可以包含有关发出记录的应用程序或应用程序运行的基础设施的信息。表示此数据模型的格式可能被设计成允许 Resource 字段对于来自同一源的批次日志记录仅记录一次。**应**遵循 OpenTelemetry 资源语义约定。此字段是可选的。
字段:InstrumentationScope
描述:仪器范围。来自同一范围的事件的多次出现可能在时间上发生,它们都具有相同的 InstrumentationScope 值。此字段是可选的。
字段:Attributes
类型:map<string, any> 或 Attribute Collection。
描述:有关特定事件发生的附加信息。与对于特定源固定的 Resource 字段不同,Attributes 对于来自同一源的事件的每次发生可能不同。可以包含请求上下文的信息(除了跟踪上下文字段)。此字段是可选的。
错误和异常
与日志记录相关的错误和/或异常的附加信息**可以**包含在记录的 Attributes 部分的结构化数据中。如果包含,它们**必须**遵循 OpenTelemetry 异常相关属性的语义约定。
字段:EventName
类型:字符串。
描述:标识事件类别/类型的名称。此名称**应**唯一标识事件结构(包括属性和正文)。具有非空事件名称的日志记录是事件。
示例日志记录
有关示例日志记录,请参阅JSON 文件序列化。
示例映射
有关示例日志格式映射,请参阅数据模型附录。
参考
日志数据模型 OTEP 0097