Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Owo color #2914

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion examples/Cargo.toml
Expand Up @@ -39,7 +39,7 @@ bytes = "1.2.0"
argh = "0.1.8"

# sloggish example
nu-ansi-term = "0.46.0"
owo-colors = "4.0"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has a very low MSRV (1.56), so it's a fine choice https://github.com/jam1garner/owo-colors/blob/master/Cargo.toml

humantime = "2.1.0"
log = "0.4.17"

Expand Down
40 changes: 11 additions & 29 deletions examples/examples/sloggish/sloggish_collector.rs
@@ -1,4 +1,4 @@
use nu_ansi_term::{Color, Style};
use owo_colors::OwoColorize;
use tracing::{
field::{Field, Visit},
Collect, Id, Level, Metadata,
Expand Down Expand Up @@ -79,13 +79,13 @@ struct ColorLevel<'a>(&'a Level);
impl<'a> fmt::Display for ColorLevel<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self.0 {
Level::TRACE => Color::Purple.paint("TRACE"),
Level::DEBUG => Color::Blue.paint("DEBUG"),
Level::INFO => Color::Green.paint("INFO "),
Level::WARN => Color::Yellow.paint("WARN "),
Level::ERROR => Color::Red.paint("ERROR"),
/* Extra space to keep the width consistent */
Level::TRACE => "TRACE".purple().fmt(f),
Level::DEBUG => "DEBUG".blue().fmt(f),
Level::INFO => "INFO ".green().fmt(f),
Level::WARN => "WARN ".yellow().fmt(f),
Level::ERROR => "ERROR".red().fmt(f),
}
.fmt(f)
}
}

