Skip to content

Commit a667805

Browse files
committed
refactor(log): reimplement log using tracing
1 parent 58f8bd1 commit a667805

File tree

4 files changed

+159
-84
lines changed

4 files changed

+159
-84
lines changed

src/bin/rustup-init.rs

+11-22
Original file line numberDiff line numberDiff line change
@@ -54,31 +54,20 @@ fn main() {
5454
}
5555

5656
async fn maybe_trace_rustup() -> Result<utils::ExitCode> {
57-
#[cfg(not(feature = "otel"))]
58-
{
59-
run_rustup().await
60-
}
6157
#[cfg(feature = "otel")]
62-
{
63-
use tracing_subscriber::{layer::SubscriberExt, Registry};
64-
65-
let telemetry = {
66-
use opentelemetry::global;
67-
use opentelemetry_sdk::propagation::TraceContextPropagator;
68-
69-
global::set_text_map_propagator(TraceContextPropagator::new());
70-
rustup::cli::log::telemetry()
71-
};
72-
73-
let subscriber = Registry::default().with(telemetry);
74-
tracing::subscriber::set_global_default(subscriber)?;
75-
let result = run_rustup().await;
76-
// We're tracing, so block until all spans are exported.
77-
opentelemetry::global::shutdown_tracer_provider();
78-
result
79-
}
58+
opentelemetry::global::set_text_map_propagator(
59+
opentelemetry_sdk::propagation::TraceContextPropagator::new(),
60+
);
61+
let subscriber = rustup::cli::log::tracing_subscriber(process());
62+
tracing::subscriber::set_global_default(subscriber)?;
63+
let result = run_rustup().await;
64+
// We're tracing, so block until all spans are exported.
65+
#[cfg(feature = "otel")]
66+
opentelemetry::global::shutdown_tracer_provider();
67+
result
8068
}
8169

