Skip to content
AI 文档索引 ↗

日志交接边界 ​

LoggingConsumer 将 Collector 网络请求替换为本地 JSON Lines 写入,由独立 LogAgent 负责后续交付。该方式依赖磁盘、Agent、清理和 ACK 运维协议,吞吐、延迟与故障恢复需在目标文件系统和 Agent 上测试。


一、 HTTP 上传与本地日志交接 ​

选择日志交接前,应先比较默认批量上传与本地磁盘通道的故障边界:

  1. 默认批量上传:SDK 有界内存队列会重试网络故障;Collector 长时间不可用时,队列可能满,宿主必须处理拒收结果。
  2. 日志交接:SDK 把事件写入本地文件,减少调用路径对 Collector 的直接依赖;磁盘满、写入失败或 Agent 停滞仍会导致拒收或交接延迟。
  3. 业务事务一致性:两种模式都不能把业务状态提交与埋点写入合成一个原子事务;订单、支付等权威事实应采用宿主事务 Outbox。

二、 LoggingConsumer 解耦架构 ​

LoggingConsumer 将文件写入与网络上传拆成两个独立环节,接入方需部署并验证 Agent/Relay:

text
┌─────────────────────────────────────────────────────────────┐
│ 宿主机 / 容器 Pod                                           │
│                                                             │
│   业务系统                                                   │
│       │                                                     │
│       ▼ (序列化 + 本地文件追加;可选 fsync)                  │
│   LoggingConsumer                                           │
│       │                                                     │
│       ▼                                                     │
│   写入中 events-*.open → 封存后 events-*.jsonl              │
│   [UTF-8 JSON Lines;Agent 只读取封存文件]                  │
│       │                                                     │
│       ▼ (独立常驻进程监听文件读取)                          │
│   日志采集代理 + 批协议 Relay                                │
└───────┬─────────────────────────────────────────────────────┘
        │
        ▼ (批量压缩聚合网络上传)
   接收网关 (Collector / Identity Gateway)

行为与限制 ​

  • SDK 写入路径不直接调用 Collector,但仍有 JSON 序列化、锁、文件写和可选 fsync 成本;Java 和 Go 为同步调用。
  • Collector 故障不会直接阻塞 SDK 的网络请求,但磁盘预算耗尽、文件系统错误或 Agent 积压仍会让写入失败。
  • LogAgent 必须把每行中的 event 转换为 Collector 批协议并保留 eventId;读取文件成功不等于 Collector ACK。

三、 磁盘预算与文件管理 ​

本地日志需要设置容量上限并定义文件交接和清理策略。LoggingConsumer 提供以下保护:

  1. 单文件大小轮转 (maxFileBytes):
    • 日志文件达到 maxFileBytes 后轮转,创建新的活动文件。
  2. 单前缀文件预算 (maxTotalBytes):
    • 为每个写者前缀设置 maxTotalBytes 上限;SDK 统计该前缀的活动 .open 和已封存 .jsonl,超出预算时拒绝新事件。
    • 多个 Worker 共用目录时,各自预算相加可能超过目录可用空间。需要目录级上限时,为每个 Worker 使用独立目录,或另设文件系统配额和磁盘监控。
  3. 单写者所有权与文件交接:
    • 同一本地规范化目录和文件前缀由 .prefix.lock 独占;第二 owner 在预算统计前失败。四端在每次 accept/flush 及 close 最终同步前复核 token,sidecar 被删除或替换时先进入终态失败,不继续追加或发布用户态缓冲。
    • 干净关闭释放锁。异常退出会保留锁,只有确认旧进程终止后才可人工移走;不得按 PID 或时间自动偷锁。
    • 同一宿主上的多个 Worker 可使用不同且稳定的文件前缀,例如 events-worker-1、events-worker-2;同前缀第二写者必须失败,不能依赖操作系统日志追加的偶然原子性。前缀只允许字母、数字、点、下划线和连字符,且首字符须为字母或数字。
    • 前缀按完整文件名边界识别;例如 events 不接管 events-worker-1 的文件。Agent 的 events-*.jsonl 通配符可以读取多个前缀,但 SDK 所有权和预算仍分别计算。
    • 该 sidecar 不是 NFS/PVC 分布式锁。多 Pod 应使用各自独占卷/前缀,或采用具备分布式协调的交付系统。
    • 活动文件以 .open 结尾;轮转或正常 close() 时先同步并关闭,再在同一目录原子改名为 .jsonl。Agent 仅匹配 .jsonl。flush() 只同步当前 .open,不会把它交给 Agent。因此低流量常驻进程可能直到轮转或关闭才交接文件,应按业务时效和文件大小安排受控轮转/实例关闭,而不能假定 flush() 会立即交接。
    • 异常退出后 .open 保留为待检查文件。确认旧进程停止后移除残留锁,重新打开相同前缀仍会报 recovery-required / RECOVERY_REQUIRED / ErrRecoveryRequired,直至操作员检查并隔离或修复 .open。不得直接改名为 .jsonl 或自动补发;先核对完整行、尾部截断和下游去重状态。
  4. 已交接文件释放预算:
    • Agent 只能在下游已持久接收并完成自身 checkpoint 后删除已封存的 .jsonl 文件,不得删除 .open。预算同时统计两类文件。
    • SDK 按各语言实现核对目录用量;误删活动文件会 fail-closed。具体复核时机见所用语言的接入说明。
    • SDK 不知道 Collector 是否 ACK;删除策略属于 Agent/Relay 运维合同,不能由“已读取一行”替代。

