Communicating with humans

Make sure to read the chapter on CLI outputin the tutorial first.It covers how to write output to the terminal,while this chapter will talk about what to output.

When everything is fine

It is useful to report on the application’s progresseven when everything is fine.Try to be informative and concise in these messages.Don’t use overly technical terms in the logs.Remember:the application is not crashingso there’s no reason for users to look up errors.

Most importantly,be consistent in the style of communication.Use the same prefixes and sentence structureto make the logs easily skimmable.

Try to let your application output tell a storyabout what it’s doingand how it impacts the user.This can involve showing a timeline of steps involvedor even a progress bar and indicator for long-running actions.The user should at no pointget the feeling that the application is doing something mysteriousthat they cannot follow.

When it’s hard to tell what’s going on

When communicating non-nominal state it’s important to be consistent.A heavily logging application that doesn’t follow strict logging levelsprovides the same amount, or even less informationthan a non-logging application.

Because of this,it’s important to define the severity of eventsand messages that are related to it;then use consistent log levels for them.This way users can select the amount of logging themselvesvia —verbose flagsor environment variables (like RUST_LOG).

The commonly used log cratedefines the following levels(ordered by increasing severity):

  • trace
  • debug
  • info
  • warning
  • error

It’s a good idea to think of info as the default log level.Use it for, well, informative output.(Some applications that lean towards a more quiet output stylemight only show warnings and errors by default.)

Additionally,it’s always a good idea to use similar prefixesand sentence structure across log messages,making it easy to use a tool like grep to filter for them.A message should provide enough context by itselfto be useful in a filtered logwhile not being too verbose at the same time.

Example log statements

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

The following log output is taken from wasm-pack:

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

When panicking

One aspect often forgotten is thatyour program also outputs something when it crashes.In Rust, “crashes” are most often “panics”(i.e., “controlled crashing”in contrast to “the operating system killed the process”).By default,when a panic occurs,a “panic handler” will print some information to the console.

For example,if you create a new binary projectwith cargo new —bin fooand replace the content of fn main with panic!("Hello World"),you get this when you run your program:

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

This is useful information to you, the developer.(Surprise: the program crashed because of line 2 in your main.rs file).But for a user who doesn’t even have access to the source code,this is not very valuable.In fact, it most likely is just confusing.That’s why it’s a good idea to add a custom panic handler,that provides a bit more end-user focused output.

One library that does just that is called human-panic.To add it to your CLI project,you import itand call the setup_panic!() macroat the beginning of your main function:

  1. use human_panic::setup_panic;
  2. fn main() {
  3. setup_panic!();
  4. panic!("Hello world")
  5. }

This will now show a very friendly message,and tells the user what they can do:

  1. Well, this is embarrassing.
  2. foo had a problem and crashed. To help us diagnose the problem you can send us a crash report.
  3. 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.
  4. - Authors: Your Name <your.name@example.com>
  5. 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.
  6. Thank you kindly!