8个版本
0.5.0 | 2023年12月28日 |
---|---|
0.4.3 | 2023年12月2日 |
0.4.2 | 2023年11月25日 |
0.3.0 | 2023年10月5日 |
0.3.0-dev | 2023年9月9日 |
在调试中排名161
每月下载量152
在3 个代码包中使用
26KB
262 行
Marker Error 🔴
此代码包为所有Marker代码包提供通用的错误处理工具。如果您对项目感兴趣,欢迎查看Marker的Readme。
警告
此代码包不是Marker官方API的一部分,它仅打算由marker内部使用。
错误处理的目标是为用户和marker
的开发者提供有关错误上下文的诊断信息。这意味着至少有两个角色需要观察错误。
用户视角
理解简单错误
用户希望在出错时看到简洁但信息丰富的错误消息。错误消息必须指导用户了解失败的原因,并可选地建议修复方法。
解决方案
只需将错误消息打印到进程的stderr
即可。用户将能够在他们的终端或CI日志中看到它。默认情况下,错误不应包含任何详尽的诊断信息,如回溯或span跟踪。
使用 Result
类型来报告此类错误。负责渲染此类错误crate的选择是 miette
。该crate以可读且色彩丰富的形式渲染了在 marker
代码执行过程中发生的错误链。它还允许包含错误所引用的文本片段;例如,如果配置无效,我们可能会显示无效配置的片段并描述其错误的部分。
miette
基于 thiserror
构建,并扩展了 std::error::Error
,增加了上述功能以及错误代码、帮助信息、多重错误处理等。这个crate结合使用 miette
和 thiserror
来达到用户友好的错误报告目标。
错误链
错误具有“链”的概念。有一个原始错误是问题的根本原因,以及在错误通过调用栈传播时包装原始错误的全部其他错误。请确保不要在包装错误的错误消息中包含原始错误的消息。这是因为 miette
仍会渲染链中所有错误的错误消息。以下是一个有问题的代码示例。
#[derive(Debug, thiserror::Error)]
enum RootError {
#[error("Oh no, Foo happened: {0}")]
Foo(FooError)
}
#[derive(Debug, thiserror::Error)]
#[error("Foo failed with factor {foo_factor}")]
struct FooError {
foo_factor: usize
}
在这种情况下,当输出 RootError
时,您将看到以下内容。
× Oh no, Foo happened: Foo failed with factor 42
╰─▶ Foo failed with factor 42
您可能会注意到这里消息的重复。
如果您从 RootError
的错误消息中删除 : {0}
,错误输出将更简洁。
× Oh no, Foo happened
╰─▶ Foo failed with factor 42
开发错误解决方案
如果用户在 marker
中遇到bug,他们需要理解这确实是一个bug。通常,当错误是由用户引起的时,他们可以找出这一点。如果错误是由 marker
中的bug引起的,用户可能需要更深入地了解 marker
代码的内部,以找出他们可以应用的任何解决方案来暂时解决问题。
解决方案
可以通过利用可选的 MARKER_ERROR_TRACE=1
和 MARKER_LOG 环境变量来请求额外的诊断日志输出。变量
MARKER_ERROR_TRACE=1
用于在错误发生的点启用 spantrace 和 backtrace 的捕获。变量 MARKER_LOG
用于配置 tracing
日志框架的 EnvFilter
,启用更详细的日志记录和 spantrace 捕获。
向 marker
开发者报告错误
如果用户认为错误是 marker
代码中的bug,他们应该能够将此错误报告为GitHub问题。用户应能方便地收集有关bug上下文的诊断信息并将其发送给我们。
解决方案
用户可以通过将其重定向到文件或复制终端日志(如果日志不多)来捕获他们运行的 cargo-marker
命令的输出。理想情况下,cargo-marker
应该自动或通过单独的子命令收集尽可能多的相关信息。有关这方面的更多信息,请参阅历史跟踪段落。
marker
开发者的视角
报告bug
报告代码中的bug应该很简单。bug的结果应该是可观察的。如果bug是关键的,那么进程很可能会被关闭,但如果受损的子系统不会阻碍程序的执行,那么也应该能够生成警告级别的事件。
解决方案
这与向marker开发者报告错误相同,但在报告bug的情况下,我们会自动收集诊断信息,尽可能减少用户交互。例如,无论 RUST_BACKTRACE
环境变量如何,我们都会收集spantrace和backtrace。为此,我们在 cargo-marker
中有一个自定义的panic hook来完成这项工作。
报告用户可恢复的错误
一些错误可能是由用户自己造成的。例如,用户使用TOML格式编写了配置,其中包含一些无效数据,例如指定了一个不存在的lint crate。这种错误是预期发生的,并且不是 marker
代码中的bug。
解决方案
此crate提供了一种报告此类错误的方法,与报告bug不同,这样在发生此类错误时不会让用户淹没在大量诊断信息中。
此crate尽可能少地采取行动来处理用户可恢复的错误。通常使用错误枚举,但人们经常过度投资于此。枚举通常用于需要结构化错误处理的情况,其中可能需要采取不同的操作来修复某种特殊的错误,并且调用代码希望将其与其他所有错误分开。
cargo-marker
中的大多数错误只是传播给调用者,以便在 cargo-marker
中没有代码会匹配它们。这种错误可以直接在生成它们的代码中创建。没有必要为新类型错误创建新的枚举变体。如果确实需要针对特定类型的错误进行匹配,则可以创建错误变体。
作为先例,您可以查看 cargo
的代码库。他们一直在使用 anyhow
来处理所有错误,并且这可能对他们来说一直工作得很好。主观地说,他们可能过度使用了 anyhow
,因为在一些地方他们不得不 downcast_ref
错误以进行结构化错误处理。因此,此crate采用了一种结合的方法,我们仍然有错误枚举,但该枚举具有用于未分类错误的特殊变体,这些变体只是传播给用户。如果需要在我们的代码中特别处理错误,我们可以在任何时候为特殊情况创建新的变体。
在GitHub上审查问题
当用户在 marker
中发现错误时,他们所写的报告应该包含足够关于错误背景的诊断信息。即使返回给用户的错误是可恢复的,也应有方法让用户收集尽可能多的诊断信息。在这种情况下,我们希望有一种能力,可以强制我们的错误报告机制收集与检测到错误时相同量的信息。
解决方案
使用 MARKER_LOG
和 MARKER_ERROR_TRACE
环境变量,我们可以要求用户以更高的日志级别重新运行 cargo-marker
。这将捕获他们报告的错误的事件跟踪和堆栈跟踪。理想情况下,用户可以将由 cargo-marker
生成的操作历史日志附件给我们(见下文)。
获取应用程序的历史跟踪
一类非常罕见且令人烦恼的错误是那些难以复制的错误。例如,当用户的工具链版本有偏差或只有在他们机器上存在的特殊环境变量以某种方式破坏了我们的逻辑时。这类只在单个机器上复制而不在其他机器上复制的“不可靠”错误正是这类的一部分。还有其他潜在的过程调度和执行顺序的潜在错误,这些错误根本无法稳定地复制。
对于这种情况,最好是有一个用户执行操作的历史记录,其中包含他们在执行过程中收集的所有遥测信息的完整量。这个历史记录仅供 marker
开发者使用,以恢复导致 marker
出现错误状态的行动链。这样,我们就可以通过查看历史记录来调试这种情况,即使我们无法在本地复制该问题。
重要的是,历史记录必须包含 marker
独立运行的跟踪。这意味着它应该在进程运行之间持久存储。例如,如果用户安装了工具链然后运行 marker
检查,我们希望看到这两个动作都在历史记录中。
解决方案(尚未实现)
⚠️ 本节描述的功能尚未实现,因为它不是非常关键。我们可能在未来的某个时候实现它。
cargo-marker
可能在某些已知位置存储日志文件,记录其执行的日志。日志文件应进行轮换并限制大小,以防止耗尽磁盘空间。然后用户应该能够通过将日志文件附加在 GitHub 问题中发送给我们。在 cargo-marker
中可能有子命令,用于导出日志文件的存档,从而使用户更容易附加日志。
贡献
非常欢迎贡献!如果您遇到任何问题或对改进有建议,请查看 Marker 的 GitHub 存储库。
许可
版权所有(c)2022-2023 Rust-Marker
Rust-marker 在 MIT 许可证或 Apache 许可证(版本 2.0)的条款下分发。
依赖
~5–15MB
~171K SLoC