Skip to content

Commit

Permalink
fmt::Subscriber NO_COLOR support
Browse files Browse the repository at this point in the history
The `NO_COLOR` environment variable should disable all ANSI color
output (https:://no-color.org).

This commit updates `fmt::Subscriber` to check the `NO_COLOR`
environment variable before enabling ANSI color output.  As described in
the spec, any non-empty value set for `NO_COLOR` will prevent `is_ansi`
from being set to `true`.

fixes tokio-rs#2388
  • Loading branch information
dmlary committed Jul 13, 2023
1 parent 27f688e commit 8fa10c9
Showing 1 changed file with 72 additions and 2 deletions.
74 changes: 72 additions & 2 deletions tracing-subscriber/src/fmt/fmt_subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
};
use format::{FmtSpan, TimingDisplay};
use std::{
any::TypeId, cell::RefCell, fmt, io, marker::PhantomData, ops::Deref, ptr::NonNull,
any::TypeId, cell::RefCell, env, fmt, io, marker::PhantomData, ops::Deref, ptr::NonNull,
time::Instant,
};
use tracing_core::{
Expand Down Expand Up @@ -234,9 +234,15 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {
/// This method is primarily expected to be used with the
/// [`reload::Handle::modify`](crate::reload::Handle::modify) method when changing
/// the writer.
///
/// If the `NO_COLOR` environment variable is set to a non-empty value,
/// `with_ansi(true)` will have no effect.
///
/// [`NO_COLOR` environment variable]: https://no-color.org/
#[cfg(feature = "ansi")]
#[cfg_attr(docsrs, doc(cfg(feature = "ansi")))]
pub fn set_ansi(&mut self, ansi: bool) {
let ansi = ansi & env::var("NO_COLOR").map_or(true, |v| v.is_empty());
self.is_ansi = ansi;
}

Expand Down Expand Up @@ -282,13 +288,18 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {
/// feature flag enabled will panic if debug assertions are enabled, or
/// print a warning otherwise.
///
/// If the `NO_COLOR` environment variable is set to a non-empty value,
/// `with_ansi(true)` will have no effect.
///
/// This method itself is still available without the feature flag. This
/// is to allow ANSI escape codes to be explicitly *disabled* without
/// having to opt-in to the dependencies required to emit ANSI formatting.
/// This way, code which constructs a formatter that should never emit
/// ANSI escape codes can ensure that they are not used, regardless of
/// whether or not other crates in the dependency graph enable the "ansi"
/// feature flag.
///
/// [`NO_COLOR` environment variable]: https://no-color.org/
pub fn with_ansi(self, ansi: bool) -> Self {
#[cfg(not(feature = "ansi"))]
if ansi {
Expand All @@ -300,6 +311,7 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {
eprintln!("{}", ERROR);
}

let ansi = ansi & env::var("NO_COLOR").map_or(true, |v| v.is_empty());
Subscriber {
is_ansi: ansi,
..self
Expand Down Expand Up @@ -680,12 +692,16 @@ impl<C, N, E, W> Subscriber<C, N, E, W> {

impl<C> Default for Subscriber<C> {
fn default() -> Self {
// only enable ANSI when the feature is enabled, and the NO_COLOR
// environment variable is unset or empty.
let ansi = cfg!(feature = "ansi") && env::var("NO_COLOR").map_or(true, |v| v.is_empty());

Subscriber {
fmt_fields: format::DefaultFields::default(),
fmt_event: format::Format::default(),
fmt_span: format::FmtSpanConfig::default(),
make_writer: io::stdout,
is_ansi: cfg!(feature = "ansi"),
is_ansi: ansi,
log_internal_errors: false,
_inner: PhantomData,
}
Expand Down Expand Up @@ -1505,4 +1521,58 @@ mod test {
actual.as_str()
);
}

// Because we need to modify an environment variable for these test cases,
// we do them all in a single test.
#[cfg(feature = "ansi")]
#[test]
fn subscriber_no_color() {
// save off the existing value of NO_COLOR
const NO_COLOR: &str = "NO_COLOR";
let saved_no_color = std::env::var(NO_COLOR);

let cases: Vec<(Option<&str>, bool)> = vec![
(Some("0"), false), // any non-empty value disables ansi
(Some("off"), false), // any non-empty value disables ansi
(Some("1"), false),
(Some(""), true), // empty value does not disable ansi
(None, true),
];

for (var, ansi) in cases {
if let Some(value) = var {
env::set_var(NO_COLOR, value);
} else {
env::remove_var(NO_COLOR);
}

let subscriber: Subscriber<()> = fmt::Subscriber::default();
assert_eq!(
subscriber.is_ansi, ansi,
"NO_COLOR={:?}; Subscriber::default().is_ansi should be {}",
var, ansi
);

let subscriber: Subscriber<()> = fmt::Subscriber::default().with_ansi(true);
assert_eq!(
subscriber.is_ansi, ansi,
"NO_COLOR={:?}; Subscriber::default().with_ansi(true).is_ansi should be {}",
var, ansi
);

let mut subscriber: Subscriber<()> = fmt::Subscriber::default();
subscriber.set_ansi(true);
assert_eq!(
subscriber.is_ansi, ansi,
"NO_COLOR={:?}; subscriber.set_ansi(true); subscriber.is_ansi should be {}",
var, ansi
);
}

if let Ok(value) = saved_no_color {
env::set_var(NO_COLOR, value);
} else {
env::remove_var(NO_COLOR);
}
}
}

0 comments on commit 8fa10c9

Please sign in to comment.