日志
一个 **日志** 是一个带有时间戳的文本记录,可以是结构化(推荐)或非结构化的,并带有可选的元数据。在所有遥测信号中,日志的历史包袱最大。大多数编程语言都有内置的日志功能或知名的、广泛使用的日志库。
OpenTelemetry 日志
OpenTelemetry 提供了一个 Logs API 和 SDK 来生成日志记录,以及语言 SDK 和日志桥接器来与现有日志框架集成。日志是通过 Logging Provider 发送的任何内容,而事件是日志的一种特殊类型。并非所有日志都是事件,但所有事件都是日志。Logs API 是公开的,可以由应用程序代码直接使用,也可以通过现有的日志库和桥接器间接使用。
OpenTelemetry 旨在与您已生成的日志协同工作,提供用于将日志与其他信号关联、添加上下文属性以及将不同源规范化为通用表示形式以进行处理和导出的工具。
OpenTelemetry Collector 中的 OpenTelemetry 日志
本 OpenTelemetry Collector 提供了多个处理日志的工具
- 提供用于从特定、已知日志数据源解析日志的接收器。
filelogreceiver,它从任何文件中读取日志,并提供从不同格式解析或使用正则表达式的功能。- 如
transformprocessor这样的处理器,它允许您解析嵌套数据、展平嵌套结构、添加/删除/更新值等。 - 允许您以非 OpenTelemetry 格式发出日志数据的导出器。
采用 OpenTelemetry 的第一步通常是部署一个 Collector 作为通用日志代理。
应用程序的 OpenTelemetry 日志
在应用程序中,OpenTelemetry 日志是使用任何日志库或内置日志功能创建的。当您添加自动插桩或激活 SDK 时,OpenTelemetry 会自动将现有日志与任何活动的跟踪和跨度关联起来,用它们的 ID 包装日志正文。换句话说,OpenTelemetry 会自动关联您的日志和跟踪。
语言支持
日志是 OpenTelemetry 规范中一个 稳定 的信号。对于 Logs API 和 SDK 的各个语言实现,状态如下:
| 语言 | 日志 |
|---|---|
| C++ | 稳定 |
| C#/.NET | 稳定 |
| Erlang/Elixir | 开发 |
| Go | Beta |
| Java | 稳定 |
| JavaScript | 开发 |
| PHP | 稳定 |
| Python | 开发 |
| Ruby | 开发 |
| Rust | Beta |
| Swift | 开发 |
结构化、非结构化和半结构化日志
OpenTelemetry 接受任何日志格式,但并非所有格式都对分析同样有用。以下部分解释了结构化、半结构化和非结构化日志之间的区别。重要提示:以 JSON 编码的日志并不自动被视为“结构化”—— 它可能只是半结构化的,没有稳定的模式。结构化日志意味着存在一个稳定的模式或定义明确的类型化字段,下游处理可以可靠地依赖它们。
结构化日志
结构化日志是具有定义良好、一致的模式或类型化字段的日志,下游系统可以可靠地解析和解释。文本编码可以是 JSON、protobuf 或其他格式,但使日志成为结构化的是存在一个稳定的模式(字段名称、类型和语义),而不仅仅是它是有效的 JSON。例如,结构化 JSON 日志可能看起来像:
{
"timestamp": "2024-08-04T12:34:56.789Z",
"level": "INFO",
"service": "user-authentication",
"environment": "production",
"message": "User login successful",
"context": {
"userId": "12345",
"username": "johndoe",
"ipAddress": "192.168.1.1",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
},
"transactionId": "abcd-efgh-ijkl-mnop",
"duration": 200,
"request": {
"method": "POST",
"url": "/api/v1/login",
"headers": {
"Content-Type": "application/json",
"Accept": "application/json"
},
"body": {
"username": "johndoe",
"password": "******"
}
},
"response": {
"statusCode": 200,
"body": {
"success": true,
"token": "jwt-token-here"
}
}
}
对于基础设施组件,通常使用通用日志格式 (CLF)
127.0.0.1 - johndoe [04/Aug/2024:12:34:56 -0400] "POST /api/v1/login HTTP/1.1" 200 1234
遇到混合格式或扩展格式也很常见(例如,CLF 字段与尾随的 JSON 块结合)。
192.168.1.1 - johndoe [04/Aug/2024:12:34:56 -0400] "POST /api/v1/login HTTP/1.1" 200 1234 "http://example.com" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" {"transactionId": "abcd-efgh-ijkl-mnop", "responseTime": 150, "requestBody": {"username": "johndoe"}, "responseHeaders": {"Content-Type": "application/json"}}
在这些情况下,解析或提取所需的部分到规范化记录中,以便下游工具可以一致地分析它们。OpenTelemetry Collector 中的 filelogreceiver 提供了用于解析混合格式的帮助器。
结构化日志在生产环境中更受青睐,因为它们稳定的模式使其易于验证、解析、与跟踪和指标关联以及大规模分析。
非结构化日志
非结构化日志是不遵循一致结构的日志。它们可能更易于人类阅读,并且经常用于开发。然而,不建议在生产可观测性中使用非结构化日志,因为它们在规模上更难解析和分析。
非结构化日志示例
[ERROR] 2024-08-04 12:45:23 - Failed to connect to database. Exception: java.sql.SQLException: Timeout expired. Attempted reconnect 3 times. Server: db.example.com, Port: 5432
System reboot initiated at 2024-08-04 03:00:00 by user: admin. Reason: Scheduled maintenance. Services stopped: web-server, database, cache. Estimated downtime: 15 minutes.
DEBUG - 2024-08-04 09:30:15 - User johndoe performed action: file_upload. Filename: report_Q3_2024.pdf, Size: 2.3 MB, Duration: 5.2 seconds. Result: Success
在生产中存储和分析非结构化日志是可能的,尽管您可能需要做大量工作来解析或预处理它们以使其可供机器读取。例如,上述三个日志需要使用正则表达式来解析其时间戳,并需要自定义解析器来一致地提取日志消息正文。这通常是日志后端知道如何按时间戳对日志进行排序和组织的必要步骤。虽然可以解析非结构化日志用于分析目的,但这可能比切换到结构化日志(例如通过应用程序中的标准日志框架)要付出更多的工作。
半结构化日志
半结构化日志包含机器可读的键/值对或分隔字段,但不保证跨发射器的稳定模式。示例包括键=值日志(如下所示)或字段名称和类型在消息之间变化的 JSON blob。半结构化日志通常比非结构化日志更容易解析,但可能仍需要预处理和规范化才能进行分析。
半结构化日志示例
2024-08-04T12:45:23Z level=ERROR service=user-authentication userId=12345 action=login message="Failed login attempt" error="Invalid password" ipAddress=192.168.1.1 userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
半结构化日志可能需要在摄取期间进行映射和类型转换,才能完全用于下游分析。
OpenTelemetry 日志组件
以下概念和组件列表支持 OpenTelemetry 的日志功能。
日志 Appender / 桥接器
作为应用程序开发人员,您不应该直接调用 **Logs Bridge API**,因为它提供给日志库作者构建日志 Appender / 桥接器。相反,您只需使用您喜欢的日志库,并将其配置为使用能够将日志输出到 OpenTelemetry LogRecordExporter 的日志 Appender(或日志桥接器)。
OpenTelemetry 语言 SDK 提供了此功能。
Logger Provider
属于 **Logs Bridge API** 的一部分,只有在您是日志库的作者时才应使用。
Logger Provider(有时称为 LoggerProvider)是 Logger 的工厂。在大多数情况下,Logger Provider 只初始化一次,其生命周期与应用程序的生命周期匹配。Logger Provider 的初始化还包括 Resource 和 Exporter 的初始化。
Logger
属于 **Logs Bridge API** 的一部分,只有在您是日志库的作者时才应使用。
Logger 创建日志记录。Logger 从 Log Provider 创建。
Log Record Exporter
Log Record Exporter 将日志记录发送到消费者。该消费者可以是用于调试和开发的标准输出、OpenTelemetry Collector,或者您选择的任何开源或供应商后端。
Log Record
Log record 代表事件的记录。在 OpenTelemetry 中,Log record 包含两种字段:
- 具有特定类型和含义的命名顶级字段
- 任意值和类型的 Resource 和 Attributes 字段
顶级字段是:
| 字段名称 | 描述 |
|---|---|
| Timestamp | 事件发生的时间。 |
| ObservedTimestamp | 事件被观测到的时间。 |
| TraceId | 请求的跟踪 ID。 |
| SpanId | 请求的跨度 ID。 |
| TraceFlags | W3C 跟踪标志。 |
| SeverityText | 严重性文本(也称为日志级别)。 |
| SeverityNumber | 严重性的数值。 |
| Body | 日志记录的正文。 |
| Resource | 描述日志的来源。 |
| InstrumentationScope | 描述发出日志的作用域。 |
| Attributes | 有关事件的附加信息。 |
有关日志记录和日志字段的更多详细信息,请参阅 日志数据模型。
规范
要了解更多关于 OpenTelemetry 中日志的信息,请参阅 日志规范。