Env_logger 0.5.0-rc.1

Following the recent announcement of log 0.4.0-rc.1 I'm excited to announce an 0.5.0-rc.1 version of env_logger! It now looks a bit like this:

See the release notes for some more details and breaking changes.

The env_logger crate is what you would call a sink for the log crate. It allows you to print log records created using log's macros to stdout or stderr. The plan for env_logger going forward is good terminal logging with minimal friction. So in this release we've taken the crate's existing features and tried to make them all just that little bit better. Some highlights include:

  • You can now parse a custom environment variable as your logging filter instead of RUST_LOG
  • All records are now buffered using thread-local, color-aware buffers instead of being written to Strings
  • We now add a timestamp in the default format

We need your feedback!

This is an RC release so we'd really appreciate your thoughts on it before we call it 5.0.0 proper. Some things in particular:

  • This release introduces some new dependencies; chrono and termcolor
  • There's an API for coloring portions of the record and grabbing a timestamp, but it's not publicly exposed yet. We need some more input on how users would expect that to work. Please chime in if you have any thoughts!
  • Should we pull the log filter parsing into an entirely separate crate?

If you get a chance to try it out or dig through the source I'd love to hear your thoughts!

11 Likes

This is entirely petty, but my OCD is killing me to see those timestamps (a string of constant length) appear after the loglevel (a string of variable length).

7 Likes

Exactly! Please pad the log level output to 5 characters!

Edit: Made a PR:

https://github.com/sebasmagri/env_logger/pull/41

4 Likes

I'd prefer swapping the two fields.

3 Likes

Merged :wink: Thanks @killercup!

2 Likes

Hi all!

I'd like to get a colouring API available in the final 0.5.0 release so am working on cleaning it up. I'd like to know what you all think.

I've based this first pass on the term-painter API, which is nice and clear to use:

let mut level_style = buf.style();
let mut ts_style = buf.style();

match level {
    Level::Error => level_style.set_color(Color::Red).set_bold(true),
    ...
}

ts_style.set_color(Color::White).set_bg(Color::Yellow);

writeln!(buf, "{}: {}: {}", level_style.value(level), ts_style.value(timestamp), args)

Some thoughts:

  • This design is mostly working around issues with mutably borrowing the Formatter for writing and then borrowing again to apply styles. I opted against having an ambient type for styling like term-painter because this styling is really only relevant to env_logger's custom formats, so I wanted to limit the scope styles can be declared and used
  • The Style returned by .style is tied to that buffer we pass to the formatting method. If you use a style but don't write to the given buffer (say you use println instead) then you won't see any styling applied. This highlights an expectation that all custom formats will use the buffer provided to write records to
  • The Style uses builders that take &mut self and return &mut Self
  • The Style does not implement formatting traits. You call .value and get a StyledValue<'a, T> which can be formatted, applying the style. Since it's borrowed you can use the same Style for multiple values
  • You might get strange results if you create nested structures of StyledValues and wrap those in another Style

I have a proof-of-concept implementation here but wanted to see if anyone had other ideas before tidying it up.

Here's the approach I took in log4rs for colors: log4rs::encode::Write - Rust.

Thanks! This is similar to the API termcolor offers that we're sitting on top of.

I quite like the idea of gathering all the styles up and writing them with a single format expression, but if we don't have that nasty implicit behaviour of the std::fmt::Formatter observing the style changes applied by the StyledValue then the implementation is a bit simpler.

Right now, after each styled value is written we reset the terminal style. I'll find out exactly what reset means in termcolor and see if it's safe to apply one style on top of another and have it reset to that. Maybe then we could offer a similar .set_style API too that's cheaper that using .style, but maybe less ergonomic.

Hi everyone!

I've published another pre-release of env_logger. I think there's enough here that we can run with and continue to improve in non-breaking changes. If there aren't any issues in the next week or so then we can push 0.5.0 out the door.

What's changed

EDIT: I copied the snippet below from a PR and forgot to include some other important changes:

  • @matthiasbeyer upgraded to the released log 0.4.0
  • @rukai fixed some spelling errors in the docs
  • @killercup made the log format nicer by making the levels the same width in the output

Sorry for the oversight!

Disabling colors

Adds a new builder property for whether or not to include colors. By default this is RUST_LOG_STYLE, but it can be overridden.

Setting this environment variable to never will disable colors and other styles:

$ export RUST_LOG_STYLE=never
$ ./my-app

Valid values are:

  • auto (or missing/invalid) will decide whether or not the terminal supports colors
  • always will always use colors
  • never will never use colors

In order to support multiple environment variables, I've refactored our from_env functions to accept a generic T: Into<Env>, where Env is a container for the environment variables we care about. These methods can now be called in a few ways:

// reads filters from `MY_LOG` and styles from `RUST_LOG_STYLE`
env_logger::init_from_env("MY_LOG");

// reads filters from `MY_LOG` and styles from `MY_LOG_STYLE`
env_logger::init_from_env(Env::default().filter("MY_LOG").write_style("MY_LOG_STYLE"));

This lets us add new environment variables in the future without potentially breaking people. But it does mean if you're overriding all environment variables that new ones could slip in without you noticing.

Using alternative environment variables

Since we use two environment variables to configure the logger we need an ergonomic way to pass different combinations of those variables to from_env methods. This PR adds an Env type with builder methods for naming environment variables:

env_logger::init_from_env(Env::new().filter("MY_LOG"));

With a few From conversions, the above is also equivalent to:

env_logger::init_from_env("MY_LOG");

Whether or not we want to keep these conversions is up for discussion.

Writing colors

The color API has been refactored and made public so you can use them in your own formats:

let mut style = buf.style();

style.set_color(Color::Red).set_bold(true).set_bg(Color::White);

writeln!(buf, "{}", style.value(42))

This saves you from having to split the writes into multiple calls and juggle Result types.

Writing timestamps

Oh the Formatter.timestamp method is now also public so you can grab timestamps.

1 Like