Expand Down Expand Up @@ -117,21 +117,9 @@ impl<'a> Visit for Event<'a> {
.unwrap();
let name = field.name();
if name == "message" {
write!(
&mut self.stderr,
"{}",
// Have to alloc here due to `nu_ansi_term`'s API...
Style::new().bold().paint(format!("{:?}", value))
)
.unwrap();
write!(&mut self.stderr, "{:?}", value.bold()).unwrap();
} else {
write!(
&mut self.stderr,
"{}: {:?}",
Style::new().bold().paint(name),
value
)
.unwrap();
write!(&mut self.stderr, "{}: {:?}", name.bold(), value).unwrap();
}
self.comma = true;
}
Expand Down Expand Up @@ -162,16 +150,10 @@ impl SloggishCollector {
{
let mut kvs = kvs.into_iter();
if let Some((k, v)) = kvs.next() {
write!(
writer,
"{}{}: {}",
leading,
Style::new().bold().paint(k.as_ref()),
v
)?;
write!(writer, "{}{}: {}", leading, k.as_ref().bold(), v)?;
}
for (k, v) in kvs {
write!(writer, ", {}: {}", Style::new().bold().paint(k.as_ref()), v)?;
write!(writer, ", {}: {}", k.as_ref().bold(), v)?;
}
Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions tracing-subscriber/Cargo.toml
Expand Up @@ -29,7 +29,7 @@ alloc = ["tracing-core/alloc"]
std = ["alloc", "tracing-core/std"]
env-filter = ["matchers", "regex", "once_cell", "tracing", "std", "thread_local"]
fmt = ["registry", "std"]
ansi = ["fmt", "nu-ansi-term"]
ansi = ["fmt", "owo-colors"]
registry = ["sharded-slab", "thread_local", "std"]
json = ["tracing-serde", "serde", "serde_json"]

Expand All @@ -49,7 +49,7 @@ once_cell = { optional = true, version = "1.13.0" }

# fmt
tracing-log = { path = "../tracing-log", version = "0.2", optional = true, default-features = false, features = ["log-tracer", "std"] }
nu-ansi-term = { version = "0.46.0", optional = true }
owo-colors = { version = "4.0", optional = true }
time = { version = "0.3.2", features = ["formatting"], optional = true }

# only required by the json feature
Expand Down
49 changes: 19 additions & 30 deletions tracing-subscriber/src/filter/env/builder.rs
Expand Up @@ -210,46 +210,35 @@ impl Builder {
}

if !disabled.is_empty() {
#[cfg(feature = "nu_ansi_term")]
use nu_ansi_term::{Color, Style};
#[cfg(feature = "ansi")]
use owo_colors::{OwoColorize, XtermColors};
// NOTE: We can't use a configured `MakeWriter` because the EnvFilter
// has no knowledge of any underlying subscriber or collector, which
// may or may not use a `MakeWriter`.
let warn = |msg: &str| {
#[cfg(not(feature = "nu_ansi_term"))]
let msg = format!("warning: {}", msg);
#[cfg(feature = "nu_ansi_term")]
let msg = {
let bold = Style::new().bold();
let mut warning = Color::Yellow.paint("warning");
warning.style_ref_mut().is_bold = true;
format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg))
};
eprintln!("{}", msg);
#[cfg(not(feature = "ansi"))]
eprintln!("warning: {}", msg);
#[cfg(feature = "ansi")]
eprintln!("{}{} {}", "warning".yellow().bold(), ":".bold(), msg.bold());
};
let ctx_prefixed = |prefix: &str, msg: &str| {
#[cfg(not(feature = "nu_ansi_term"))]
let msg = format!("{} {}", prefix, msg);
#[cfg(feature = "nu_ansi_term")]
let msg = {
let mut equal = Color::Fixed(21).paint("="); // dark blue
equal.style_ref_mut().is_bold = true;
format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg)
};
eprintln!("{}", msg);
#[cfg(not(feature = "ansi"))]
eprintln!("{} {}", prefix, msg);
#[cfg(feature = "ansi")]
eprintln!(
" {} {} {}",
"=".color(XtermColors::Blue).bold(),
prefix.bold(),
msg
);
};
let ctx_help = |msg| ctx_prefixed("help:", msg);
let ctx_note = |msg| ctx_prefixed("note:", msg);
let ctx = |msg: &str| {
#[cfg(not(feature = "nu_ansi_term"))]
let msg = format!("note: {}", msg);
#[cfg(feature = "nu_ansi_term")]
let msg = {
let mut pipe = Color::Fixed(21).paint("|");
pipe.style_ref_mut().is_bold = true;
format!(" {} {}", pipe, msg)
};
eprintln!("{}", msg);
#[cfg(not(feature = "ansi"))]
eprintln!("note: {}", msg);
#[cfg(feature = "ansi")]
eprintln!(" {} {}", "|".color(XtermColors::Blue).bold(), msg);
};
warn("some trace filter directives would enable traces that are disabled statically");
for directive in disabled {
Expand Down
136 changes: 83 additions & 53 deletions tracing-subscriber/src/fmt/format/mod.rs
Expand Up @@ -46,7 +46,7 @@ use tracing_core::{
use tracing_log::NormalizeEvent;

#[cfg(feature = "ansi")]
use nu_ansi_term::{Color, Style};
use owo_colors::{Style, Styled};

#[cfg(feature = "json")]
mod json;
Expand Down Expand Up @@ -103,7 +103,7 @@ use fmt::{Debug, Display};
/// does not support ANSI escape codes (such as a log file), and they should
/// not be emitted.
///
/// Crates like [`nu_ansi_term`] and [`owo-colors`] can be used to add ANSI
/// Crates like [`owo-colors`] and [`nu_ansi_term`] can be used to add ANSI
/// escape codes to formatted output.
///
/// * The actual [`Event`] to be formatted.
Expand Down Expand Up @@ -644,7 +644,6 @@ impl<F, T> Format<F, T> {
/// tracing_subscriber::fmt()
/// .pretty()
/// .with_ansi(false)
/// .fmt_fields(format::PrettyFields::new().with_ansi(false))
/// // ... other settings ...
/// .init();
/// ```
Expand Down Expand Up @@ -851,32 +850,8 @@ impl<F, T> Format<F, T> {
return Ok(());
}

// If ANSI color codes are enabled, format the timestamp with ANSI
// colors.
#[cfg(feature = "ansi")]
{
if writer.has_ansi_escapes() {
let style = Style::new().dimmed();
write!(writer, "{}", style.prefix())?;

// If getting the timestamp failed, don't bail --- only bail on
// formatting errors.
if self.timer.format_time(writer).is_err() {
writer.write_str("<unknown time>")?;
}

write!(writer, "{} ", style.suffix())?;
return Ok(());
}
}

// Otherwise, just format the timestamp without ANSI formatting.
// If getting the timestamp failed, don't bail --- only bail on
// formatting errors.
if self.timer.format_time(writer).is_err() {
writer.write_str("<unknown time>")?;
}
writer.write_char(' ')
let dimmed = writer.dimmed();
stefnotch marked this conversation as resolved.
Show resolved Hide resolved
write!(writer, "{} ", dimmed.paint(FormatTimeDisplay(&self.timer)))
}
}

Expand Down Expand Up @@ -1024,10 +999,9 @@ where
if let Some(line_number) = line_number {
write!(
writer,
"{}{}:{} ",
dimmed.prefix(),
line_number,
dimmed.suffix()
"{}{} ",
dimmed.paint(line_number),
dimmed.paint(":")
)?;
}

Expand Down Expand Up @@ -1094,14 +1068,7 @@ where

if self.display_line_number {
if let Some(line_number) = meta.line() {
write!(
writer,
"{}{}{}{}",
dimmed.prefix(),
line_number,
dimmed.suffix(),
dimmed.paint(":")
)?;
write!(writer, "{}{}", dimmed.paint(line_number), dimmed.paint(":"))?;
}
}

Expand Down Expand Up @@ -1290,7 +1257,12 @@ impl<'a> Display for ErrorSourceList<'a> {
}
}

