04-与人沟通

面向终端用户的输出与交互

译文 · 基于 Command Line Applications in Rust

与人沟通

原文链接: https://rust-cli.github.io/book/in-depth/human-communication.html

请先阅读教程中的 CLI 输出一章。那一章讲如何向终端写入输出,而本章讨论的是要输出什么。

一切正常时

即使一切正常,报告应用进度也很有用。这些消息应尽量信息充分且简洁。日志里不要用过于技术化的术语。记住:应用并没有崩溃,用户没有理由去查找错误。

最重要的是,沟通风格要一致。使用相同的前缀和句子结构,让日志易于扫读。

尽量让应用输出讲述一个故事:它在做什么,以及对用户有何影响。这可以表现为展示相关步骤的时间线,甚至为长时间操作显示进度条和指示器。用户在任何时候都不应觉得应用在做一些神秘、跟不上的事情。

难以判断发生了什么时

在沟通非正常状态时,一致性同样重要。一个大量打日志却不遵循严格日志级别的应用,提供的信息量可能与完全不打日志的应用相当,甚至更少。

因此,重要的是为事件及其相关消息定义严重程度,并对它们使用一致的日志级别。这样用户就可以通过 --verbose 标志或环境变量(如 RUST_LOG)自行选择日志量。

常用的 log crate 定义了以下级别(按严重程度递增排列):

  • trace
  • debug
  • info
  • warning
  • error

把 info 当作默认日志级别是个好主意。用它输出,嗯,信息性内容。(一些偏好更安静输出风格的应用,默认可能只显示 warning 和 error。)

此外,在各条日志消息中使用相似的前缀和句子结构总是好的,这样便于用 grep 一类工具过滤。一条消息本身应提供足够上下文,以便在过滤后的日志里仍有用,同时又不要过于冗长。

日志语句示例

1
error: could not find `Cargo.toml` in `/home/you/project/`
1
2
=> Downloading repository index
=> Downloading packages...

以下日志输出取自 wasm-pack:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 [1/7] Adding WASM target...
 [2/7] Compiling to WASM...
 [3/7] Creating a pkg directory...
 [4/7] Writing a package.json...
 > [WARN]: Field `description` is missing from Cargo.toml. It is not necessary, but recommended
 > [WARN]: Field `repository` is missing from Cargo.toml. It is not necessary, but recommended
 > [WARN]: Field `license` is missing from Cargo.toml. It is not necessary, but recommended
 [5/7] Copying over your README...
 > [WARN]: origin crate has no README
 [6/7] Installing WASM-bindgen...
 > [INFO]: wasm-bindgen already installed
 [7/7] Running WASM-bindgen...
 Done in 1 second

发生 panic 时

一个常被忽略的方面是:程序崩溃时也会有输出。在 Rust 中,「崩溃」最常见的是「panic」(即「受控的崩溃」,相对于「被操作系统杀掉」)。默认情况下,发生 panic 时,「panic 处理器」会向控制台打印一些信息。

例如,如果你用 cargo new --bin foo 创建一个新的二进制项目,并把 fn main 的内容替换为 panic!("Hello World"),运行程序时会得到:

1
2
thread 'main' panicked at 'Hello, world!', src/main.rs:2:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

这对你(开发者)很有用。(惊喜:程序因 main.rs 文件第 2 行而崩溃。)但对连源码都没有的用户来说,这没什么价值。事实上,它多半只会令人困惑。因此,添加一个自定义的 panic 处理器、提供更面向最终用户的输出是个好主意。

专门做这件事的一个库叫 human-panic。要把它加到你的 CLI 项目中,导入它,并在 main 函数开头调用 setup_panic!() 宏:

1
2
3
4
5
6
7
use human_panic::setup_panic;

fn main() {
   setup_panic!();

   panic!("Hello world")
}

这会显示一条非常友好的消息,并告诉用户可以做什么:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Well, this is embarrassing.

foo had a problem and crashed. To help us diagnose the problem you can send us a crash report.

We have generated a report file at "/var/folders/n3/dkk459k908lcmkzwcmq0tcv00000gn/T/report-738e1bec-5585-47a4-8158-f1f7227f0168.toml". Submit an issue or email with the subject of "foo Crash Report" and include the report as an attachment.

- Authors: Your Name <your.name@example.com>

We take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports.

Thank you kindly!
最后修改 August 11, 2026: 更新 (70a5af133)