日志

对事件的记录。

日志是带有时间戳的文本记录,可以是结构化的(推荐)或非结构化的,且可附带元数据。 在所有遥测信号中,日志具有最悠久的历史。大多数编程语言都内置了日志记录功能,或有知名且广泛使用的日志库。

OpenTelemetry 日志

OpenTelemetry 并未定义专用的 API 或 SDK 来创建日志。相反,OpenTelemetry 日志指的是你已有的日志, 这些日志来自现有的日志框架或基础设施组件。OpenTelemetry SDK 和自动注入功能利用多个组件, 自动将日志与链路关联。

OpenTelemetry 的日志支持旨在与你已有的日志完全兼容,提供了能力来为这些日志添加上下文, 并提供统一的工具包,将来自不同来源的日志解析并转换为统一格式。

OpenTelemetry Collector 中的 OpenTelemetry 日志

OpenTelemetry Collector 提供了多个处理日志的工具:

  • 多种接收器,可从特定的、已知的数据源解析日志。
  • filelogreceiver,可从任意文件读取日志,并支持多种日志格式解析或使用正则表达式。
  • 类似 transformprocessor 的处理器,可解析嵌套数据、扁平化结构、添加/移除/更新值等。
  • 多种导出器(exporters),可将日志数据导出为非 OpenTelemetry 格式。

采纳 OpenTelemetry 的第一步通常是部署 Collector 作为通用日志代理。

应用中的 OpenTelemetry 日志

在应用中,可以通过任意日志库或语言内置日志功能创建 OpenTelemetry 日志。 当你启用自动注入或激活 SDK 后,OpenTelemetry 会自动将现有日志与活动的链路与 Span 关联起来, 并将这些 ID 包裹到日志正文中。换句话说,OpenTelemetry 会自动关联你的日志与链路信息。

语言支持情况

日志信号在 OpenTelemetry 规范中是稳定的。 各语言对 Logs API 和 SDK 的支持情况如下:

LanguageLogs
C++Stable
C#/.NETStable
Erlang/ElixirDevelopment
GoBeta
JavaStable
JavaScriptDevelopment
PHPStable
PythonDevelopment
RubyDevelopment
RustBeta
SwiftDevelopment

结构化、非结构化与半结构化日志

OpenTelemetry 在技术上并不区分结构化与非结构化日志。你可以使用任何已有的日志配合 OpenTelemetry。不过, 并非所有日志格式都同样有用!尤其是在生产环境中,推荐使用结构化日志,因为它们易于解析和大规模分析。 以下部分将解释三种类型日志的差异。

结构化日志

结构化日志是指其文本格式具有一致性、可被机器读取的日志。在应用中,最常见的结构化格式是 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 ..."
  },
  "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

也可以混用不同结构化格式。例如扩展日志格式(ELF)可以将 JSON 与空格分隔的 CLF 数据混合:

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 ..." {"transactionId": "abcd-efgh...", ...}

为了充分利用这类日志,需要将 JSON 和 ELF 部分都解析为统一格式,便于在可观测性后端分析。 OpenTelemetry Collector 中的 filelogreceiver 提供了解析这类日志的标准方法。

结构化日志是最推荐的日志使用方式。由于格式一致,它们更易于解析,从而便于在 Collector 中预处理、与其他数据关联,并在后端平台分析。

非结构化日志

非结构化日志指没有一致结构的日志。它们可能更易读,因此在开发中常用。 但在生产可观测性中不推荐使用,因为很难大规模解析和分析。

非结构化日志示例:

[ERROR] 2024-08-04 12:45:23 - Failed to connect to database. Exception: java.sql.SQLException: Timeout expired. ...

System reboot initiated at 2024-08-04 03:00:00 by user: admin. Reason: Scheduled maintenance. ...

DEBUG - 2024-08-04 09:30:15 - User johndoe performed action: file_upload. Filename: report_Q3_2024.pdf, Size: 2.3 MB ...

在生产环境中也可以存储和分析非结构化日志,尽管你可能需要进行大量解析或预处理工作,使其具备机器可读性。 例如,前面提到的三条日志就需要使用正则表达式解析时间戳,并编写自定义解析器来稳定地提取日志消息的正文。 这通常是日志后端根据时间戳对日志进行排序和组织所必需的。虽然解析非结构化日志以便分析是可行的, 但所需工作量往往比直接采用结构化日志要大,比如通过在应用中使用标准的日志记录框架来实现结构化日志。

半结构化日志

半结构化日志具有某种自一致的模式以便被机器读取,但不同系统之间的数据格式和分隔符可能不一致。

半结构化日志示例:

2024-08-04T12:45:23Z level=ERROR service=user-authentication userId=12345 action=login message="Failed login attempt" error="Invalid password" ...

尽管机器可读,但为了实现大规模分析,仍可能需要多个解析器。

OpenTelemetry 日志组件

以下是构成 OpenTelemetry 日志支持的核心概念和组件。

日志附加器/桥接器

作为应用开发者,你无需直接调用 Logs Bridge API,该 API 是为日志库作者构建日志附加器或桥接器而设。 你只需使用喜欢的日志库,并配置其使用能将日志传递给 OpenTelemetry LogRecordExporter 的附加器。

OpenTelemetry 各语言 SDK 提供了此功能。

Logger Provider

属于 Logs Bridge API,仅供日志库作者使用。

Logger Provider(有时叫 LoggerProvider)是 Logger 的工厂。 一般在应用启动时初始化一次,其生命周期与应用相同。初始化时也包括 Resource 和 Exporter 的初始化。

Logger

属于 Logs Bridge API,仅供日志库作者使用。

Logger 用于创建日志记录,Logger 实例由 Logger Provider 生成。

日志记录导出器

Log Record Exporter 将日志记录发送到某个消费者。 消费者可以是标准输出(用于调试或开发),也可以是 OpenTelemetry Collector,或你选择的任何开源或商业后端。

日志记录

日志记录代表对事件的记录。在 OpenTelemetry 中,日志记录包含两类字段:

  • 具备特定含义的命名顶级字段;
  • 可任意定义的 Resource 和 Attributes 字段。

顶级字段如下:

字段名描述
Timestamp事件发生的时间
ObservedTimestamp观察到事件的时间
TraceId请求的链路 ID
SpanId请求的 Span ID
TraceFlagsW3C 链路标志
SeverityText严重性文本(也称日志级别)
SeverityNumber严重性的数值表示
Body日志正文
Resource日志来源
InstrumentationScope产生该日志的范围
Attributes事件的附加信息

更多有关日志记录与字段的细节,请参阅日志数据模型

规范

如需进一步了解 OpenTelemetry 中的日志,请参阅日志规范