From 581a317bbbbad807fd88eab490516787f1e9249e Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Sat, 11 Nov 2023 13:34:20 +0100
Subject: [PATCH 1/2] rustc_log: provide a way to init logging based on the
 values, not names, of the env vars

---
 compiler/rustc_driver_impl/src/lib.rs   | 10 +++---
 compiler/rustc_log/src/lib.rs           | 45 +++++++++++++++++++------
 src/librustdoc/lib.rs                   |  3 +-
 src/tools/error_index_generator/main.rs |  3 +-
 4 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 84ae45d6a2b6f..37c2a0094951f 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -1524,14 +1524,14 @@ fn report_ice(
 /// This allows tools to enable rust logging without having to magically match rustc's
 /// tracing crate version.
 pub fn init_rustc_env_logger(handler: &EarlyErrorHandler) {
-    init_env_logger(handler, "RUSTC_LOG");
+    init_logger(handler, rustc_log::LoggerConfig::from_env("RUSTC_LOG"));
 }
 
 /// This allows tools to enable rust logging without having to magically match rustc's
-/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var
-/// other than `RUSTC_LOG`.
-pub fn init_env_logger(handler: &EarlyErrorHandler, env: &str) {
-    if let Err(error) = rustc_log::init_env_logger(env) {
+/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose
+/// the values directly rather than having to set an environment variable.
+pub fn init_logger(handler: &EarlyErrorHandler, cfg: rustc_log::LoggerConfig) {
+    if let Err(error) = rustc_log::init_logger(cfg) {
         handler.early_error(error.to_string());
     }
 }
diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs
index 0c9ec556549fa..e3464cb8a2df6 100644
--- a/compiler/rustc_log/src/lib.rs
+++ b/compiler/rustc_log/src/lib.rs
@@ -14,7 +14,7 @@
 //!
 //! ```
 //! fn main() {
-//!     rustc_log::init_env_logger("LOG").unwrap();
+//!     rustc_log::init_logger(rustc_log::LoggerConfig::from_env("LOG")).unwrap();
 //!
 //!     let edition = rustc_span::edition::Edition::Edition2021;
 //!     rustc_span::create_session_globals_then(edition, || {
@@ -52,13 +52,36 @@ use tracing_subscriber::fmt::{
 };
 use tracing_subscriber::layer::SubscriberExt;
 
-pub fn init_env_logger(env: &str) -> Result<(), Error> {
-    let filter = match env::var(env) {
+/// The values of all the environment variables that matter for configuring a logger.
+/// Errors are explicitly preserved so that we can share error handling.
+pub struct LoggerConfig {
+    pub filter: Result<String, VarError>,
+    pub color_logs: Result<String, VarError>,
+    pub verbose_entry_exit: Result<String, VarError>,
+    pub verbose_thread_ids: Result<String, VarError>,
+    pub backtrace: Result<String, VarError>,
+}
+
+impl LoggerConfig {
+    pub fn from_env(env: &str) -> Self {
+        LoggerConfig {
+            filter: env::var(env),
+            color_logs: env::var(format!("{env}_COLOR")),
+            verbose_entry_exit: env::var(format!("{env}_ENTRY_EXIT")),
+            verbose_thread_ids: env::var(format!("{env}_THREAD_IDS")),
+            backtrace: env::var(format!("{env}_BACKTRACE")),
+        }
+    }
+}
+
+/// Initialize the logger with the given values for the filter, coloring, and other options env variables.
+pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
+    let filter = match cfg.filter {
         Ok(env) => EnvFilter::new(env),
         _ => EnvFilter::default().add_directive(Directive::from(LevelFilter::WARN)),
     };
 
-    let color_logs = match env::var(String::from(env) + "_COLOR") {
+    let color_logs = match cfg.color_logs {
         Ok(value) => match value.as_ref() {
             "always" => true,
             "never" => false,
@@ -69,14 +92,14 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> {
         Err(VarError::NotUnicode(_value)) => return Err(Error::NonUnicodeColorValue),
     };
 
-    let verbose_entry_exit = match env::var_os(String::from(env) + "_ENTRY_EXIT") {
-        None => false,
-        Some(v) => &v != "0",
+    let verbose_entry_exit = match cfg.verbose_entry_exit {
+        Ok(v) => &v != "0",
+        Err(_) => false,
     };
 
-    let verbose_thread_ids = match env::var_os(String::from(env) + "_THREAD_IDS") {
-        None => false,
-        Some(v) => &v == "1",
+    let verbose_thread_ids = match cfg.verbose_thread_ids {
+        Ok(v) => &v == "1",
+        Err(_) => false,
     };
 
     let layer = tracing_tree::HierarchicalLayer::default()
@@ -91,7 +114,7 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> {
         .with_thread_names(verbose_thread_ids);
 
     let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
-    match env::var(format!("{env}_BACKTRACE")) {
+    match cfg.backtrace {
         Ok(str) => {
             let fmt_layer = tracing_subscriber::fmt::layer()
                 .with_writer(io::stderr)
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index dda06d4c9c1d7..a43ea5582b786 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -53,6 +53,7 @@ extern crate rustc_interface;
 extern crate rustc_lexer;
 extern crate rustc_lint;
 extern crate rustc_lint_defs;
+extern crate rustc_log;
 extern crate rustc_macros;
 extern crate rustc_metadata;
 extern crate rustc_middle;
@@ -175,7 +176,7 @@ pub fn main() {
     // in the sysroot), and all of rustdoc's logging goes to its version (the one in Cargo.toml).
 
     init_logging(&handler);
-    rustc_driver::init_env_logger(&handler, "RUSTDOC_LOG");
+    rustc_driver::init_logger(&handler, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG"));
 
     let exit_code = rustc_driver::catch_with_exit_code(|| match get_args(&handler) {
         Some(args) => main_args(&mut handler, &args, using_internal_features),
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index 62a58576da555..3a23ff7fe6a72 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -1,6 +1,7 @@
 #![feature(rustc_private)]
 
 extern crate rustc_driver;
+extern crate rustc_log;
 extern crate rustc_session;
 
 use std::env;
@@ -173,7 +174,7 @@ fn parse_args() -> (OutputFormat, PathBuf) {
 fn main() {
     let handler =
         rustc_session::EarlyErrorHandler::new(rustc_session::config::ErrorOutputType::default());
-    rustc_driver::init_env_logger(&handler, "RUST_LOG");
+    rustc_driver::init_logger(&handler, rustc_log::LoggerConfig::from_env("RUST_LOG"));
     let (format, dst) = parse_args();
     let result = main_with_result(format, &dst);
     if let Err(e) = result {

From 86e5d36c19d2cd53f55e8a2e0b8589f756ff7d66 Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Sat, 11 Nov 2023 13:44:45 +0100
Subject: [PATCH 2/2] Miri: use new init_logger function, avoid calling
 env::set_var

---
 src/tools/miri/src/bin/miri.rs | 68 +++++++++++++++++++---------------
 1 file changed, 39 insertions(+), 29 deletions(-)

diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index cd628444fed93..7f777cd4727ad 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -9,11 +9,12 @@ extern crate rustc_data_structures;
 extern crate rustc_driver;
 extern crate rustc_hir;
 extern crate rustc_interface;
+extern crate rustc_log;
 extern crate rustc_metadata;
 extern crate rustc_middle;
 extern crate rustc_session;
 
-use std::env;
+use std::env::{self, VarError};
 use std::num::NonZeroU64;
 use std::path::PathBuf;
 use std::str::FromStr;
@@ -183,45 +184,54 @@ macro_rules! show_error {
     ($($tt:tt)*) => { show_error(&format_args!($($tt)*)) };
 }
 
-fn init_early_loggers(handler: &EarlyErrorHandler) {
-    // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
-    // initialize them both, and we always initialize `miri`'s first.
-    let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE");
-    env_logger::init_from_env(env);
-    // Enable verbose entry/exit logging by default if MIRI_LOG is set.
-    if env::var_os("MIRI_LOG").is_some() && env::var_os("RUSTC_LOG_ENTRY_EXIT").is_none() {
-        env::set_var("RUSTC_LOG_ENTRY_EXIT", "1");
-    }
-    // We only initialize `rustc` if the env var is set (so the user asked for it).
-    // If it is not set, we avoid initializing now so that we can initialize
-    // later with our custom settings, and *not* log anything for what happens before
-    // `miri` gets started.
-    if env::var_os("RUSTC_LOG").is_some() {
-        rustc_driver::init_rustc_env_logger(handler);
-    }
-}
+fn rustc_logger_config() -> rustc_log::LoggerConfig {
+    // Start with the usual env vars.
+    let mut cfg = rustc_log::LoggerConfig::from_env("RUSTC_LOG");
 
-fn init_late_loggers(handler: &EarlyErrorHandler, tcx: TyCtxt<'_>) {
-    // We initialize loggers right before we start evaluation. We overwrite the `RUSTC_LOG`
-    // env var if it is not set, control it based on `MIRI_LOG`.
-    // (FIXME: use `var_os`, but then we need to manually concatenate instead of `format!`.)
+    // Overwrite if MIRI_LOG is set.
     if let Ok(var) = env::var("MIRI_LOG") {
-        if env::var_os("RUSTC_LOG").is_none() {
+        // MIRI_LOG serves as default for RUSTC_LOG, if that is not set.
+        if matches!(cfg.filter, Err(VarError::NotPresent)) {
             // We try to be a bit clever here: if `MIRI_LOG` is just a single level
             // used for everything, we only apply it to the parts of rustc that are
             // CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`.
             // This way, if you set `MIRI_LOG=trace`, you get only the right parts of
             // rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_const_eval::interpret=debug`.
             if log::Level::from_str(&var).is_ok() {
-                env::set_var(
-                    "RUSTC_LOG",
-                    format!("rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var}"),
-                );
+                cfg.filter = Ok(format!(
+                    "rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var}"
+                ));
             } else {
-                env::set_var("RUSTC_LOG", &var);
+                cfg.filter = Ok(var);
             }
-            rustc_driver::init_rustc_env_logger(handler);
         }
+        // Enable verbose entry/exit logging by default if MIRI_LOG is set.
+        if matches!(cfg.verbose_entry_exit, Err(VarError::NotPresent)) {
+            cfg.verbose_entry_exit = Ok(format!("1"));
+        }
+    }
+
+    cfg
+}
+
+fn init_early_loggers(handler: &EarlyErrorHandler) {
+    // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
+    // initialize them both, and we always initialize `miri`'s first.
+    let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE");
+    env_logger::init_from_env(env);
+    // Now for rustc. We only initialize `rustc` if the env var is set (so the user asked for it).
+    // If it is not set, we avoid initializing now so that we can initialize later with our custom
+    // settings, and *not* log anything for what happens before `miri` gets started.
+    if env::var_os("RUSTC_LOG").is_some() {
+        rustc_driver::init_logger(handler, rustc_logger_config());
+    }
+}
+
+fn init_late_loggers(handler: &EarlyErrorHandler, tcx: TyCtxt<'_>) {
+    // If `RUSTC_LOG` is not set, then `init_early_loggers` did not call
+    // `rustc_driver::init_logger`, so we have to do this now.
+    if env::var_os("RUSTC_LOG").is_none() {
+        rustc_driver::init_logger(handler, rustc_logger_config());
     }
 
     // If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`.