跳转到内容

错误处理与日志

组件不使用异常。错误分三层:构造、启动、运行期。

阶段查询方式错误去向
构造valid() / last_error()构造函数不返回错误码;具体原因已由 ET.TOML / ET.CONFIG 输出
启动start() 返回值同时置 Status.state = Error、Status.last_error
运行期get_status().last_error + Error 事件组件内部记录最近一次错误

组件不缓存解析诊断。要定位配置错误,必须看串口日志;last_error() 只给错误码。

et::Inst inst(toml, options);
if (!inst.valid()) {
ESP_LOGE(TAG, "config rejected: 0x%x", inst.last_error());
// 具体原因看串口第一条 ET.TOML / ET.CONFIG Error
return;
}

常见错误码:

错误码常见原因
ESP_ERR_INVALID_ARG字段类型/取值错误、peer URI 非法、结构校验失败
ESP_ERR_NO_MEM运行上下文分配失败(堆不足)
ESP_ERR_INVALID_SIZETOML 节点/数组/表键超限
ESP_ERR_NOT_SUPPORTED宿主构建(无 FreeRTOS)调用 start()

ipv4 是唯一不报错的字段:内容非法时静默忽略。

esp_err_t err = inst.start();
if (err != ESP_OK) {
ESP_LOGE(TAG, "start failed: 0x%x", err);
return;
}

start() 失败会回滚已创建资源并置 State::Error。常见原因:

  • 队列/信号量分配失败(降低深度或开 PSRAM);
  • TcpConn 槽分配失败(约 12 KiB/槽);
  • 加密组件初始化失败(PSA/mbedTLS);
  • 任务创建失败(栈太大或内存不足);
  • 虚拟网卡创建失败(静态地址时)。
void on_event(const et::Event& event, void*) noexcept {
if (event.type == et::EventType::Error) {
ESP_LOGE(TAG, "runtime error: 0x%x", event.status.last_error);
}
}

Status::last_error 是“最近一次错误”,不是历史累计。排障时结合日志时间线看。

标签模块
ET.INSTANCEInst 构造/生命周期
ET.TOMLTOML 解析与字段校验
ET.CONFIG结构校验、身份补齐
ET.CONTEXT上下文、队列、任务、启动/停止
ET.IOsocket、连接、切帧、重连
ET.TCPTCP 连接细节
ET.PROTO握手、保活、RPC 分发、状态迁移
ET.RPC服务注册、事务、分片
ET.SYNC路由同步
ET.ROUTE最短路与快照
ET.TOPO拓扑表
ET.DHCP地址自分配
ET.NETIF虚拟网卡
ET.AES加密

Kconfig EASYTIER_ESP_LOG_LEVEL:

  • Closed:全部裁剪;
  • Error:仅错误;
  • Warning:错误 + 警告;
  • Info:默认;
  • Debug:全部。

低于所选等级的日志在编译期被裁剪,不占固件体积。ESP-IDF 全局日志等级仍会叠加生效。

  1. 看第一条 ET.TOML / ET.CONFIG Error;
  2. 核对字段类型、范围、peer URI;
  3. 确认至少 1 个入口、且配置了静态 IP 或 DHCP;
  4. 确认 network_name 非空。
  1. 看 ET.CONTEXT 的分配失败日志;
  2. 开 PSRAM;
  3. 降低连接槽/队列深度;
  4. 确认任务栈没有超过可用内存。
  1. 看 ET.PROTO/ET.IO 的时间线;
  2. 结合 Status 的 state、direct_peer_count、reachable_route_count、dropped_packets;
  3. 记录 last_error 与日志时间线,必要时在 常见问题 FAQ 中查找同类问题。
  • 不要只打印 last_error() 而不看日志;
  • 不要把 last_error 当成“当前是否可用”;
  • 不要在事件回调里做长时间日志格式化/阻塞 IO;
  • 不要用旧 ELF 解析崩溃回溯。