trait StylePainter {
fn paint<T>(&self, d: T) -> Styled<T>;
}

#[cfg(not(feature = "ansi"))]
#[derive(Copy, Clone)]
struct Style;

#[cfg(not(feature = "ansi"))]
Expand All @@ -1299,16 +1271,59 @@ impl Style {
Style
}

fn paint(&self, d: impl fmt::Display) -> impl fmt::Display {
d
fn bold(self) -> Self {
self
}

fn dimmed(self) -> Self {
self
}

fn prefix(&self) -> impl fmt::Display {
""
fn italic(self) -> Self {
self
}
}
#[cfg(not(feature = "ansi"))]
impl StylePainter for Style {
fn paint<T>(&self, d: T) -> Styled<T> {
Styled { target: d }
}
}

#[cfg(not(feature = "ansi"))]
struct Styled<T> {
target: T,
}

#[cfg(not(feature = "ansi"))]
macro_rules! impl_fmt {
($($trait:path),* $(,)?) => {
$(
impl<T: $trait> $trait for Styled<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T as $trait>::fmt(&self.target, f)
}
}
)*
};
}
#[cfg(not(feature = "ansi"))]
impl_fmt! {
fmt::Display,
fmt::Debug,
fmt::UpperHex,
fmt::LowerHex,
fmt::Binary,
fmt::UpperExp,
fmt::LowerExp,
fmt::Octal,
fmt::Pointer,
}

fn suffix(&self) -> impl fmt::Display {
""
#[cfg(feature = "ansi")]
impl StylePainter for Style {
fn paint<T>(&self, d: T) -> Styled<T> {
self.style(d)
}
}

Expand Down Expand Up @@ -1415,13 +1430,15 @@ impl<F: LevelNames> fmt::Display for FmtLevel<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "ansi")]
{
// Be careful about importing owo_colors::OwoColorize in a too large scope, since it adds methods to every type.
use owo_colors::OwoColorize;
if self.ansi {
return match self.level {
Level::TRACE => write!(f, "{}", Color::Purple.paint(F::TRACE_STR)),
Level::DEBUG => write!(f, "{}", Color::Blue.paint(F::DEBUG_STR)),
Level::INFO => write!(f, "{}", Color::Green.paint(F::INFO_STR)),
Level::WARN => write!(f, "{}", Color::Yellow.paint(F::WARN_STR)),
Level::ERROR => write!(f, "{}", Color::Red.paint(F::ERROR_STR)),
Level::TRACE => write!(f, "{}", F::TRACE_STR.purple()),
Level::DEBUG => write!(f, "{}", F::DEBUG_STR.blue()),
Level::INFO => write!(f, "{}", F::INFO_STR.green()),
Level::WARN => write!(f, "{}", F::WARN_STR.yellow()),
Level::ERROR => write!(f, "{}", F::ERROR_STR.red()),
};
}
}
Expand Down Expand Up @@ -1648,6 +1665,19 @@ impl Display for TimingDisplay {
}
}

struct FormatTimeDisplay<T>(T);
impl<T: FormatTime> fmt::Display for FormatTimeDisplay<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// If getting the timestamp failed, don't bail --- only bail on
// formatting errors.
if self.0.format_time(&mut Writer::new(f)).is_err() {
f.write_str("<unknown time>")
} else {
Ok(())
}
}
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ended up being the relatively nice workaround for jam1garner/owo-colors#123

#[cfg(test)]
pub(super) mod test {
use crate::fmt::{test::MockMakeWriter, time::FormatTime};
Expand Down Expand Up @@ -1688,7 +1718,7 @@ pub(super) mod test {
#[cfg(feature = "ansi")]
#[test]
fn with_ansi_true() {
let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m \u{1b}[2mtracing_subscriber::fmt::format::test\u{1b}[0m\u{1b}[2m:\u{1b}[0m hello\n";
let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[39m \u{1b}[2mtracing_subscriber::fmt::format::test\u{1b}[0m\u{1b}[2m:\u{1b}[0m hello\n";
stefnotch marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I understand 39 sets the foreground colour to default while 0 resets everything to default. So I guess it is fine here if we don't have other modifiers, but 0 is safer if possible.

assert_info_hello_ansi(true, expected);
}

Expand Down