ocli 0.2.0

A simple, opinionated logger for command line tools
Documentation
# ocli

A simple, opinionated logger for command line tools

`ocli` aims at a very simple thing: logging for CLI tools done right. It uses the
`log` crate and the `ansi_term` crate for colors. It provides very few configuration —
at this time, just the expected log level.

## Features

### logs everything to `stderr`

CLI tools are expected to be usable in a pipe. In that context,
the messages addressed to the user must be written on `stderr` to have a chance to be read
by the user, independently of the log level.
The program outputs that are meant to be used with a pipe shouldn't go through the logging
system, but instead be printed to `stdout`, for example with `println!`.

### shows the `Info` message as plain uncolored text

`Info` is expected to be the normal log
level to display messages that are not highlighting a problem and that are not too verbose
for a standard usage of the tool. Because it is intended for messages that are related
to a normal situation, the messages of that level are not prefixed with the log level.

### prefix the messages with their colored log level for any level other than `Info`

The color depends on the log level, allowing to quickly locate a message at a specific log level

### displays the module path and line when configured at the `Trace` log level

for all the
messages, even if they are not at the `Trace` log level. The `Trace` log level is used
to help the developer understand where a message comes from, in addition to display a larger
amount of messages.

### disable all colorization in case the `stderr` is not a tty or when requested by `NO_COLOR`

so the output is not polluted with unreadable characters when `stderr` is redirected to a file.
This crate uses the `colored` crate to color text, which disables colorization when `NO_COLOR` is set.

## Example with `Info` log level

```rust
#[macro_use] extern crate log;

fn main() {
     ocli::init(log::Level::Info).unwrap();

     error!("This is printed to stderr, with the 'error: ' prefix colored in red");
     warn!("This is printed to stderr, with the 'warn: ' prefix colored in yellow");
     info!("This is printed to stderr, without prefix or color");
     debug!("This is not printed");
     trace!("This is not printed");
}
```
![info example](info.png)

## Example with `Trace` log level

```rust
#[macro_use] extern crate log;

fn main() {
     ocli::init(log::Level::Trace).unwrap();

     error!("This is printed to stderr, with the 'path(line): error: ' prefix colored in red");
     warn!("This is printed to stderr, with the 'path(line): warn: ' prefix colored in yellow");
     info!("This is printed to stderr, with the 'path(line): info: ' prefix");
     debug!("This is printed to stderr, with the 'path(line): debug: ' prefix colored in blue");
     trace!("This is printed to stderr, with the 'path(line): trace: ' prefix colored in magenta");
}
```

![trace example](trace.png)

and a more realistic example with [hld](https://github.com/glehmann/hld):

![hld trace](hld.png)

## Example with clap integration

The log level can be configured with a command line argument, using the `clap` crate.
Try running it with `cargo run --example cli -- --log-level trace` and change the log level
to see the difference.

```rust
#[macro_use]
extern crate log;

use clap::Parser;

/// A demo of ocli with clap
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Config {
    /// Log level
    #[arg(short, long, default_value_t = log::Level::Info)]
    pub log_level: log::Level,
}

fn main() {
    let config = Config::parse();
    ocli::init(config.log_level).unwrap();

    println!("this is on stdout — try to pipe it to another command like `grep` or `wc`");
    error!("log at error level on stderr");
    warn!("log at warn level on stderr");
    info!("log at info level on stderr");
    debug!("log at debug level on stderr");
    trace!("log at trace level on stderr");
    info!("the logs at any level are meant to inform the user");
    info!("while still being able to pipe stdout");
}
```

## Example with clap-verbosity-flag integration

The log level can be configured with a command line argument, using the `clap-verbosity-flag` crate.
Try running it with `cargo run --example verbosity -- -vv` or `cargo run --example verbosity -- -q`.
Changing the number of `-v` or `-q` changes the log level.

```rust
#[macro_use]
extern crate log;

use clap::Parser;
use clap_verbosity_flag::{InfoLevel, Verbosity};

/// A demo of ocli with clap
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Config {
    /// Log level
    #[command(flatten)]
    pub verbose: Verbosity<InfoLevel>,
}

fn main() {
    let config = Config::parse();
    if let Some(level) = config.verbose.log_level() {
        ocli::init(level).unwrap();
    }
    println!("this is on stdout — try to pipe it to another command like `grep` or `wc`");
    error!("log at error level on stderr");
    warn!("log at warn level on stderr");
    info!("log at info level on stderr");
    debug!("log at debug level on stderr");
    trace!("log at trace level on stderr");
    info!("the logs at any level are meant to inform the user");
    info!("while still being able to pipe stdout");
}
```

## License

`ocli` is distributed under the terms of the MIT license.

See [LICENSE](LICENSE) for details.