|
1 | 1 | use std::{fmt, io::Write};
|
2 | 2 |
|
| 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 | + |
3 | 14 | #[cfg(feature = "otel")]
|
4 | 15 | use once_cell::sync::Lazy;
|
5 | 16 | #[cfg(feature = "otel")]
|
6 | 17 | 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}; |
11 | 18 |
|
12 |
| -use crate::currentprocess::{process, terminalsource}; |
| 19 | +use crate::{currentprocess::Process, utils::notify::NotificationLevel}; |
13 | 20 |
|
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 ) * ) ) |
16 | 23 | }
|
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 ) * ) ) |
19 | 27 | }
|
| 28 | + |
20 | 29 | macro_rules! info {
|
21 |
| - ( $ ( $ arg : tt ) * ) => ( $crate::cli::log::info_fmt ( format_args ! ( $ ( $ arg ) * ) ) ) |
| 30 | + ( $ ( $ arg : tt ) * ) => ( ::tracing::info ! ( $ ( $ arg ) * ) ) |
22 | 31 | }
|
23 | 32 |
|
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 ) * ) ) |
26 | 35 | }
|
27 | 36 |
|
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 ) * ) ) |
30 | 39 | }
|
31 | 40 |
|
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}; |
41 | 43 |
|
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 | + } |
50 | 55 | }
|
51 | 56 |
|
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 | + } |
59 | 85 | }
|
60 | 86 |
|
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 | + } |
69 | 118 | }
|
70 | 119 |
|
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 | + } |
80 | 129 | }
|
81 | 130 | }
|
82 | 131 |
|
83 | 132 | /// A [`tracing::Subscriber`] [`Layer`][`tracing_subscriber::Layer`] that corresponds to Rustup's
|
84 | 133 | /// optional `opentelemetry` (a.k.a. `otel`) feature.
|
85 | 134 | #[cfg(feature = "otel")]
|
86 |
| -pub fn telemetry<S>() -> impl Layer<S> |
| 135 | +fn telemetry<S>(process: Process) -> impl Layer<S> |
87 | 136 | where
|
88 | 137 | S: Subscriber + for<'span> LookupSpan<'span>,
|
89 | 138 | {
|
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 | + }; |
92 | 147 | tracing_opentelemetry::layer()
|
93 | 148 | .with_tracer(TELEMETRY_DEFAULT_TRACER.clone())
|
94 | 149 | .with_filter(env_filter)
|
|
0 commit comments