diff --git a/Cargo.lock b/Cargo.lock index f8196368..dfafe403 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,7 +173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.4.7", "serde", ] @@ -438,6 +438,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs" version = "5.0.1" @@ -681,8 +690,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -904,7 +913,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata", + "regex-automata 0.4.7", "same-file", "walkdir", "winapi-util", @@ -1012,6 +1021,7 @@ dependencies = [ "console", "ctrlc", "dialoguer", + "directories", "dirs", "dunce", "env_logger", @@ -1038,6 +1048,9 @@ dependencies = [ "tar", "tempfile", "thiserror", + "tracing", + "tracing-error", + "tracing-subscriber", "url", "windows", "winres", @@ -1078,6 +1091,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.4" @@ -1164,6 +1186,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1255,6 +1287,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "path-absolutize" version = "3.1.1" @@ -1486,8 +1524,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1498,9 +1545,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.4", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.4" @@ -1754,6 +1807,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell-words" version = "1.1.0" @@ -1908,6 +1970,16 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -2047,9 +2119,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -2057,6 +2141,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -2124,6 +2250,12 @@ dependencies = [ "getrandom", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index f6ce7650..c2d8fca8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,10 @@ is-terminal = "0.4" path-absolutize = "3.1.0" human-sort = "0.2.2" regex = "1.10" +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter", "serde", "serde_json"] } +tracing-error = "0.2.0" +directories = "5.0.1" [target.'cfg(windows)'.dependencies] windows = { version = "0.58.0", features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_Security", "Win32_System_JobObjects", "Win32_System_Console", "Win32_System_Threading", "Services_Store", "Foundation", "Foundation_Collections", "Web_Http", "Web_Http_Headers", "Storage_Streams", "Management_Deployment"] } diff --git a/src/utils.rs b/src/utils.rs index b5b8fad1..3fed30f8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,8 +1,77 @@ use anyhow::{anyhow, bail, Context, Result}; use semver::{BuildMetadata, Version}; use std::path::PathBuf; +use tracing_error::ErrorLayer; +use tracing_subscriber::{ + self, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, +}; use url::Url; +/// Returns the project directories. +/// Uses the `ProjectDirs` struct from the `directories` crate to define a directory +/// structure for the project with "org" as the organization, "julialang" as the application, +/// and "install" as the qualifier. This is a standard approach for determining data storage paths. +fn project_dirs() -> Option { + directories::ProjectDirs::from("org", "julialang", "install") +} + +/// Returns the default data directory for the application. +/// First, it checks the environment variable `JULIAUP_DATA_HOME`. +/// If the environment variable is not set, it falls back to the data directory +/// provided by `project_dirs`, which is typically platform-specific. +/// If both fail, it defaults to a `.data` directory in the current working directory. +pub fn default_data_dir() -> PathBuf { + std::env::var("JULIAUP_DATA_HOME") + .map(PathBuf::from) + .or_else(|_| { + project_dirs() + .map(|dirs| dirs.data_local_dir().to_path_buf()) + .ok_or(()) + }) + .unwrap_or(PathBuf::from(".").join(".data")) +} + +/// Initializes logging for the application. +/// Creates the default data directory if it doesn't already exist. +/// A log file named after the package is created in this directory. +/// The `tracing_subscriber` library is then used to configure logging to the file, +/// including file and line numbers in log entries, while disabling ANSI coloring. +/// The log level for specific libraries (`tokio_util`, `hyper`, `reqwest`) is turned off. +pub fn init_logging() -> Result<()> { + // Get the default data directory + let directory = default_data_dir(); + + // Create the directory (and any missing parent directories) if it doesn't exist + std::fs::create_dir_all(directory.clone())?; + + // Create a log file named after the package + let log_file = format!("{}.log", env!("CARGO_PKG_NAME")); + let log_path = directory.join(log_file); + let log_file = std::fs::File::create(log_path)?; + + // Set up a logging subscriber that writes to the log file, with specific configurations + let file_subscriber = tracing_subscriber::fmt::layer() + .with_file(true) // Include source file name in logs + .with_line_number(true) // Include line number in logs + .with_writer(log_file) // Log to the created file + .with_target(false) // Disable logging target (e.g., module paths) + .with_ansi(false); // Disable ANSI color codes + + // Initialize the tracing subscriber with filters for specific libraries + tracing_subscriber::registry() + .with(file_subscriber) + .with(ErrorLayer::default()) // Add error handling layer + .with( + tracing_subscriber::filter::EnvFilter::from_default_env() + .add_directive("tokio_util=off".parse().unwrap()) // Disable logging for `tokio_util` + .add_directive("hyper=off".parse().unwrap()) // Disable logging for `hyper` + .add_directive("reqwest=off".parse().unwrap()), // Disable logging for `reqwest` + ) + .init(); + + Ok(()) +} + pub fn get_juliaserver_base_url() -> Result { let base_url = if let Ok(val) = std::env::var("JULIAUP_SERVER") { if val.ends_with('/') {