# RtcEventLog · 通话的黑匣子

> RtcEventLog 是 WebRTC 的黑匣子——把通话期间每个关键事件（RTP 头、RTCP、ICE、带宽估计变化）逐条记进一个二进制日志，事后能完整回放。这一章读 rtc_event_log.h，讲清它记什么、怎么开关、二进制格式为何不该当 protobuf 直接读。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/webrtc/rtc-event-log
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# RtcEventLog · 通话的黑匣子

[getStats 是“快照”](https://daiw.org/manual/webrtc/stats-report)，回答“现在怎么样”。但要查“[第 30 秒为什么开始卡](https://daiw.org/manual/webrtc/observability-overview)”这类**时序问题**，快照不够——你需要一份“录像”。这就是 **`RtcEventLog`**：WebRTC 的黑匣子，把通话期间发生的一切逐条记下来。

## 接口：开、关、记

`RtcEventLog` 的接口极简（真实源码 `rtc_event_log/rtc_event_log.h`）：

```cpp
// rtc_event_log/rtc_event_log.h(节选,真实源码)
class RtcEventLog {
 public:
  enum class EncodingType { Legacy, NewFormat, ProtoFree };

  virtual bool StartLogging(std::unique_ptr<RtcEventLogOutput> output,
                            int64_t output_period_ms) = 0;
  virtual void StopLogging() = 0;
  virtual void Log(std::unique_ptr<RtcEvent> event) = 0;   // 记一个事件
};

// 关闭日志时用的空实现,什么都不记(零开销)
class RtcEventLogNull final : public RtcEventLog {
  void Log(std::unique_ptr<RtcEvent> /*event*/) override {}
};
```

<Callout type="info">
  **`RtcEventLog` 就三件事：`StartLogging`（开始记，指定[输出到哪](https://daiw.org/manual/webrtc/rtc-event-log)）、`StopLogging`（停）、`Log(event)`（记一个事件）。引擎各处在关键时刻调 `Log`，把[事件](https://daiw.org/manual/webrtc/rtc-event-log)投进来。** 两个设计细节：

  - **`RtcEventLogNull`** —— 日志关闭时用这个**空实现**，`Log` 是空函数。于是引擎代码可以[无脑到处调 `Log`](https://daiw.org/manual/webrtc/rtc-event-log)，不开日志时零开销（调一个空虚函数），开了才真记。这是“[默认零成本](https://daiw.org/manual/webrtc/rtc-base-overview)”的典型手法。
  - **`output_period_ms`** —— 日志**批量**写出（攒一批再落盘），而非每个事件都同步写，避免拖慢[热路径](https://daiw.org/manual/webrtc/pacing)。
  - **`EncodingType`** —— 编码格式有 `Legacy`/`NewFormat`/`ProtoFree` 几种演进，反映日志格式一直在优化。
</Callout>

## 记什么：几乎所有关键事件

`RtcEventLog` 记录的内容，[官方 g3doc](https://daiw.org/manual/webrtc/reading-the-source) 列得很清楚——覆盖了排障需要的几乎所有维度：

| 记录的事件 | 对应本教程 |
| --- | --- |
| 收 / 发的 **RTP 头**（不含负载） | [第 4 部分 RTP](https://daiw.org/manual/webrtc/rtp-packet) |
| 完整的 **RTCP 反馈** | [RTCP 反馈](https://daiw.org/manual/webrtc/rtcp-feedback) |
| **ICE** candidate、ping、response | [连通性检查](https://daiw.org/manual/webrtc/connection-and-checks) |
| **带宽估计**事件（loss/delay-based 估计、[探测结果](https://daiw.org/manual/webrtc/probing)、[ALR 状态](https://daiw.org/manual/webrtc/pacing)） | [第 5 部分拥塞控制](https://daiw.org/manual/webrtc/congestion-control-overview) |
| **音频网络自适应**设置 | [音频编解码](https://daiw.org/manual/webrtc/audio-codecs) |
| **音频播放**事件 | [NetEq](https://daiw.org/manual/webrtc/neteq-overview) |

<Callout type="info">
  **注意：RTP **只记头、不记负载**——日志是为**排障**，不是[录音录像](https://daiw.org/manual/webrtc/srtp-encryption)。** 记 [RTP 头](https://daiw.org/manual/webrtc/rtp-packet)（序号、时间戳、[SSRC](https://daiw.org/manual/webrtc/rtp-demuxer)、[扩展](https://daiw.org/manual/webrtc/rtp-header-extensions)）就够重建“[包何时发 / 到、丢了没、乱序没](https://daiw.org/manual/webrtc/transport-cc)”；记[负载](https://daiw.org/manual/webrtc/video-frame)则既是[隐私风险](https://daiw.org/manual/webrtc/srtp-encryption)、又让日志大到没法用。这个“[记元数据、不记内容](https://daiw.org/manual/webrtc/rtc-event-log)”的取舍，让 event log 足够小、又足够还原网络行为。尤其宝贵的是它记了[**带宽估计的内部事件**](https://daiw.org/manual/webrtc/delay-based-bwe)——[GoogCC 每次改主意](https://daiw.org/manual/webrtc/rate-control)、[每次探测](https://daiw.org/manual/webrtc/probing)都留痕，这是从外部[永远看不到的引擎内心活动](https://daiw.org/manual/webrtc/congestion-control-overview)，也是调“[为什么码率上不去](https://daiw.org/manual/webrtc/loss-based-bwe)”的关键证据。
</Callout>

## 二进制格式：别当 protobuf 直接读

一个容易踩的坑（[g3doc 特别强调](https://daiw.org/manual/webrtc/reading-the-source)）：

<Callout type="warn">
  **event log 是**二进制格式**，且“不保证格式稳定、可能随时变”——你**不应该**把它当 protobuf 直接解析，而要用官方的 `rtc_event_log_parser.h` 来读。** 为什么？日志格式为了[省空间](https://daiw.org/manual/webrtc/rtc-event-log)一直在演进（[`EncodingType` 那几种](https://daiw.org/manual/webrtc/rtc-event-log)就是证据），内部虽然用了 protobuf-like 编码，但布局细节属于实现私货。**直接按 protobuf 解会在下个版本挂掉**；而官方 parser 会兼容新旧格式。这个约束提醒我们：event log 是[给官方工具链吃的中间产物](https://daiw.org/manual/webrtc/event-log-visualizer)，不是稳定的对外 API——你的角色是“抓日志 + 用官方工具分析”，而非自己解析二进制。
</Callout>

日志一般[输出到文件](https://daiw.org/manual/webrtc/rtc-event-log)（`RtcEventLogOutputFile`），Chrome 里可通过 `chrome://webrtc-internals` 一键 dump。抓到的 `.log` 文件，交给下一章的**可视化工具**。

## 小结与下一章

**`RtcEventLog`** 是 WebRTC 的黑匣子——`StartLogging`/`StopLogging`/`Log(event)` 把通话期间的[每个关键事件逐条记下](https://daiw.org/manual/webrtc/rtc-event-log)。日志关闭时用 **`RtcEventLogNull`** 空实现做到[零开销](https://daiw.org/manual/webrtc/rtc-base-overview)，开启时[批量落盘](https://daiw.org/manual/webrtc/pacing)。它记[RTP 头（不记负载，兼顾隐私与体积）](https://daiw.org/manual/webrtc/srtp-encryption)、[RTCP](https://daiw.org/manual/webrtc/rtcp-feedback)、[ICE 事件](https://daiw.org/manual/webrtc/connection-and-checks)、以及最宝贵的[**带宽估计内部事件**](https://daiw.org/manual/webrtc/delay-based-bwe)——[外部看不到的引擎内心活动](https://daiw.org/manual/webrtc/congestion-control-overview)。格式是[会变的二进制](https://daiw.org/manual/webrtc/reading-the-source)，须用[官方 parser](https://daiw.org/manual/webrtc/event-log-visualizer) 读、别当 protobuf 直接解。

抓到日志后，怎么把它变成能看懂的图、定位问题？下一章看可视化工具与排障实战。👉 [可视化与排障](https://daiw.org/manual/webrtc/event-log-visualizer)。

## 参考资料

- [1] libwebrtc 源码.`rtc_event_log/rtc_event_log.h`（`StartLogging`/`Log`/`EncodingType`、`RtcEventLogNull`）、`rtc_event_log_output_file.h`。
- [2] WebRTC 官方 `g3doc/rtc_event_log.md`（记录内容清单、格式说明）。
- [3] 关联[第 4 部分 RTP/RTCP](https://daiw.org/manual/webrtc/rtp-packet)、[第 5 部分拥塞控制](https://daiw.org/manual/webrtc/congestion-control-overview)。