70+
// FIXME: Make `tracing::instrument` always run
8271
#[cfg_attr(feature = "otel", tracing::instrument)]
8372
async fn run_rustup() -> Result<utils::ExitCode> {
8473
if let Ok(dir) = process().var("RUSTUP_TRACE_DIR") {

src/cli/log.rs

+113-58
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,149 @@
11
use std::{fmt, io::Write};
22

3+
use termcolor::{Color, ColorSpec, WriteColor};
4+
use tracing::{level_filters::LevelFilter, Event, Subscriber};
5+
use tracing_subscriber::{
6+
fmt::{
7+
format::{self, FormatEvent, FormatFields},
8+
FmtContext,
9+
},
10+
registry::LookupSpan,
11+
EnvFilter, Layer,
12+
};
13+
314
#[cfg(feature = "otel")]
415
use once_cell::sync::Lazy;
516
#[cfg(feature = "otel")]
617
use opentelemetry_sdk::trace::Tracer;
7-
#[cfg(feature = "otel")]
8-
use tracing::Subscriber;
9-
#[cfg(feature = "otel")]
10-
use tracing_subscriber::{registry::LookupSpan, EnvFilter, Layer};
1118

12-
use crate::currentprocess::{process, terminalsource};
19+
use crate::{currentprocess::Process, utils::notify::NotificationLevel};
1320

14-
macro_rules! warn {
15-
( $ ( $ arg : tt ) * ) => ( $crate::cli::log::warn_fmt ( format_args ! ( $ ( $ arg ) * ) ) )
21+
macro_rules! debug {
22+
( $ ( $ arg : tt ) * ) => ( ::tracing::trace ! ( $ ( $ arg ) * ) )
1623
}
17-
macro_rules! err {
18-
( $ ( $ arg : tt ) * ) => ( $crate::cli::log::err_fmt ( format_args ! ( $ ( $ arg ) * ) ) )
24+
25+
macro_rules! verbose {
26+
( $ ( $ arg : tt ) * ) => ( ::tracing::debug ! ( $ ( $ arg ) * ) )
1927
}
28+
2029
macro_rules! info {
21-
( $ ( $ arg : tt ) * ) => ( $crate::cli::log::info_fmt ( format_args ! ( $ ( $ arg ) * ) ) )
30+
( $ ( $ arg : tt ) * ) => ( ::tracing::info ! ( $ ( $ arg ) * ) )
2231
}
2332

24-
macro_rules! verbose {
25-
( $ ( $ arg : tt ) * ) => ( $crate::cli::log::verbose_fmt ( format_args ! ( $ ( $ arg ) * ) ) )
33+
macro_rules! warn {
34+
( $ ( $ arg : tt ) * ) => ( ::tracing::warn ! ( $ ( $ arg ) * ) )
2635
}
2736

28-
macro_rules! debug {
29-
( $ ( $ arg : tt ) * ) => ( $crate::cli::log::debug_fmt ( format_args ! ( $ ( $ arg ) * ) ) )
37+
macro_rules! err {
38+
( $ ( $ arg : tt ) * ) => ( ::tracing::error ! ( $ ( $ arg ) * ) )
3039
}
3140

32-
pub(crate) fn warn_fmt(args: fmt::Arguments<'_>) {
33-
let mut t = process().stderr().terminal();
34-
let _ = t.fg(terminalsource::Color::Yellow);
35-
let _ = t.attr(terminalsource::Attr::Bold);
36-
let _ = write!(t.lock(), "warning: ");
37-
let _ = t.reset();
38-
let _ = t.lock().write_fmt(args);
39-
let _ = writeln!(t.lock());
40-
}
41+
pub fn tracing_subscriber(process: Process) -> impl tracing::Subscriber {
42+
use tracing_subscriber::{layer::SubscriberExt, Registry};
4143

42-
pub(crate) fn err_fmt(args: fmt::Arguments<'_>) {
43-
let mut t = process().stderr().terminal();
44-
let _ = t.fg(terminalsource::Color::Red);
45-
let _ = t.attr(terminalsource::Attr::Bold);
46-
let _ = write!(t.lock(), "error: ");
47-
let _ = t.reset();
48-
let _ = t.lock().write_fmt(args);
49-
let _ = writeln!(t.lock());
44+
#[cfg(feature = "otel")]
45+
let telemetry = telemetry(process.clone());
46+
let console_logger = console_logger(process);
47+
#[cfg(feature = "otel")]
48+
{
49+
Registry::default().with(console_logger).with(telemetry)
50+
}
51+
#[cfg(not(feature = "otel"))]
52+
{
53+
Registry::default().with(console_logger)
54+
}
5055
}
5156

52-
pub(crate) fn info_fmt(args: fmt::Arguments<'_>) {
53-
let mut t = process().stderr().terminal();
54-
let _ = t.attr(terminalsource::Attr::Bold);
55-
let _ = write!(t.lock(), "info: ");
56-
let _ = t.reset();
57-
let _ = t.lock().write_fmt(args);
58-
let _ = writeln!(t.lock());
57+
/// A [`tracing::Subscriber`] [`Layer`][`tracing_subscriber::Layer`] that prints out the log
58+
/// lines to the current [`Process`]' `stderr`.
59+
///
60+
/// When the `RUST_LOG` environment variable is present, a standard [`tracing_subscriber`]
61+
/// formatter will be used according to the filtering directives set in its value.
62+
/// Otherwise, this logger will use [`EventFormatter`] to mimic "classic" Rustup `stderr` output.
63+
fn console_logger<S>(process: Process) -> impl Layer<S>
64+
where
65+
S: Subscriber + for<'span> LookupSpan<'span>,
66+
{
67+
let maybe_rust_log_directives = process.var_os("RUST_LOG").clone();
68+
let has_ansi = process.stderr().is_a_tty();
69+
let logger = tracing_subscriber::fmt::layer()
70+
.with_writer(move || process.stderr())
71+
.with_ansi(has_ansi);
72+
if let Some(directives) = maybe_rust_log_directives {
73+
let env_filter = EnvFilter::builder()
74+
.with_default_directive(LevelFilter::INFO.into())
75+
.parse_lossy(directives.to_string_lossy());
76+
logger.compact().with_filter(env_filter).boxed()
77+
} else {
78+
// Receive log lines from Rustup only.
79+
let env_filter = EnvFilter::new("rustup=DEBUG");
80+
logger
81+
.event_format(EventFormatter)
82+
.with_filter(env_filter)
83+
.boxed()
84+
}
5985
}
6086

61-
pub(crate) fn verbose_fmt(args: fmt::Arguments<'_>) {
62-
let mut t = process().stderr().terminal();
63-
let _ = t.fg(terminalsource::Color::Magenta);
64-
let _ = t.attr(terminalsource::Attr::Bold);
65-
let _ = write!(t.lock(), "verbose: ");
66-
let _ = t.reset();
67-
let _ = t.lock().write_fmt(args);
68-
let _ = writeln!(t.lock());
87+
// Adapted from
88+
// https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/trait.FormatEvent.html#examples
89+
struct EventFormatter;
90+
91+
impl<S, N> FormatEvent<S, N> for EventFormatter
92+
where
93+
S: Subscriber + for<'a> LookupSpan<'a>,
94+
N: for<'a> FormatFields<'a> + 'static,
95+
{
96+
fn format_event(
97+
&self,
98+
ctx: &FmtContext<'_, S, N>,
99+
mut writer: format::Writer<'_>,
100+
event: &Event<'_>,
101+
) -> fmt::Result {
102+
let has_ansi = writer.has_ansi_escapes();
103+
let level = NotificationLevel::from(*event.metadata().level());
104+
{
105+
let mut buf = termcolor::Buffer::ansi();
106+
if has_ansi {
107+
_ = buf.set_color(ColorSpec::new().set_bold(true).set_fg(level.fg_color()));
108+
}
109+
_ = write!(buf, "{level}: ");
110+
if has_ansi {
111+
_ = buf.reset();
112+
}
113+
writer.write_str(std::str::from_utf8(buf.as_slice()).unwrap())?;
114+
}
115+
ctx.field_format().format_fields(writer.by_ref(), event)?;
116+
writeln!(writer)
117+
}
69118
}
70119

71-
pub(crate) fn debug_fmt(args: fmt::Arguments<'_>) {
72-
if process().var("RUSTUP_DEBUG").is_ok() {
73-
let mut t = process().stderr().terminal();
74-
let _ = t.fg(terminalsource::Color::Blue);
75-
let _ = t.attr(terminalsource::Attr::Bold);
76-
let _ = write!(t.lock(), "debug: ");
77-
let _ = t.reset();
78-
let _ = t.lock().write_fmt(args);
79-
let _ = writeln!(t.lock());
120+
impl NotificationLevel {
121+
fn fg_color(&self) -> Option<Color> {
122+
match self {
123+
NotificationLevel::Debug => Some(Color::Blue),
124+
NotificationLevel::Verbose => Some(Color::Magenta),
125+
NotificationLevel::Info => None,
126+
NotificationLevel::Warn => Some(Color::Yellow),
127+
NotificationLevel::Error => Some(Color::Red),
128+
}
80129
}
81130
}
82131

83132
/// A [`tracing::Subscriber`] [`Layer`][`tracing_subscriber::Layer`] that corresponds to Rustup's
84133
/// optional `opentelemetry` (a.k.a. `otel`) feature.
85134
#[cfg(feature = "otel")]
86-
pub fn telemetry<S>() -> impl Layer<S>
135+
fn telemetry<S>(process: Process) -> impl Layer<S>
87136
where
88137
S: Subscriber + for<'span> LookupSpan<'span>,
89138
{
90-
// NOTE: This reads from the real environment variables instead of `process().var_os()`.
91-
let env_filter = EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("INFO"));
139+
let maybe_rust_log_directives = process.var_os("RUST_LOG").clone();
140+
let env_filter = if let Some(directives) = maybe_rust_log_directives {
141+
EnvFilter::builder()
142+
.with_default_directive(LevelFilter::INFO.into())
143+
.parse_lossy(directives.to_string_lossy())
144+
} else {
145+
EnvFilter::new("rustup=DEBUG")
146+
};
92147
tracing_opentelemetry::layer()
93148
.with_tracer(TELEMETRY_DEFAULT_TRACER.clone())
94149
.with_filter(env_filter)

src/currentprocess.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::{
1717

1818
#[cfg(feature = "test")]
1919
use rand::{thread_rng, Rng};
20+
use tracing_subscriber::util::SubscriberInitExt;
2021

2122
pub mod filesource;
2223
pub mod terminalsource;
@@ -181,7 +182,8 @@ where
181182
if let Some(old_p) = &*p.borrow() {
182183
panic!("current process already set {old_p:?}");
183184
}
184-
*p.borrow_mut() = Some(process);
185+
*p.borrow_mut() = Some(process.clone());
186+
let _guard = crate::cli::log::tracing_subscriber(process).set_default();
185187
let result = f();
186188
*p.borrow_mut() = None;
187189
result
@@ -253,8 +255,11 @@ pub fn with_runtime<'a, R>(
253255
if let Some(old_p) = &*p.borrow() {
254256
panic!("current process already set {old_p:?}");
255257
}
256-
*p.borrow_mut() = Some(process);
257-
let result = runtime.block_on(fut);
258+
*p.borrow_mut() = Some(process.clone());
259+
let result = runtime.block_on(async {
260+
let _guard = crate::cli::log::tracing_subscriber(process).set_default();
261+
fut.await
262+
});
258263
*p.borrow_mut() = None;
259264
result
260265
})

src/utils/notify.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
1+
use std::fmt;
2+
13
#[derive(Debug)]
24
pub(crate) enum NotificationLevel {
5+
Debug,
36
Verbose,
47
Info,
58
Warn,
69
Error,
7-
Debug,
10+
}
11+
12+
impl fmt::Display for NotificationLevel {
13+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
14+
f.write_str(match self {
15+
NotificationLevel::Debug => "debug",
16+
NotificationLevel::Verbose => "verbose",
17+
NotificationLevel::Info => "info",
18+
NotificationLevel::Warn => "warning",
19+
NotificationLevel::Error => "error",
20+
})
21+
}
22+
}
23+
24+
impl From<tracing::Level> for NotificationLevel {
25+
fn from(level: tracing::Level) -> Self {
26+
match level {
27+
tracing::Level::TRACE => Self::Debug,
28+
tracing::Level::DEBUG => Self::Verbose,
29+
tracing::Level::INFO => Self::Info,
30+
tracing::Level::WARN => Self::Warn,
31+
tracing::Level::ERROR => Self::Error,
32+
}
33+
}
834
}

0 commit comments

Comments
 (0)