四、 各语言构造 ​

按语言配置 LoggingConsumer:

五、 LogAgent 接入边界与 Filebeat 输入示例 ​

落盘文件是单行 JSON。下面的模式只匹配已封存的 .jsonl;禁止改成 events-*,否则会读到写入中的 .open。Filebeat 当前官方支持的输出包括 Elasticsearch、Logstash、Kafka、Redis、File、Console 和 Discard,并没有通用 output.http;因此不能把 Filebeat 直接指向本项目 Collector 批接口。可使用 filestream + ndjson 读取,再输出到 Kafka/Logstash,由受控 Relay 组装 ServerBatch、保留事件 ID 并处理 ACK。支持的输出列表见 Elastic 官方文档,filestream 的清理状态约束见 官方输入文档。

yaml
filebeat.inputs:
  - type: filestream
    id: track-business-events
    enabled: true
    paths:
      - /var/log/track-business/events-*.jsonl
    parsers:
      - ndjson:
          target: ''

# 示例中转目的地;不是 Track Business Collector。
output.kafka:
  hosts: ['kafka-1.internal:9092']
  topic: track-business-sdk-handoff

从旧版“活动文件直接使用 .jsonl”升级时,应先停止写者与 Agent,确认旧进程已完全关闭并对旧文件完成下游 checkpoint,再部署新版写者及只匹配 .jsonl 的 Agent 配置。旧文件可能含不完整末行,须单独检查;不要把升级当作自动无损迁移。接入方应验证缺锁、残留 .open、容量耗尽、同前缀冲突及文件篡改时均不会静默继续采集。


六、业务事务与 Transactional Outbox ​

事务一致性

SDK 内置的 FileEventStore 仅是针对单进程单机重启场景的轻量级快照恢复适配器,它不是分布式事务 WAL,不能防止数据库回滚导致的数据虚假上报。

对于订单支付、扣款和退款等必须与业务事务保持一致的事件,可使用宿主实现的 Transactional Outbox:

text
[业务服务 Web 接口]
       │
       ▼ (开启数据库本地事务)
┌──────────────────────────────────────────────────┐
│ 本地数据库 (MySQL / PostgreSQL)                   │
│                                                  │
│   1. INSERT INTO orders (id, amount, status);    │
│   2. INSERT INTO outbox_events (id, payload);    │
└──────────────────────────────────────────────────┘
       │
       ▼ (提交本地事务,使业务更新与 outbox insert 同成同败)
[事务提交成功]
       │
       ▼ (独立轮询服务或 CDC 监听 outbox_events)
[读取未发送事件并通过 SDK 发送] ──► [收到 ACK 后标记 outbox_events 为已发送]

业务记录与 outbox insert 在同一个本地事务中,可以消除二者之间的提交窗口;Relay 仍可能重复投递,必须复用不可变 eventId,并只在 Collector 明确 ACK 后更新终态。数据库 Outbox 和 Relay 需由接入方实现与验证。