|
| 1 | +//! This crate allows tools to enable rust logging without having to magically |
| 2 | +//! match rustc's tracing crate version. |
| 3 | +//! |
| 4 | +//! For example if someone is working on rustc_ast and wants to write some |
| 5 | +//! minimal code against it to run in a debugger, with access to the `debug!` |
| 6 | +//! logs emitted by rustc_ast, that can be done by writing: |
| 7 | +//! |
| 8 | +//! ```toml |
| 9 | +//! [dependencies] |
| 10 | +//! rustc_ast = { path = "../rust/compiler/rustc_ast" } |
| 11 | +//! rustc_log = { path = "../rust/compiler/rustc_log" } |
| 12 | +//! rustc_span = { path = "../rust/compiler/rustc_span" } |
| 13 | +//! ``` |
| 14 | +//! |
| 15 | +//! ``` |
| 16 | +//! fn main() { |
| 17 | +//! rustc_log::init_rustc_env_logger().unwrap(); |
| 18 | +//! |
| 19 | +//! let edition = rustc_span::edition::Edition::Edition2021; |
| 20 | +//! rustc_span::create_session_globals_then(edition, || { |
| 21 | +//! /* ... */ |
| 22 | +//! }); |
| 23 | +//! } |
| 24 | +//! ``` |
| 25 | +//! |
| 26 | +//! Now `RUSTC_LOG=debug cargo run` will run your minimal main.rs and show |
| 27 | +//! rustc's debug logging. In a workflow like this, one might also add |
| 28 | +//! `std::env::set_var("RUSTC_LOG", "debug")` to the top of main so that `cargo |
| 29 | +//! run` by itself is sufficient to get logs. |
| 30 | +//! |
| 31 | +//! The reason rustc_log is a tiny separate crate, as opposed to exposing the |
| 32 | +//! same things in rustc_driver only, is to enable the above workflow. If you |
| 33 | +//! had to depend on rustc_driver in order to turn on rustc's debug logs, that's |
| 34 | +//! an enormously bigger dependency tree; every change you make to rustc_ast (or |
| 35 | +//! whichever piece of the compiler you are interested in) would involve |
| 36 | +//! rebuilding all the rest of rustc up to rustc_driver in order to run your |
| 37 | +//! main.rs. Whereas by depending only on rustc_log and the few crates you are |
| 38 | +//! debugging, you can make changes inside those crates and quickly run main.rs |
| 39 | +//! to read the debug logs. |
| 40 | +
|
| 41 | +use std::env::{self, VarError}; |
| 42 | +use std::fmt::{self, Display}; |
| 43 | +use std::io; |
| 44 | +use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter}; |
| 45 | +use tracing_subscriber::layer::SubscriberExt; |
| 46 | + |
| 47 | +pub fn init_rustc_env_logger() -> Result<(), Error> { |
| 48 | + init_env_logger("RUSTC_LOG") |
| 49 | +} |
| 50 | + |
| 51 | +/// In contrast to `init_rustc_env_logger` this allows you to choose an env var |
| 52 | +/// other than `RUSTC_LOG`. |
| 53 | +pub fn init_env_logger(env: &str) -> Result<(), Error> { |
| 54 | + let filter = match env::var(env) { |
| 55 | + Ok(env) => EnvFilter::new(env), |
| 56 | + _ => EnvFilter::default().add_directive(Directive::from(LevelFilter::WARN)), |
| 57 | + }; |
| 58 | + |
| 59 | + let color_logs = match env::var(String::from(env) + "_COLOR") { |
| 60 | + Ok(value) => match value.as_ref() { |
| 61 | + "always" => true, |
| 62 | + "never" => false, |
| 63 | + "auto" => stderr_isatty(), |
| 64 | + _ => return Err(Error::InvalidColorValue(value)), |
| 65 | + }, |
| 66 | + Err(VarError::NotPresent) => stderr_isatty(), |
| 67 | + Err(VarError::NotUnicode(_value)) => return Err(Error::NonUnicodeColorValue), |
| 68 | + }; |
| 69 | + |
| 70 | + let layer = tracing_tree::HierarchicalLayer::default() |
| 71 | + .with_writer(io::stderr) |
| 72 | + .with_indent_lines(true) |
| 73 | + .with_ansi(color_logs) |
| 74 | + .with_targets(true) |
| 75 | + .with_indent_amount(2); |
| 76 | + #[cfg(parallel_compiler)] |
| 77 | + let layer = layer.with_thread_ids(true).with_thread_names(true); |
| 78 | + |
| 79 | + let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); |
| 80 | + tracing::subscriber::set_global_default(subscriber).unwrap(); |
| 81 | + |
| 82 | + Ok(()) |
| 83 | +} |
| 84 | + |
| 85 | +pub fn stdout_isatty() -> bool { |
| 86 | + atty::is(atty::Stream::Stdout) |
| 87 | +} |
| 88 | + |
| 89 | +pub fn stderr_isatty() -> bool { |
| 90 | + atty::is(atty::Stream::Stderr) |
| 91 | +} |
| 92 | + |
| 93 | +#[derive(Debug)] |
| 94 | +pub enum Error { |
| 95 | + InvalidColorValue(String), |
| 96 | + NonUnicodeColorValue, |
| 97 | +} |
| 98 | + |
| 99 | +impl std::error::Error for Error {} |
| 100 | + |
| 101 | +impl Display for Error { |
| 102 | + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 103 | + match self { |
| 104 | + Error::InvalidColorValue(value) => write!( |
| 105 | + formatter, |
| 106 | + "invalid log color value '{}': expected one of always, never, or auto", |
| 107 | + value, |
| 108 | + ), |
| 109 | + Error::NonUnicodeColorValue => write!( |
| 110 | + formatter, |
| 111 | + "non-Unicode log color value: expected one of always, never, or auto", |
| 112 | + ), |
| 113 | + } |
| 114 | + } |
| 115 | +} |
0 commit comments