Coding - arxanas/git-branchless Wiki

Logging

For logging, the project uses the tracing library. You can generate logs using macros like tracing::warn!. These macros support structured logging, so you can directly include variables which you want to log, without having to convert them to strings first:

// Includes the contents of `buffer` in the log message.
warn!(?buffer, "WriteProgress dropped while buffer was not empty");

To display logs at runtime, set the RUST_LOG environment variable. For example:

$ RUST_LOG=info cargo run smartlog

Error-handling

Most of the project uses the eyre library for error-handling. The eyre::Result type allows you to use the ? operator with nearly any error type, rather than having to wrap every third-party library Result into a project- or function-specific Result type.

Span traces

Stack traces are displayed with the help of the color-eyre and tracing-error libraries. Technically, span traces are recorded, not stack traces. These correspond to functions which have been explicitly annotated using the tracing library, such as with #[tracing::instrument]. Other function calls are not included in the span trace.

Example span trace

When the program exits with error, a span trace will appear:

$ cargo run smartlog
   Compiling git-branchless v0.3.4 (/Users/wkhan/workspace/git-branchless)
    Finished dev [unoptimized + debuginfo] target(s) in 5.74s
     Running `target/debug/git-branchless smartlog`
Error:
   0: Could not find repository main branch

Location:
   src/git/repo.rs:302

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

   0: branchless::commands::smartlog::smartlog with effects=<Output fancy=true>
      at src/commands/smartlog.rs:269

The span trace includes branchless::commands::smartlog::smartlog because it is wrapped with #[tracing::instrument]. Also notice that it includes the parameter value effects, along with its Debug representation.

Adding functions to the span trace

When you want a function to appear in the span trace, you must do one of two things:

Including parameter values in the span trace

By default, #[tracing::instrument]-annotated functions will include all of their parameters in the span trace by getting their Debug representations, which is helpful for debugging.

However, parameters should not be included when the Debug representation is very large. A parameter whose type doesn't implement Debug can't be included at all.

To skip printing a certain parameter in the span trace: add a skip to tracing::instrument:

#[instrument(skip(value))]
fn set_config(
    effects: &Effects,
    config: &mut Config,
    name: &str,
    value: impl Into<ConfigValue>,
) -> eyre::Result<()> {

vs. anyhow

The project also uses the similar anyhow library in a few cases:

vs. Result

When specific error values need to be consumed by the rest of the program, rather than reported to the user, the project uses the standard library Result instead (or in addition). If necessary, the thiserror library could also be used to compose these kinds of errors.

Compile times

To keep compile times down, heed the advice in Fast Rust Builds. In particular, you'll see some examples of reducing monomorphization in the codebase:

The git module

The project wraps Git operations into its own git module, rather than use the types from e.g. git2 directly. This is for a few reasons:

Types specific to git2, etc., should not escape the git module. Instead, our own wrapper types should be exposed.