diff --git a/Cargo.lock b/Cargo.lock index d311fba8d..e72ced7eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5014,7 +5014,6 @@ dependencies = [ "indoc", "insta", "itertools 0.14.0", - "jiff", "miette", "nix 0.30.1", "owo-colors", @@ -5063,6 +5062,7 @@ dependencies = [ "uv-git-types", "uv-install-wheel", "uv-installer", + "uv-logging", "uv-normalize", "uv-pep440", "uv-pep508", @@ -5184,8 +5184,11 @@ dependencies = [ name = "uv-build" version = "0.8.13" dependencies = [ + "anstream", "anyhow", + "tracing-subscriber", "uv-build-backend", + "uv-logging", "uv-version", ] @@ -5838,6 +5841,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "uv-logging" +version = "0.0.1" +dependencies = [ + "jiff", + "owo-colors", + "tracing", + "tracing-subscriber", +] + [[package]] name = "uv-macros" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index fae199a2f..36b81d30e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ uv-git-types = { path = "crates/uv-git-types" } uv-globfilter = { path = "crates/uv-globfilter" } uv-install-wheel = { path = "crates/uv-install-wheel", default-features = false } uv-installer = { path = "crates/uv-installer" } +uv-logging = { path = "crates/uv-logging" } uv-macros = { path = "crates/uv-macros" } uv-metadata = { path = "crates/uv-metadata" } uv-normalize = { path = "crates/uv-normalize" } @@ -180,7 +181,7 @@ toml = { version = "0.9.2", features = ["fast_hash"] } toml_edit = { version = "0.23.2", features = ["serde"] } tracing = { version = "0.1.40" } tracing-durations-export = { version = "0.3.0", features = ["plot"] } -tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json", "registry"] } +tracing-subscriber = { version = "0.3.18" } # Default feature set for uv_build, uv activates extra features tracing-test = { version = "0.2.5" } tracing-tree = { version = "0.4.0" } unicode-width = { version = "0.2.0" } diff --git a/crates/uv-build/Cargo.toml b/crates/uv-build/Cargo.toml index 413a8603e..b3e13139d 100644 --- a/crates/uv-build/Cargo.toml +++ b/crates/uv-build/Cargo.toml @@ -11,9 +11,12 @@ license = { workspace = true } [dependencies] uv-build-backend = { workspace = true } +uv-logging = { workspace = true } uv-version = { workspace = true } +anstream = { workspace = true } anyhow = { workspace = true } +tracing-subscriber = { workspace = true, features = ["env-filter"] } [lints] workspace = true diff --git a/crates/uv-build/src/main.rs b/crates/uv-build/src/main.rs index 4b9b5b3ee..5dd2ce160 100644 --- a/crates/uv-build/src/main.rs +++ b/crates/uv-build/src/main.rs @@ -1,10 +1,32 @@ -use anyhow::{Context, Result, bail}; use std::env; use std::io::Write; use std::path::PathBuf; +use anyhow::{Context, Result, bail}; +use tracing_subscriber::filter::LevelFilter; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; +use tracing_subscriber::{EnvFilter, Layer}; + +use uv_logging::UvFormat; + /// Entrypoint for the `uv-build` Python package. fn main() -> Result<()> { + // Support configuring the log level with `RUST_LOG` (shows only the error level by default) and + // color. + // + // This configuration is a simplified version of the uv logging configuration. When using + // uv_build through uv proper, the uv logging configuration applies. + let filter = EnvFilter::builder() + .with_default_directive(LevelFilter::OFF.into()) + .from_env() + .context("Invalid RUST_LOG directives")?; + let stderr_layer = tracing_subscriber::fmt::layer() + .event_format(UvFormat::default()) + .with_writer(std::sync::Mutex::new(anstream::stderr())) + .with_filter(filter); + tracing_subscriber::registry().with(stderr_layer).init(); + // Handrolled to avoid the large clap dependency let mut args = env::args_os(); // Skip the name of the binary diff --git a/crates/uv-dev/Cargo.toml b/crates/uv-dev/Cargo.toml index 23b571c6f..8eb62ce61 100644 --- a/crates/uv-dev/Cargo.toml +++ b/crates/uv-dev/Cargo.toml @@ -59,7 +59,7 @@ tokio = { workspace = true } tokio-util = { workspace = true } tracing = { workspace = true } tracing-durations-export = { workspace = true, features = ["plot"] } -tracing-subscriber = { workspace = true } +tracing-subscriber = { workspace = true, features = ["env-filter"] } uv-performance-memory-allocator = { path = "../uv-performance-memory-allocator", optional = true } walkdir = { workspace = true } diff --git a/crates/uv-logging/Cargo.toml b/crates/uv-logging/Cargo.toml new file mode 100644 index 000000000..d660bf454 --- /dev/null +++ b/crates/uv-logging/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "uv-logging" +version = "0.0.1" +edition = { workspace = true } +rust-version = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +repository = { workspace = true } +authors = { workspace = true } +license = { workspace = true } + +[lib] +doctest = false + +[lints] +workspace = true + +[dependencies] +jiff = { workspace = true } +owo-colors = { workspace = true } +tracing-subscriber = { workspace = true } +tracing = { workspace = true } diff --git a/crates/uv-logging/src/lib.rs b/crates/uv-logging/src/lib.rs new file mode 100644 index 000000000..7d5aa1575 --- /dev/null +++ b/crates/uv-logging/src/lib.rs @@ -0,0 +1,95 @@ +use std::fmt; + +use jiff::Timestamp; +use owo_colors::OwoColorize; +use tracing::{Event, Subscriber}; +use tracing_subscriber::fmt::format::Writer; +use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields}; +use tracing_subscriber::registry::LookupSpan; + +/// The style of a uv logging line. +pub struct UvFormat { + pub display_timestamp: bool, + pub display_level: bool, + pub show_spans: bool, +} + +impl Default for UvFormat { + /// Regardless of the tracing level, show messages without any adornment. + fn default() -> Self { + Self { + display_timestamp: false, + display_level: true, + show_spans: false, + } + } +} + +/// See +impl FormatEvent for UvFormat +where + S: Subscriber + for<'a> LookupSpan<'a>, + N: for<'a> FormatFields<'a> + 'static, +{ + fn format_event( + &self, + ctx: &FmtContext<'_, S, N>, + mut writer: Writer<'_>, + event: &Event<'_>, + ) -> fmt::Result { + let meta = event.metadata(); + let ansi = writer.has_ansi_escapes(); + + if self.display_timestamp { + if ansi { + write!(writer, "{} ", Timestamp::now().dimmed())?; + } else { + write!(writer, "{} ", Timestamp::now())?; + } + } + + if self.display_level { + let level = meta.level(); + // Same colors as tracing + if ansi { + match *level { + tracing::Level::TRACE => write!(writer, "{} ", level.purple())?, + tracing::Level::DEBUG => write!(writer, "{} ", level.blue())?, + tracing::Level::INFO => write!(writer, "{} ", level.green())?, + tracing::Level::WARN => write!(writer, "{} ", level.yellow())?, + tracing::Level::ERROR => write!(writer, "{} ", level.red())?, + } + } else { + write!(writer, "{level} ")?; + } + } + + if self.show_spans { + let span = event.parent(); + let mut seen = false; + + let span = span + .and_then(|id| ctx.span(id)) + .or_else(|| ctx.lookup_current()); + + let scope = span.into_iter().flat_map(|span| span.scope().from_root()); + + for span in scope { + seen = true; + if ansi { + write!(writer, "{}:", span.metadata().name().bold())?; + } else { + write!(writer, "{}:", span.metadata().name())?; + } + } + + if seen { + writer.write_char(' ')?; + } + } + + ctx.field_format().format_fields(writer.by_ref(), event)?; + + writeln!(writer) + } +} diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml index e71c587ed..e10a84e1f 100644 --- a/crates/uv/Cargo.toml +++ b/crates/uv/Cargo.toml @@ -35,6 +35,7 @@ uv-git = { workspace = true } uv-git-types = { workspace = true } uv-install-wheel = { workspace = true, default-features = false } uv-installer = { workspace = true } +uv-logging = { workspace = true } uv-normalize = { workspace = true } uv-pep440 = { workspace = true } uv-pep508 = { workspace = true } @@ -82,7 +83,6 @@ indicatif = { workspace = true } indoc = { workspace = true } itertools = { workspace = true } h2 = { workspace = true } -jiff = { workspace = true } miette = { workspace = true, features = ["fancy-no-backtrace"] } owo-colors = { workspace = true } petgraph = { workspace = true } @@ -102,7 +102,7 @@ toml = { workspace = true } toml_edit = { workspace = true } tracing = { workspace = true } tracing-durations-export = { workspace = true, features = ["plot"], optional = true } -tracing-subscriber = { workspace = true, features = ["json"] } +tracing-subscriber = { workspace = true, features = ["env-filter", "json", "registry"] } tracing-tree = { workspace = true } unicode-width = { workspace = true } url = { workspace = true } diff --git a/crates/uv/src/logging.rs b/crates/uv/src/logging.rs index d09cbc3f9..9654e3ab2 100644 --- a/crates/uv/src/logging.rs +++ b/crates/uv/src/logging.rs @@ -1,25 +1,19 @@ -use std::fmt; use std::str::FromStr; use anyhow::Context; -use jiff::Timestamp; -use owo_colors::OwoColorize; -use tracing::{Event, Subscriber}; #[cfg(feature = "tracing-durations-export")] use tracing_durations_export::{ DurationsLayer, DurationsLayerBuilder, DurationsLayerDropGuard, plot::PlotConfig, }; use tracing_subscriber::filter::Directive; -use tracing_subscriber::fmt::format::Writer; -use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields}; use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::registry::LookupSpan; use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::{EnvFilter, Layer, Registry}; use tracing_tree::HierarchicalLayer; use tracing_tree::time::Uptime; use uv_cli::ColorChoice; +use uv_logging::UvFormat; use uv_static::EnvVars; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] @@ -31,81 +25,6 @@ pub(crate) enum Level { TraceAll, } -struct UvFormat { - display_timestamp: bool, - display_level: bool, - show_spans: bool, -} - -/// See -impl FormatEvent for UvFormat -where - S: Subscriber + for<'a> LookupSpan<'a>, - N: for<'a> FormatFields<'a> + 'static, -{ - fn format_event( - &self, - ctx: &FmtContext<'_, S, N>, - mut writer: Writer<'_>, - event: &Event<'_>, - ) -> fmt::Result { - let meta = event.metadata(); - let ansi = writer.has_ansi_escapes(); - - if self.display_timestamp { - if ansi { - write!(writer, "{} ", Timestamp::now().dimmed())?; - } else { - write!(writer, "{} ", Timestamp::now())?; - } - } - - if self.display_level { - let level = meta.level(); - // Same colors as tracing - if ansi { - match *level { - tracing::Level::TRACE => write!(writer, "{} ", level.purple())?, - tracing::Level::DEBUG => write!(writer, "{} ", level.blue())?, - tracing::Level::INFO => write!(writer, "{} ", level.green())?, - tracing::Level::WARN => write!(writer, "{} ", level.yellow())?, - tracing::Level::ERROR => write!(writer, "{} ", level.red())?, - } - } else { - write!(writer, "{level} ")?; - } - } - - if self.show_spans { - let span = event.parent(); - let mut seen = false; - - let span = span - .and_then(|id| ctx.span(id)) - .or_else(|| ctx.lookup_current()); - - let scope = span.into_iter().flat_map(|span| span.scope().from_root()); - - for span in scope { - seen = true; - if ansi { - write!(writer, "{}:", span.metadata().name().bold())?; - } else { - write!(writer, "{}:", span.metadata().name())?; - } - } - - if seen { - writer.write_char(' ')?; - } - } - - ctx.field_format().format_fields(writer.by_ref(), event)?; - - writeln!(writer) - } -} - /// Configure `tracing` based on the given [`Level`], taking into account the `RUST_LOG` environment /// variable. /// @@ -183,18 +102,11 @@ pub(crate) fn setup_logging( ) .init(); } else { - // Regardless of the tracing level, show messages without any adornment. - let format = UvFormat { - display_timestamp: false, - display_level: true, - show_spans: false, - }; - tracing_subscriber::registry() .with(durations_layer) .with( tracing_subscriber::fmt::layer() - .event_format(format) + .event_format(UvFormat::default()) .with_writer(writer) .with_ansi(ansi) .with_filter(filter), diff --git a/docs/concepts/build-backend.md b/docs/concepts/build-backend.md index bd9a912fb..1e86d35ab 100644 --- a/docs/concepts/build-backend.md +++ b/docs/concepts/build-backend.md @@ -237,6 +237,13 @@ must either be under the module root or in the appropriate [data directory](../reference/settings.md#build-backend_data). Most packages store small data in the module root alongside the source code. +!!! tip + + When using the uv build backend through a frontend that is not uv, such as pip or + `pythom -m build`, debug logging can be enabled through environment variables with + `RUST_LOG=uv=debug` or `RUST_LOG=uv=verbose`. When used through uv, the uv build backend shares + the verbosity level of uv. + ### Include and exclude syntax Includes are anchored, which means that `pyproject.toml` includes only `/pyproject.toml` and