组件管理器错误样式指南

设计初衷

最重要的是,所有错误都应该有用。这是他们存在的唯一目的,否则我们将静默失败,让每个人都感到好奇究竟发生了什么。

错误应该提供用户采取适当操作所需的信息。有时该用户是代码中的 match,有时是查看日志缓冲区的人类。以用户为中心是构建对他们有用的关键!

简而言之,如果您遵循以下原则:

  • 错误将变得更有意义
  • 错误更易于处理
  • 组件管理器的代码库将更易于阅读
  • 将避免更多 bug
  • 会触发错误的 bug 更易于修复

原则

以下原则只是有关如何出现有意义且有用的错误的指南,但并不是很有指导意义。如果您认为某个场景中的这些准则是错误的,请运用您的最佳判断来更新此文档。

请勿不小心使用 panic!assert!unwrap(...)expect(...)

组件管理器应该不会崩溃。如果组件管理器崩溃,系统会重新启动,且用户数据可能会丢失。

极少数情况下,崩溃是可以接受的:

请勿使用 println!

虽然 println! 与组件管理器中的调试日志集成,但 tracing log library 可提供更多功能,并在我们的代码库中应用更广泛。tracing 库还与 debuglog 集成,具有方便的宏,并且支持结构化日志记录。

此功能目前受 lint 规则保护。

请勿使用 anyhow

借助 anyhow 库,无法构建结构合理的错误类型并与之进行匹配。

此规则没有已知的例外情况。

务必使用 thiserror 库创建自定义错误类型

thiserror 库会自动针对自定义错误类型实现 std::error::Error。避免手动实现 std::error::Error

此规则没有已知的例外情况。

务必为组件管理器中的特定功能/操作创建自定义错误类型

自定义错误类型有助于枚举功能/操作的所有可能错误状态。它们不会与组件管理器的任何其他区域相关联,并且可以独立于代码库的其余部分进行维护。

务必考虑是否有必要针对您的自定义错误类型实现 Into<ModelError>

ModelError 有许多针对不同错误类别的枚举变体。您可能根本不需要将自定义错误类型添加到 ModelError 中。

请勿ModelError 添加精确错误

最好创建自定义错误类型来表示您的地图项/操作的所有错误,即使您当前只有一个错误也是如此。如果将精确错误直接添加到 ModelError 中,开发者可能会失去关于错误适用位置的背景信息。

请勿存储 CloneableError 等一般性错误类型

宽泛的错误类型会导致无法构建结构合理的错误。没有惯用的方法可以匹配这些泛型类型。

此规则的唯一例外情况是错误来自使用通用错误类型的外部库。可能无法更改外部库,因此可以接受存储泛型。

请勿将现有错误类型用于“足够接近”的行为

在组件管理器中导致 bug 的根本原因在于,错误描述世界状态的错误会造成难以调试的问题,尤其是在组件管理器中。最好创建错误类型,以准确描述不同的错误状态。另一种方法是向现有错误类型添加枚举,以提供其他分类。

此规则没有已知的例外情况。

务必考虑日志记录是否绝对必要

可以将组件管理器视为一种超级库。作为一个库,组件管理器通常无法知道错误是否有意义。例如,请考虑如果 UDP 库记录每个丢弃的数据包,会发生什么情况。

虽然在此问题上还没有普遍接受的建议,但对于组件管理器,应遵循以下提示:

  • 谨慎使用日志记录。
  • 在提交之前,为协助开发/调试而创建的日志必须移除或设置为 DEBUG 日志级别。
  • 避免在热代码路径中添加日志。这可能会产生日志垃圾内容,并且这种垃圾内容并无用处。
  • 如果错误也通过 FIDL 返回给客户端,并且详细信息数量相同,请勿记录错误。
  • 相反,如果通过 FIDL 发送错误时丢失了一些详细信息,可以记录详细的错误消息。
    • 另外,请考虑客户端没有收到详细错误的原因。
  • 记录未涉及客户端的错误,并且该错误可能对调试问题很重要。
  • 遵循 RFC-0003 中设置的日志记录准则

务必将组件标识符添加到日志和错误中

组件管理器经常会产生与特定组件相关的错误。确保错误包含组件标识符,以便于调试。 对于日志,请使用组件级范围的日志记录器,或在消息中添加组件标识符。

可接受的组件标识符(按优先顺序排列):名称、组件网址、实例 ID

不要在日志消息中输出对象的 Debug 字符串

Debug 特征和相应的 {:?} 格式说明符只能用于交互式调试目的。将等效的 JSON 对象输出到日志会使它们更难以理解。倾向于将简单易懂的错误消息输出到日志。

此规则没有已知的例外情况。

请勿存储字符串化的错误消息

请勿存储错误的 DebugDisplay 字符串。字符串错误没有可靠的结构,无法匹配。始终首选按原样存储错误。

外部库中的某些错误未实现 ClonePartialEq 等所需的特征。在这类罕见情况下,如果无法将这些特征添加到外部库,可以将错误消息字符串化并存储。

请勿为每个错误变体创建函数

不需要为错误变体创建创建函数。它们允许某些类型的隐式转换,还会隐藏字段名称。最好直接创建错误类型并手动设置字段名称。

此规则没有已知的例外情况。

像板箱作者一样思考

组件管理器是一个大型 crate。我们已将路线引入自己的箱子中 未来可能会提供更多功能思考一下,如果您处理的特定代码部分是其自身 crate 的一部分,那么您将会使用哪些错误类型。对于 crate 中的代码逻辑集合,合理的错误类型是什么?