diff --git a/Cargo.lock b/Cargo.lock index 85b03a20c24d..27d243e9e72e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,7 +193,6 @@ dependencies = [ "camino", "crossbeam", "dashmap 6.1.0", - "hdrhistogram", "insta", "libc", "mimalloc", @@ -1205,7 +1204,6 @@ dependencies = [ "schemars", "serde", "serde_json", - "tracing", ] [[package]] @@ -2264,16 +2262,6 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "byteorder", - "num-traits", -] - [[package]] name = "hermit-abi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 3f5207897d45..1fff1d408fc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -210,7 +210,7 @@ syn = "1.0.109" termcolor = "1.4.1" terminal_size = "0.4.1" tokio = "1.42.0" -tracing = { version = "0.1.41", default-features = false, features = ["std"] } +tracing = { version = "0.1.41", default-features = false, features = ["std", "attributes"] } tracing-subscriber = "0.3.19" unicode-bom = "2.0.3" unicode-width = "0.1.12" diff --git a/crates/biome_analyze/src/categories.rs b/crates/biome_analyze/src/categories.rs index 3d5d7be5a194..1b0835a82e9e 100644 --- a/crates/biome_analyze/src/categories.rs +++ b/crates/biome_analyze/src/categories.rs @@ -1,5 +1,6 @@ use enumflags2::{bitflags, BitFlags}; use std::borrow::Cow; +use std::fmt::{Display, Formatter}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr( @@ -23,6 +24,17 @@ pub enum RuleCategory { Transformation, } +impl Display for RuleCategory { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + RuleCategory::Syntax => write!(f, "Syntax"), + RuleCategory::Lint => write!(f, "Lint"), + RuleCategory::Action => write!(f, "Action"), + RuleCategory::Transformation => write!(f, "Transformation"), + } + } +} + /// Actions that suppress rules should start with this string pub const SUPPRESSION_INLINE_ACTION_CATEGORY: &str = "quickfix.suppressRule.inline"; pub const SUPPRESSION_TOP_LEVEL_ACTION_CATEGORY: &str = "quickfix.suppressRule.topLevel"; @@ -231,6 +243,26 @@ pub(crate) enum Categories { /// Use [RuleCategoriesBuilder] to generate the categories you want to query. pub struct RuleCategories(BitFlags); +impl Display for RuleCategories { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.0.is_empty() { + write!(f, "No categories") + } else { + let mut list = f.debug_list(); + if self.0.contains(Categories::Syntax) { + list.entry(&RuleCategory::Syntax); + } + if self.0.contains(Categories::Lint) { + list.entry(&RuleCategory::Lint); + } + if self.0.contains(Categories::Assist) { + list.entry(&RuleCategory::Action); + } + list.finish() + } + } +} + impl RuleCategories { pub fn empty() -> Self { let empty: BitFlags = BitFlags::empty(); diff --git a/crates/biome_analyze/src/lib.rs b/crates/biome_analyze/src/lib.rs index 7df64cc5a2b1..806b8381fb42 100644 --- a/crates/biome_analyze/src/lib.rs +++ b/crates/biome_analyze/src/lib.rs @@ -6,7 +6,6 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, BinaryHeap}; use std::fmt::{Debug, Display, Formatter}; use std::ops; -use tracing::trace; mod analyzer_plugin; mod categories; @@ -293,7 +292,6 @@ where /// Runs phase 0 over nodes and tokens to process line breaks and /// suppression comments fn run_first_phase(mut self) -> ControlFlow { - trace!("Running first analyzer phase"); let iter = self.root.syntax().preorder_with_tokens(Direction::Next); for event in iter { let node_event = match event { diff --git a/crates/biome_cli/Cargo.toml b/crates/biome_cli/Cargo.toml index d18a5770b7d5..93f9d813ce6a 100644 --- a/crates/biome_cli/Cargo.toml +++ b/crates/biome_cli/Cargo.toml @@ -43,7 +43,6 @@ bpaf = { workspace = true, features = ["bright-color"] } camino = { workspace = true } crossbeam = { workspace = true } dashmap = { workspace = true } -hdrhistogram = { version = "7.5.4", default-features = false } path-absolutize = { version = "3.1.1", optional = false, features = ["use_unix_paths_on_wasm"] } quick-junit = "0.5.1" rayon = { workspace = true } @@ -59,6 +58,7 @@ tracing-appender = "0.2.3" tracing-subscriber = { workspace = true, features = ["env-filter", "json"] } tracing-tree = "0.4.0" + [target.'cfg(unix)'.dependencies] libc = "0.2.169" tokio = { workspace = true, features = ["process"] } diff --git a/crates/biome_cli/src/commands/mod.rs b/crates/biome_cli/src/commands/mod.rs index 75377260dcaf..642da5050a8b 100644 --- a/crates/biome_cli/src/commands/mod.rs +++ b/crates/biome_cli/src/commands/mod.rs @@ -38,6 +38,7 @@ use biome_service::{Workspace, WorkspaceError}; use bpaf::Bpaf; use camino::Utf8PathBuf; use std::ffi::OsString; +use tracing::info; pub(crate) mod check; pub(crate) mod ci; @@ -588,10 +589,6 @@ impl BiomeCommand { } } - pub const fn has_metrics(&self) -> bool { - false - } - pub fn is_verbose(&self) -> bool { self.cli_options() .map_or(false, |cli_options| cli_options.verbose) @@ -779,6 +776,11 @@ pub(crate) trait CommandRunner: Sized { cli_options.verbose, )?; } + info!( + "Configuration file loaded: {:?}, diagnostics detected {}", + loaded_configuration.file_path, + loaded_configuration.diagnostics.len(), + ); let configuration_path = loaded_configuration.directory_path.clone(); let configuration = self.merge_configuration(loaded_configuration, fs, console)?; let vcs_base_path = configuration_path.clone().or(fs.working_directory()); diff --git a/crates/biome_cli/src/execute/mod.rs b/crates/biome_cli/src/execute/mod.rs index e7dbfc4fb970..8289e7e54b23 100644 --- a/crates/biome_cli/src/execute/mod.rs +++ b/crates/biome_cli/src/execute/mod.rs @@ -30,7 +30,7 @@ use biome_service::workspace::{ use camino::{Utf8Path, Utf8PathBuf}; use std::ffi::OsString; use std::fmt::{Display, Formatter}; -use tracing::info; +use tracing::{info, instrument}; /// Useful information during the traversal of files and virtual content #[derive(Debug, Clone)] @@ -45,42 +45,6 @@ pub struct Execution { max_diagnostics: u32, } -impl Execution { - pub fn new_format(project_key: ProjectKey, vcs_targeted: VcsTargeted) -> Self { - Self { - traversal_mode: TraversalMode::Format { - project_key, - ignore_errors: false, - write: false, - stdin: None, - vcs_targeted, - }, - report_mode: ReportMode::default(), - max_diagnostics: 0, - } - } - - pub fn report_mode(&self) -> &ReportMode { - &self.report_mode - } -} - -impl Execution { - pub(crate) fn to_feature(&self) -> FeatureName { - match self.traversal_mode { - TraversalMode::Format { .. } => FeaturesBuilder::new().with_formatter().build(), - TraversalMode::Lint { .. } => FeaturesBuilder::new().with_linter().build(), - TraversalMode::Check { .. } | TraversalMode::CI { .. } => FeaturesBuilder::new() - .with_formatter() - .with_linter() - .with_assist() - .build(), - TraversalMode::Migrate { .. } => FeatureName::empty(), - TraversalMode::Search { .. } => FeaturesBuilder::new().with_search().build(), - } - } -} - #[derive(Debug, Clone, Copy)] pub enum ExecutionEnvironment { GitHub, @@ -390,24 +354,29 @@ impl Execution { matches!(self.traversal_mode, TraversalMode::Lint { .. }) } - pub(crate) const fn is_check_apply(&self) -> bool { - matches!( - self.traversal_mode, - TraversalMode::Check { - fix_file_mode: Some(FixFileMode::SafeFixes), - .. + #[instrument(level = "debug", skip(self), fields(result))] + pub(crate) fn is_safe_fixes_enabled(&self) -> bool { + let result = match self.traversal_mode { + TraversalMode::Check { fix_file_mode, .. } => { + fix_file_mode == Some(FixFileMode::SafeFixes) } - ) + _ => false, + }; + tracing::Span::current().record("result", result); + result } - pub(crate) const fn is_check_apply_unsafe(&self) -> bool { - matches!( - self.traversal_mode, - TraversalMode::Check { - fix_file_mode: Some(FixFileMode::SafeAndUnsafeFixes), - .. + #[instrument(level = "debug", skip(self), fields(result))] + pub(crate) fn is_safe_and_unsafe_fixes_enabled(&self) -> bool { + let result = match self.traversal_mode { + TraversalMode::Check { fix_file_mode, .. } => { + fix_file_mode == Some(FixFileMode::SafeAndUnsafeFixes) } - ) + _ => false, + }; + + tracing::Span::current().record("result", result); + result } pub(crate) const fn is_format(&self) -> bool { @@ -463,6 +432,60 @@ impl Execution { TraversalMode::Search { .. } => false, } } + + pub fn new_format(project_key: ProjectKey, vcs_targeted: VcsTargeted) -> Self { + Self { + traversal_mode: TraversalMode::Format { + project_key, + ignore_errors: false, + write: false, + stdin: None, + vcs_targeted, + }, + report_mode: ReportMode::default(), + max_diagnostics: 0, + } + } + + pub fn report_mode(&self) -> &ReportMode { + &self.report_mode + } + pub(crate) fn to_feature(&self) -> FeatureName { + match self.traversal_mode { + TraversalMode::Format { .. } => FeaturesBuilder::new().with_formatter().build(), + TraversalMode::Lint { .. } => FeaturesBuilder::new().with_linter().build(), + TraversalMode::Check { .. } | TraversalMode::CI { .. } => FeaturesBuilder::new() + .with_formatter() + .with_linter() + .with_assist() + .build(), + TraversalMode::Migrate { .. } => FeatureName::empty(), + TraversalMode::Search { .. } => FeaturesBuilder::new().with_search().build(), + } + } + + #[instrument(level = "debug", skip(self), fields(result))] + pub(crate) fn should_write(&self) -> bool { + let result = match self.traversal_mode { + TraversalMode::Format { write, .. } => write, + + _ => self.is_safe_fixes_enabled() || self.is_safe_and_unsafe_fixes_enabled(), + }; + tracing::Span::current().record("result", result); + result + } + + #[instrument(level = "debug", skip(self), fields(result))] + pub(crate) fn should_ignore_errors(&self) -> bool { + let result = match self.traversal_mode { + TraversalMode::Format { ignore_errors, .. } => ignore_errors, + + _ => false, + }; + tracing::Span::current().record("result", result); + + result + } } /// Based on the [mode](TraversalMode), the function might launch a traversal of the file system @@ -643,12 +666,12 @@ pub fn execute_mode( } else if errors > 0 || should_exit_on_warnings { let category = execution.as_diagnostic_category(); if should_exit_on_warnings { - if execution.is_check_apply() { + if execution.is_safe_fixes_enabled() { Err(CliDiagnostic::apply_warnings(category)) } else { Err(CliDiagnostic::check_warnings(category)) } - } else if execution.is_check_apply() { + } else if execution.is_safe_fixes_enabled() { Err(CliDiagnostic::apply_error(category)) } else { Err(CliDiagnostic::check_error(category)) diff --git a/crates/biome_cli/src/execute/process_file/format.rs b/crates/biome_cli/src/execute/process_file/format.rs index 85821d33b63b..be5569ea6d2e 100644 --- a/crates/biome_cli/src/execute/process_file/format.rs +++ b/crates/biome_cli/src/execute/process_file/format.rs @@ -3,15 +3,15 @@ use crate::execute::process_file::workspace_file::WorkspaceFile; use crate::execute::process_file::{ DiffKind, FileResult, FileStatus, Message, SharedTraversalOptions, }; -use crate::execute::TraversalMode; use biome_analyze::RuleCategoriesBuilder; use biome_diagnostics::{category, Diagnostic, DiagnosticExt, Error, Severity}; use biome_fs::{BiomePath, TraversalContext}; use biome_service::diagnostics::FileTooLarge; use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler}; use std::sync::atomic::Ordering; -use tracing::debug; +use tracing::{debug, instrument}; +#[instrument(name = "cli_format", level = "debug", skip(ctx, path))] pub(crate) fn format<'ctx>( ctx: &'ctx SharedTraversalOptions<'ctx, '_>, path: BiomePath, @@ -30,11 +30,11 @@ pub(crate) fn format<'ctx>( } } +#[instrument(level = "debug", skip(ctx, workspace_file))] pub(crate) fn format_with_guard<'ctx>( ctx: &'ctx SharedTraversalOptions<'ctx, '_>, workspace_file: &mut WorkspaceFile, ) -> FileResult { - let _ = tracing::info_span!("Format", path =? workspace_file.path).entered(); let max_diagnostics = ctx.remaining_diagnostics.load(Ordering::Relaxed); let diagnostics_result = workspace_file .guard() @@ -47,20 +47,11 @@ pub(crate) fn format_with_guard<'ctx>( .with_file_path_and_code(workspace_file.path.to_string(), category!("format"))?; let input = workspace_file.input()?; - let (should_write, ignore_errors) = match ctx.execution.traversal_mode { - TraversalMode::Format { - write, - ignore_errors, - .. - } => (write, ignore_errors), + let should_write = ctx.execution.should_write(); + let ignore_errors = ctx.execution.should_ignore_errors(); - _ => ( - ctx.execution.is_check_apply() || ctx.execution.is_check_apply_unsafe(), - false, - ), - }; - debug!("Should write the file to disk? {}", should_write); - debug!("Should ignore errors? {}", ignore_errors); + tracing::Span::current().record("should_write", tracing::field::display(&should_write)); + tracing::Span::current().record("ignore_errors", tracing::field::display(&ignore_errors)); if diagnostics_result.errors > 0 && ignore_errors { return Err(Message::from( @@ -119,6 +110,10 @@ pub(crate) fn format_with_guard<'ctx>( _ => {} } + debug!( + "Format output is different from intput: {}", + output != input + ); if output != input { if should_write { workspace_file.update_file(output)?; diff --git a/crates/biome_cli/src/execute/process_file/lint.rs b/crates/biome_cli/src/execute/process_file/lint.rs index 3094f9c90e90..bf1f95cb2b65 100644 --- a/crates/biome_cli/src/execute/process_file/lint.rs +++ b/crates/biome_cli/src/execute/process_file/lint.rs @@ -9,8 +9,10 @@ use biome_rowan::TextSize; use biome_service::diagnostics::FileTooLarge; use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler}; use std::sync::atomic::Ordering; +use tracing::{info, instrument}; /// Lints a single file and returns a [FileResult] +#[instrument(level = "debug", name = "cli_lint", skip_all)] pub(crate) fn lint<'ctx>( ctx: &'ctx SharedTraversalOptions<'ctx, '_>, path: BiomePath, @@ -31,14 +33,14 @@ pub(crate) fn lint<'ctx>( } } +#[instrument(level = "debug", name = "cli_lint_guard", skip_all)] + pub(crate) fn lint_with_guard<'ctx>( ctx: &'ctx SharedTraversalOptions<'ctx, '_>, workspace_file: &mut WorkspaceFile, suppress: bool, suppression_reason: Option<&str>, ) -> FileResult { - let _ = tracing::info_span!("Lint ", path =? workspace_file.path).entered(); - let mut input = workspace_file.input()?; let mut changed = false; let (only, skip) = @@ -69,6 +71,13 @@ pub(crate) fn lint_with_guard<'ctx>( ) .with_file_path_and_code(workspace_file.path.to_string(), category!("lint"))?; + info!( + "Fix file summary result. Errors {}, skipped fixes {}, actions {}", + fix_result.errors, + fix_result.skipped_suggested_fixes, + fix_result.actions.len() + ); + ctx.push_message(Message::SkippedFixes { skipped_suggested_fixes: fix_result.skipped_suggested_fixes, }); diff --git a/crates/biome_cli/src/execute/std_in.rs b/crates/biome_cli/src/execute/std_in.rs index 2d6d18e3741f..a7bba8656a4c 100644 --- a/crates/biome_cli/src/execute/std_in.rs +++ b/crates/biome_cli/src/execute/std_in.rs @@ -175,7 +175,9 @@ pub(crate) fn run<'a>( Some("svelte") => SvelteFileHandler::output(&new_content, code.as_str()), _ => code, }; - if (mode.is_check_apply() || mode.is_check_apply_unsafe()) && output != new_content { + if (mode.is_safe_fixes_enabled() || mode.is_safe_and_unsafe_fixes_enabled()) + && output != new_content + { new_content = Cow::Owned(output); } } diff --git a/crates/biome_cli/src/execute/traverse.rs b/crates/biome_cli/src/execute/traverse.rs index f6212df7a442..ab5b8d46ba39 100644 --- a/crates/biome_cli/src/execute/traverse.rs +++ b/crates/biome_cli/src/execute/traverse.rs @@ -30,6 +30,7 @@ use std::{ thread, time::{Duration, Instant}, }; +use tracing::{instrument, Span}; pub(crate) struct TraverseResult { pub(crate) summary: TraversalSummary, @@ -576,6 +577,7 @@ impl<'ctx, 'app> TraversalContext for TraversalOptions<'ctx, 'app> { self.push_message(error); } + #[instrument(level = "debug", skip(self, biome_path))] fn can_handle(&self, biome_path: &BiomePath) -> bool { let path = biome_path.as_path(); if self.fs.path_is_dir(path) || self.fs.path_is_symlink(path) { @@ -596,11 +598,14 @@ impl<'ctx, 'app> TraversalContext for TraversalOptions<'ctx, 'app> { self.push_diagnostic(err.into()); false }); + Span::current().record("can_handle", can_handle); + return can_handle; } // bail on fifo and socket files if !self.fs.path_is_file(path) { + Span::current().record("can_handle", false); return false; } @@ -614,23 +619,25 @@ impl<'ctx, 'app> TraversalContext for TraversalOptions<'ctx, 'app> { Ok(file_features) => { if file_features.is_protected() { self.protected_file(biome_path); + Span::current().record("can_handle", false); return false; } if file_features.is_not_supported() && !file_features.is_ignored() { // we should throw a diagnostic if we can't handle a file that isn't ignored self.miss_handler_err(extension_error(biome_path), biome_path); + Span::current().record("can_handle", false); return false; } file_features } Err(err) => { self.miss_handler_err(err, biome_path); - + Span::current().record("can_handle", false); return false; } }; - match self.execution.traversal_mode() { + let result = match self.execution.traversal_mode() { TraversalMode::Check { .. } | TraversalMode::CI { .. } => { file_features.supports_lint() || file_features.supports_format() @@ -641,7 +648,9 @@ impl<'ctx, 'app> TraversalContext for TraversalOptions<'ctx, 'app> { // Imagine if Biome can't handle its own configuration file... TraversalMode::Migrate { .. } => true, TraversalMode::Search { .. } => file_features.supports_search(), - } + }; + Span::current().record("can_handle", result); + result } fn handle_path(&self, path: BiomePath) { diff --git a/crates/biome_cli/src/lib.rs b/crates/biome_cli/src/lib.rs index 34ce7ff71bb2..f521d92b7a9f 100644 --- a/crates/biome_cli/src/lib.rs +++ b/crates/biome_cli/src/lib.rs @@ -17,7 +17,6 @@ mod commands; mod diagnostics; mod execute; mod logging; -mod metrics; mod panic; mod reporter; mod service; @@ -60,12 +59,7 @@ impl<'app> CliSession<'app> { /// Main function to run Biome CLI pub fn run(self, command: BiomeCommand) -> Result<(), CliDiagnostic> { - let has_metrics = command.has_metrics(); - if has_metrics { - crate::metrics::init_metrics(); - } - - let result = match command { + match command { BiomeCommand::Version(_) => commands::version::full_version(self), BiomeCommand::Rage(_, daemon_logs, formatter, linter) => { commands::rage::rage(self, daemon_logs, formatter, linter) @@ -269,13 +263,7 @@ impl<'app> CliSession<'app> { Some(log_prefix_name), ), BiomeCommand::PrintSocket => commands::daemon::print_socket(), - }; - - if has_metrics { - metrics::print_metrics(); } - - result } } diff --git a/crates/biome_cli/src/metrics.rs b/crates/biome_cli/src/metrics.rs deleted file mode 100644 index 03c88cb41f9c..000000000000 --- a/crates/biome_cli/src/metrics.rs +++ /dev/null @@ -1,410 +0,0 @@ -use std::{ - borrow::Cow, - hash::Hash, - ops::Sub, - ptr, - time::{Duration, Instant}, -}; - -use hdrhistogram::Histogram; -use rustc_hash::FxHashMap; -use std::sync::{LazyLock, Mutex, RwLock}; -use tracing::{span, subscriber::Interest, Level, Metadata, Subscriber}; -use tracing_subscriber::{ - layer::Context, - prelude::*, - registry::{LookupSpan, SpanRef}, - Layer, -}; - -/// Implementation of a tracing [Layer] that collects timing information for spans into [Histogram]s -struct MetricsLayer; - -static METRICS: LazyLock>>> = - LazyLock::new(RwLock::default); - -/// Static pointer to the metadata of a callsite, used as a unique identifier -/// for collecting spans created from there in the global metrics map -struct CallsiteKey(&'static Metadata<'static>); - -impl PartialEq for CallsiteKey { - fn eq(&self, other: &Self) -> bool { - ptr::eq(self.0, other.0) - } -} - -impl Eq for CallsiteKey {} - -impl Hash for CallsiteKey { - fn hash(&self, state: &mut H) { - ptr::hash(self.0, state); - } -} - -/// Single entry in the global callsite storage, containing handles to the -/// histograms associated with this callsite -enum CallsiteEntry { - /// Spans with the debug level only count their total duration - Debug { total: Histogram }, - /// Spans with the trace level count their total duration as well as - /// individual busy and idle times - Trace { - total: Histogram, - busy: Histogram, - idle: Histogram, - }, -} - -impl CallsiteEntry { - fn from_level(level: &Level) -> Self { - /// Number of significant figures retained by the histogram - const SIGNIFICANT_FIGURES: u8 = 3; - - match level { - &Level::TRACE => Self::Trace { - // SAFETY: Histogram::new only returns an error if the value of - // SIGNIFICANT_FIGURES is invalid, 3 is statically known to work - total: Histogram::new(SIGNIFICANT_FIGURES).unwrap(), - busy: Histogram::new(SIGNIFICANT_FIGURES).unwrap(), - idle: Histogram::new(SIGNIFICANT_FIGURES).unwrap(), - }, - _ => Self::Debug { - total: Histogram::new(SIGNIFICANT_FIGURES).unwrap(), - }, - } - } - - fn into_histograms(self, name: &str) -> Vec<(Cow, Histogram)> { - match self { - CallsiteEntry::Debug { total } => vec![(Cow::Borrowed(name), total)], - CallsiteEntry::Trace { total, busy, idle } => vec![ - (Cow::Borrowed(name), total), - (Cow::Owned(format!("{name}.busy")), busy), - (Cow::Owned(format!("{name}.idle")), idle), - ], - } - } -} - -/// Extension data attached to tracing spans to keep track of their idle and busy time -/// -/// Most of the associated code is based on the similar logic found in `tracing-subscriber` -/// for printing span timings to the console: -/// https://github.com/tokio-rs/tracing/blob/6f23c128fced6409008838a3223d76d7332d79e9/tracing-subscriber/src/fmt/fmt_subscriber.rs#L973 -struct Timings { - idle: u64, - busy: u64, - last: I, -} - -trait Timepoint: Sub + Copy + Sized { - fn now() -> Self; -} - -impl Timepoint for Instant { - fn now() -> Self { - Instant::now() - } -} - -impl Timings { - fn new() -> Self { - Self { - idle: 0, - busy: 0, - last: I::now(), - } - } - - /// Count the time between the last update and now as idle - fn enter(&mut self, now: I) { - self.idle += (now - self.last).as_nanos() as u64; - self.last = now; - } - - /// Count the time between the last update and now as busy - fn exit(&mut self, now: I) { - self.busy += (now - self.last).as_nanos() as u64; - self.last = now; - } - - /// Exit the timing for this span, and record it into a callsite entry - fn record(mut self, now: I, entry: &mut CallsiteEntry) { - self.exit(now); - - match entry { - CallsiteEntry::Debug { total } => { - total.record(self.busy + self.idle).unwrap(); - } - CallsiteEntry::Trace { total, busy, idle } => { - busy.record(self.busy).unwrap(); - idle.record(self.idle).unwrap(); - total.record(self.busy + self.idle).unwrap(); - } - } - } -} - -fn read_span<'ctx, S>(ctx: &'ctx Context<'_, S>, id: &span::Id) -> SpanRef<'ctx, S> -where - S: Subscriber + for<'a> LookupSpan<'a>, -{ - ctx.span(id) - .expect("Span not found, it should have been stored in the registry") -} - -impl Layer for MetricsLayer -where - S: Subscriber + for<'a> LookupSpan<'a>, -{ - /// Only express interest in span callsites, disabling collection of events, - /// and create new histogram for the spans created by this callsite - fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { - if !metadata.is_span() { - return Interest::never(); - } - - let entry = CallsiteEntry::from_level(metadata.level()); - - METRICS - .write() - .unwrap() - .insert(CallsiteKey(metadata), Mutex::new(entry)); - - Interest::always() - } - - /// When a new span is created, attach the timing data extension to it - fn on_new_span(&self, _attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { - let span = read_span(&ctx, id); - let mut extensions = span.extensions_mut(); - - if extensions.get_mut::().is_none() { - extensions.insert(Timings::::new()); - } - } - - /// When a span is entered, start counting idle time for the parent span if - /// it exists and busy time for the entered span itself - fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { - let span = read_span(&ctx, id); - - let now = Instant::now(); - if let Some(parent) = span.parent() { - let mut extensions = parent.extensions_mut(); - if let Some(timings) = extensions.get_mut::() { - // The parent span was busy until now - timings.exit(now); - } - } - - let mut extensions = span.extensions_mut(); - if let Some(timings) = extensions.get_mut::() { - // The child span was idle until now - timings.enter(now); - } - } - - /// When a span is exited, stop it from counting busy time and start - /// counting the parent as busy instead - fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { - let span = read_span(&ctx, id); - - let now = Instant::now(); - let mut extensions = span.extensions_mut(); - if let Some(timings) = extensions.get_mut::() { - // Child span was busy until now - timings.exit(now); - } - - // Re-enter parent - if let Some(parent) = span.parent() { - let mut extensions = parent.extensions_mut(); - if let Some(timings) = extensions.get_mut::() { - // Parent span was idle until now - timings.enter(now); - } - } - } - - /// When a span is closed, extract its timing information and write it to - /// the associated histograms - fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { - let span = read_span(&ctx, &id); - let mut extensions = span.extensions_mut(); - if let Some(timing) = extensions.remove::() { - let now = Instant::now(); - - // Acquire a read lock on the metrics storage, access the metrics entry - // associated with this call site and acquire a write lock on it - let metrics = METRICS.read().unwrap(); - let entry = metrics - .get(&CallsiteKey(span.metadata())) - .expect("callsite not found, it should have been registered in register_callsite"); - - let mut entry = entry.lock().unwrap(); - timing.record(now, &mut entry); - } - } -} - -/// Initializes metrics recording -pub fn init_metrics() { - // Create and injects the metrics recording layer with the tracing library - tracing_subscriber::registry().with(MetricsLayer).init(); -} - -/// Flush and print the recorded metrics to the console -pub fn print_metrics() { - let mut write_guard = METRICS.write().unwrap(); - let mut histograms: Vec<_> = write_guard - .drain() - .flat_map(|(key, entry)| entry.into_inner().unwrap().into_histograms(key.0.name())) - .collect(); - - histograms.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); - - for (key, histogram) in histograms { - // Print the header line for the histogram with its name, mean sample - // duration and standard deviation - println!( - "{}: mean = {:.1?}, stdev = {:.1?}", - key, - Duration::from_nanos(histogram.mean().round() as u64), - Duration::from_nanos(histogram.stdev().round() as u64), - ); - - // For each quantile bucket in the histogram print out the associated - // duration, a bar corresponding to the percentage of the total number - // of samples falling within this bucket and the percentile - // corresponding to this bucket - let total = histogram.len() as f64; - for v in histogram.iter_quantiles(1) { - let duration = Duration::from_nanos(v.value_iterated_to()); - - let count = v.count_since_last_iteration() as f64; - let bar_length = (count * 40.0 / total).ceil() as usize; - - println!( - "{: >7.1?} | {:40} | {:5.1}%", - duration, - "*".repeat(bar_length), - v.quantile_iterated_to() * 100.0, - ); - } - - // Print an empty line after each histogram - println!(); - } -} - -#[cfg(test)] -mod tests { - use std::{ops::Sub, thread, time::Duration}; - - use tracing::Level; - use tracing_subscriber::prelude::*; - - use super::{CallsiteEntry, CallsiteKey, MetricsLayer, Timepoint, Timings, METRICS}; - - #[derive(Clone, Copy)] - struct TestTime(u64); - - impl Sub for TestTime { - type Output = Duration; - - fn sub(self, rhs: Self) -> Self::Output { - Duration::from_nanos(self.0 - rhs.0) - } - } - - impl Timepoint for TestTime { - fn now() -> Self { - Self(0) - } - } - - #[test] - fn test_timing() { - let mut entry = CallsiteEntry::from_level(&Level::TRACE); - - for i in 1..=5 { - let mut timing = Timings::::new(); - - timing.enter(TestTime(i)); - - timing.record(TestTime(i * 2), &mut entry); - } - - let histograms = entry.into_histograms("test"); - for (name, histogram) in histograms { - let scale = match name.as_ref() { - "test" => 2.0, - "test.idle" | "test.busy" => 1.0, - _ => unreachable!(), - }; - - let sample_count = 5; - assert_eq!(histogram.len(), sample_count); - - let mean = 3.0 * scale; - assert_eq!(histogram.mean(), mean); - - let sum = (1..=5).fold(0.0, |sum, i| { - let sample = i as f64 * scale; - sum + (sample - mean).powi(2) - }); - - let stddev = (sum / sample_count as f64).sqrt(); - assert_eq!(histogram.stdev(), stddev); - - let s = scale as u64 - 1; - let expected_buckets = [ - (0, s, 0.0), - (1, 2 * s + 1, 0.2), - (1, 3 * s + 2, 0.4), - (1, 4 * s + 3, 0.6), - (1, 5 * s + 4, 0.8), - (1, 6 * s + 5, 1.0), - ]; - - for (bucket, expected) in histogram.iter_linear(scale as u64).zip(&expected_buckets) { - let (count, value, quantile) = *expected; - - assert_eq!(bucket.count_since_last_iteration(), count); - assert_eq!(bucket.value_iterated_to(), value); - assert_eq!(bucket.quantile_iterated_to(), quantile); - } - } - } - - #[test] - fn test_layer() { - let _guard = tracing_subscriber::registry() - .with(MetricsLayer) - .set_default(); - - let key = { - let span = tracing::trace_span!("test_layer"); - span.in_scope(|| { - thread::sleep(Duration::from_millis(1)); - }); - - span.metadata().expect("span is disabled") - }; - - let entry = { - let mut metrics = METRICS.write().unwrap(); - metrics.remove(&CallsiteKey(key)) - }; - - let entry = entry.expect("callsite does not exist in metrics storage"); - - let entry = entry.into_inner().unwrap(); - let histograms = entry.into_histograms(key.name()); - - for (_, histogram) in histograms { - assert_eq!(histogram.len(), 1); - } - } -} diff --git a/crates/biome_configuration/src/analyzer/mod.rs b/crates/biome_configuration/src/analyzer/mod.rs index c72f0544331d..59a1b8aa0039 100644 --- a/crates/biome_configuration/src/analyzer/mod.rs +++ b/crates/biome_configuration/src/analyzer/mod.rs @@ -14,7 +14,7 @@ use rustc_hash::FxHashSet; #[cfg(feature = "schema")] use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::fmt::Debug; +use std::fmt::{Debug, Display, Formatter}; use std::str::FromStr; #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] @@ -392,12 +392,27 @@ impl Merge for RuleWithFixOptions { } } -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Copy, Eq, PartialEq, Hash)] pub enum RuleSelector { Group(&'static str), Rule(&'static str, &'static str), } +impl Debug for RuleSelector { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +impl Display for RuleSelector { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + RuleSelector::Group(group) => write!(f, "{}", group), + RuleSelector::Rule(group, rule) => write!(f, "{}/{}", group, rule), + } + } +} + impl RuleSelector { /// It retrieves a [RuleSelector] from an LSP filter. /// diff --git a/crates/biome_configuration/src/lib.rs b/crates/biome_configuration/src/lib.rs index 83374be7bcb6..839c4ca7f8a9 100644 --- a/crates/biome_configuration/src/lib.rs +++ b/crates/biome_configuration/src/lib.rs @@ -36,6 +36,7 @@ pub use analyzer::{ linter_configuration, LinterConfiguration, RuleConfiguration, RuleFixConfiguration, RulePlainConfiguration, RuleWithFixOptions, RuleWithOptions, Rules, }; +use biome_console::fmt::{Display, Formatter}; use biome_deserialize::Deserialized; use biome_deserialize_macros::{Deserializable, Merge}; use biome_formatter::{IndentStyle, QuoteStyle}; @@ -387,6 +388,25 @@ pub enum ConfigurationPathHint { FromUser(Utf8PathBuf), } +impl Display for ConfigurationPathHint { + fn fmt(&self, fmt: &mut Formatter) -> std::io::Result<()> { + match self { + ConfigurationPathHint::None => write!(fmt, "Configuration file not provided.",), + ConfigurationPathHint::FromWorkspace(path) => write!( + fmt, + "Configuration path provided from a workspace: {}", + path + ), + ConfigurationPathHint::FromLsp(path) => { + write!(fmt, "Configuration path provided from the LSP: {}", path,) + } + ConfigurationPathHint::FromUser(path) => { + write!(fmt, "Configuration path provided by the user: {}", path,) + } + } + } +} + impl ConfigurationPathHint { pub const fn is_from_user(&self) -> bool { matches!(self, Self::FromUser(_)) diff --git a/crates/biome_diagnostics/src/display.rs b/crates/biome_diagnostics/src/display.rs index 37490cea8aea..9e1103bee0ae 100644 --- a/crates/biome_diagnostics/src/display.rs +++ b/crates/biome_diagnostics/src/display.rs @@ -525,7 +525,7 @@ impl Visit for PrintAdvices<'_, '_> { self.0.write_markup(markup!({ header_cell }))?; if index < headers.len() - 1 { self.0.write_markup( - markup! {{Padding::new(padding + longest_cell - header_cell.text_len())}}, + markup! {{Padding::new(padding + longest_cell.saturating_sub(header_cell.text_len()))}}, )?; } } diff --git a/crates/biome_fs/src/fs/os.rs b/crates/biome_fs/src/fs/os.rs index 12fda7f984ee..c78d96916e0a 100644 --- a/crates/biome_fs/src/fs/os.rs +++ b/crates/biome_fs/src/fs/os.rs @@ -154,7 +154,7 @@ struct OsFile { } impl File for OsFile { - #[instrument(level = "trace")] + #[instrument(level = "debug")] fn read_to_string(&mut self, buffer: &mut String) -> io::Result<()> { // Reset the cursor to the starting position self.inner.rewind()?; @@ -163,7 +163,7 @@ impl File for OsFile { Ok(()) } - #[instrument(level = "trace")] + #[instrument(level = "debug")] fn set_content(&mut self, content: &[u8]) -> io::Result<()> { // Truncate the file self.inner.set_len(0)?; diff --git a/crates/biome_fs/src/path.rs b/crates/biome_fs/src/path.rs index 33f3890c0e28..9f9eca584be0 100644 --- a/crates/biome_fs/src/path.rs +++ b/crates/biome_fs/src/path.rs @@ -8,6 +8,7 @@ use camino::{Utf8Path, Utf8PathBuf}; use enumflags2::{bitflags, BitFlags}; use smallvec::SmallVec; use std::cmp::Ordering; +use std::fmt::Formatter; use std::fs::read_to_string; use std::hash::Hash; use std::ops::DerefMut; @@ -111,6 +112,12 @@ impl serde::Serialize for BiomePath { } } +impl std::fmt::Display for BiomePath { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(self.path.as_str()) + } +} + #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for BiomePath { fn deserialize(deserializer: D) -> Result diff --git a/crates/biome_glob/src/lib.rs b/crates/biome_glob/src/lib.rs index 2167b20c7e8b..2791a47d09d7 100644 --- a/crates/biome_glob/src/lib.rs +++ b/crates/biome_glob/src/lib.rs @@ -1,7 +1,7 @@ //! `biome_glob` provides globbing functionality. When listing the globs to match, -//! it also possible to provide globs that function as "exceptions" by prefixing the globs with `!`. +//! it is also possible to provide globs that function as "exceptions" by prefixing the globs with `!`. //! -//! A glob is primarlly used to select or filter a set of file paths by matching every file paths against the glob. +//! A glob is primarily used to select or filter a set of file paths by matching every file paths against the glob. //! A file path either matches or doesn't match a glob. //! For example, the path `lib.rs` matches the glob `*.rs`. //! @@ -10,7 +10,7 @@ //! You have to understand the structure of a path to understand which path match a glob. //! A path is divided in path segments. //! Every path segment is delimited by the path separator `/` or the start/end of the path. -//! For instance `src/lib.rs` cosnists of two path segments: `src` and `lib.rs`. +//! For instance `src/lib.rs` consists of two path segments: `src` and `lib.rs`. //! A Biome glob supports the following patterns: //! //! - star `*` that matches zero or more characters inside a path segment @@ -25,7 +25,7 @@ //! For example, `**/**` is not a valid glob. //! //! `lib.rs` and `src/lib.rs` match `**` and `**/*.rs` -//! Conversely, `README.txt` doesn't match `**/*.rs` because the pat hends with `.txt`. +//! Conversely, `README.txt` doesn't match `**/*.rs` because the pat ends with `.txt`. //! //! - Use `\*` to escape `*` //! @@ -44,7 +44,7 @@ //! You can create a glob from a string using the `parse` method. //! Use [Glob::is_match] to match against anything that can be turned into a [std::path::Path], such as a string. //! -//! In the following example we parse the string `"*.rs"` into a glob and we match against two strings. +//! In the following example we parse the string `"*.rs"` into a glob, and we match against two strings. //! `lib.rs` matches the glob because the path has a single path segment that ends with `.rs`. //! Conversely, `src/lib.rs` doesn't match because it has two path segments (`src` and `lib.rs`). //! @@ -61,7 +61,7 @@ //! When a path is expected to be matched against several globs, //! you should compile the path into a [CandidatePath] using [CandidatePath::new]. //! [CandidatePath] may speed up matching against several globs. -//! To get adavantage of the speed-up, you have to use the [CandidatePath::matches] method instead of [Glob::is_match]. +//! To get advantage of the speed-up, you have to use the [CandidatePath::matches] method instead of [Glob::is_match]. //! //! In the following example, we create a list of two globs and we match them against a path compiled into a candidate path. //! The path matches the second glob of the list. @@ -104,9 +104,9 @@ //! //! Taking the previous example, the directory path `a/path` doesn't match `**/*.rs` the list of glob, //! because the path doesn't end with the `.rs` extension. -//! This behavior is porblematic when you write a file crawler that traverse the file hierarchy and +//! This behavior is problematic when you write a file crawler that traverse the file hierarchy and //! ignore directories with files that never match the list of globs. -//! Biome provides a deidcated method [CandidatePath::matches_directory_with_exceptions] for this purpose. +//! Biome provides a dedicated method [CandidatePath::matches_directory_with_exceptions] for this purpose. //! The method only check if the directory is not excluded by an exception. //! //! In the following example, `dir1` matches the list of globs, while `dir2` doesn't. @@ -127,8 +127,6 @@ //! ``` //! -use biome_deserialize::{DeserializableValue, DeserializationContext}; - /// A Biome glob pattern. #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -230,8 +228,8 @@ impl TryFrom for Glob { #[cfg(feature = "biome_deserialize")] impl biome_deserialize::Deserializable for Glob { fn deserialize( - ctx: &mut impl DeserializationContext, - value: &impl DeserializableValue, + ctx: &mut impl biome_deserialize::DeserializationContext, + value: &impl biome_deserialize::DeserializableValue, name: &str, ) -> Option { let glob = String::deserialize(ctx, value, name)?; @@ -374,7 +372,7 @@ pub enum GlobError { kind: GlobErrorKind, index: u32, }, - /// Error caused by a a third-party module. + /// Error caused by a third-party module. Generic(Box), } impl GlobError { @@ -404,8 +402,8 @@ pub enum GlobErrorKind { InvalidEscape, /// Occurs when `**` isn't enclosed by the path separator `/` or the start/end of the glob. InvalidGlobStar, - /// `{}` is not supported. - UnsupportedAlternates, + /// Nested `{}` are not supported. + UnsupportedNestedAlternates, /// `[]` is not supported. UnsupportedCharacterClass, /// `?` is not supported. @@ -419,8 +417,8 @@ impl std::fmt::Display for GlobErrorKind { Self::InvalidGlobStar => { r"`**` must be enclosed by the path separator `/`, or the start/end of the glob and mustn't be followed by `/**`." } - Self::UnsupportedAlternates => { - r"Alternates `{}` are not supported. Use `\{` and `\}` to escape the characters." + Self::UnsupportedNestedAlternates => { + r"Nested alternates `{}` are not supported. Use a separate glob for each nested alternate." } Self::UnsupportedCharacterClass => { r"Character class `[]` are not supported. Use `\[` and `\]` to escape the characters." @@ -437,6 +435,7 @@ impl std::fmt::Display for GlobErrorKind { fn validate_glob(pattern: &str) -> Result<(), GlobError> { let mut it = pattern.bytes().enumerate(); let mut allow_globstar = true; + let mut alternates_found = false; while let Some((i, c)) = it.next() { match c { b'*' => { @@ -482,11 +481,18 @@ fn validate_glob(pattern: &str) -> Result<(), GlobError> { index: i as u32, }); } - b'{' | b'}' => { - return Err(GlobError::Regular { - kind: GlobErrorKind::UnsupportedAlternates, - index: i as u32, - }); + b'{' => { + if alternates_found { + return Err(GlobError::Regular { + kind: GlobErrorKind::UnsupportedNestedAlternates, + index: i as u32, + }); + } else { + alternates_found = true; + } + } + b'}' => { + alternates_found = false; } _ => {} } @@ -567,6 +573,14 @@ mod tests { }) ); + assert_eq!( + validate_glob(r"file.{{spec,test}}"), + Err(GlobError::Regular { + kind: GlobErrorKind::UnsupportedNestedAlternates, + index: 6 + }) + ); + assert!(validate_glob("!*.js").is_ok()); assert!(validate_glob("!").is_ok()); assert!(validate_glob("*.js").is_ok()); @@ -577,14 +591,19 @@ mod tests { assert!(validate_glob(r"/**/").is_ok()); assert!(validate_glob(r"**/").is_ok()); assert!(validate_glob(r"/**").is_ok()); + assert!(validate_glob(r"*.{js,jsx}").is_ok()); } #[test] fn test_is_match() { assert!("*.rs".parse::().unwrap().is_match("lib.rs")); assert!(!"*.rs".parse::().unwrap().is_match("src/lib.rs")); - assert!("**/*.rs".parse::().unwrap().is_match("src/lib.rs")); + assert!("file.{js,jsx}".parse::().unwrap().is_match("file.js")); + assert!("file.{js,jsx}" + .parse::() + .unwrap() + .is_match("file.jsx")); } #[test] @@ -610,5 +629,9 @@ mod tests { fn test_to_string() { assert_eq!(Glob::from_str("**/*.rs").unwrap().to_string(), "**/*.rs"); assert_eq!(Glob::from_str("!**/*.rs").unwrap().to_string(), "!**/*.rs"); + assert_eq!( + Glob::from_str("file.{js,jsx}").unwrap().to_string(), + "file.{js,jsx}" + ); } } diff --git a/crates/biome_lsp/src/extension_settings.rs b/crates/biome_lsp/src/extension_settings.rs index f9754abe737b..7eaf9f0480ad 100644 --- a/crates/biome_lsp/src/extension_settings.rs +++ b/crates/biome_lsp/src/extension_settings.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; use serde_json::{Error, Value}; -use tracing::trace; +use tracing::debug; pub(crate) const CONFIGURATION_SECTION: &str = "biome"; @@ -46,7 +46,7 @@ impl ExtensionSettings { pub(crate) fn set_workspace_settings(&mut self, value: Value) -> Result<(), Error> { let workspace_settings = serde_json::from_value(value)?; self.settings = workspace_settings; - trace!( + debug!( "Correctly stored the settings coming from the client: {:?}", self.settings ); diff --git a/crates/biome_lsp/src/handlers/analysis.rs b/crates/biome_lsp/src/handlers/analysis.rs index 50664c50d6bc..43bebbe21252 100644 --- a/crates/biome_lsp/src/handlers/analysis.rs +++ b/crates/biome_lsp/src/handlers/analysis.rs @@ -23,7 +23,7 @@ use std::ops::Sub; use tower_lsp::lsp_types::{ self as lsp, CodeActionKind, CodeActionOrCommand, CodeActionParams, CodeActionResponse, }; -use tracing::{debug, info, trace}; +use tracing::{debug, info}; const FIX_ALL_CATEGORY: ActionCategory = ActionCategory::Source(SourceActionKind::FixAll); @@ -37,7 +37,7 @@ fn fix_all_kind() -> CodeActionKind { /// Queries the [`AnalysisServer`] for code actions of the file matching its path /// /// If the AnalysisServer has no matching file, results in error. -#[tracing::instrument(level = "trace", skip_all, fields(uri = display(& params.text_document.uri), range = debug(params.range), only = debug(& params.context.only), diagnostics = debug(& params.context.diagnostics)), err)] +#[tracing::instrument(level = "debug", skip_all, fields(uri = display(& params.text_document.uri), range = debug(params.range), only = debug(& params.context.only), diagnostics = debug(& params.context.diagnostics)), err)] pub(crate) fn code_actions( session: &Session, params: CodeActionParams, @@ -138,7 +138,7 @@ pub(crate) fn code_actions( } }; - trace!("Filters: {:?}", &filters); + debug!("Filters: {:?}", &filters); // Generate an additional code action to apply all safe fixes on the // document if the action category "source.fixAll" was explicitly requested @@ -193,7 +193,7 @@ pub(crate) fn code_actions( // Remove actions that do not match the categories requested by the // language client let matches_filters = filters.iter().any(|filter| { - trace!( + debug!( "Filter {:?}, category {:?}", filter, action.category.to_str() diff --git a/crates/biome_lsp/src/handlers/rename.rs b/crates/biome_lsp/src/handlers/rename.rs index bafc565bf830..6cca677dacef 100644 --- a/crates/biome_lsp/src/handlers/rename.rs +++ b/crates/biome_lsp/src/handlers/rename.rs @@ -5,7 +5,6 @@ use crate::{session::Session, utils}; use anyhow::{Context, Result}; use biome_lsp_converters::from_proto; use tower_lsp::lsp_types::{RenameParams, WorkspaceEdit}; -use tracing::trace; #[tracing::instrument(level = "debug", skip(session), err)] pub(crate) fn rename( @@ -15,8 +14,6 @@ pub(crate) fn rename( let url = params.text_document_position.text_document.uri; let path = session.file_path(&url)?; - trace!("Renaming..."); - let doc = session.document(&url)?; let position_encoding = session.position_encoding(); let cursor_range = from_proto::offset( diff --git a/crates/biome_lsp/src/handlers/text_document.rs b/crates/biome_lsp/src/handlers/text_document.rs index 935c8dbd6bc1..bbbb1fbae230 100644 --- a/crates/biome_lsp/src/handlers/text_document.rs +++ b/crates/biome_lsp/src/handlers/text_document.rs @@ -82,8 +82,8 @@ pub(crate) async fn did_change( project_key: doc.project_key, path: path.clone(), })?; - tracing::trace!("old document: {:?}", old_text); - tracing::trace!("content changes: {:?}", params.content_changes); + tracing::debug!("old document: {:?}", old_text); + tracing::debug!("content changes: {:?}", params.content_changes); let text = apply_document_changes( session.position_encoding(), @@ -91,7 +91,7 @@ pub(crate) async fn did_change( params.content_changes, ); - tracing::trace!("new document: {:?}", text); + tracing::debug!("new document: {:?}", text); session.insert_document(url.clone(), Document::new(doc.project_key, version, &text)); diff --git a/crates/biome_lsp/src/server.rs b/crates/biome_lsp/src/server.rs index af3c7748d193..dde0b1bb4419 100644 --- a/crates/biome_lsp/src/server.rs +++ b/crates/biome_lsp/src/server.rs @@ -27,7 +27,7 @@ use tokio::task::spawn_blocking; use tower_lsp::jsonrpc::Result as LspResult; use tower_lsp::{lsp_types::*, ClientSocket}; use tower_lsp::{LanguageServer, LspService, Server}; -use tracing::{error, info, trace, warn}; +use tracing::{error, info, warn}; pub struct LSPServer { pub(crate) session: SessionHandle, @@ -59,12 +59,6 @@ impl LSPServer { } async fn syntax_tree_request(&self, params: SyntaxTreePayload) -> LspResult { - trace!( - "Calling method: {}\n with params: {:?}", - SYNTAX_TREE_REQUEST, - ¶ms - ); - let url = params.text_document.uri; requests::syntax_tree::syntax_tree(&self.session, &url).map_err(into_lsp_error) } @@ -234,7 +228,7 @@ impl LanguageServer for LSPServer { // The `root_path` field is deprecated, but we still read it so we can print a warning about it #[expect(deprecated)] #[tracing::instrument( - level = "trace", + level = "debug", skip_all, fields( root_uri = params.root_uri.as_ref().map(display), @@ -275,7 +269,7 @@ impl LanguageServer for LSPServer { Ok(init) } - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] async fn initialized(&self, params: InitializedParams) { let _ = params; @@ -302,7 +296,7 @@ impl LanguageServer for LSPServer { Ok(()) } - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] async fn did_change_configuration(&self, params: DidChangeConfigurationParams) { let _ = params; self.session.load_workspace_settings().await; @@ -311,7 +305,7 @@ impl LanguageServer for LSPServer { self.session.update_all_diagnostics().await; } - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) { let file_paths = params .changes diff --git a/crates/biome_lsp/src/session.rs b/crates/biome_lsp/src/session.rs index ba3639fc4ac6..0fbc47e139d8 100644 --- a/crates/biome_lsp/src/session.rs +++ b/crates/biome_lsp/src/session.rs @@ -315,7 +315,7 @@ impl Session { /// Computes diagnostics for the file matching the provided url and publishes /// them to the client. Called from [`handlers::text_document`] when a file's /// contents changes. - #[tracing::instrument(level = "trace", skip_all, fields(url = display(&url), diagnostic_count), err)] + #[tracing::instrument(level = "debug", skip_all, fields(url = display(&url), diagnostic_count), err)] pub(crate) async fn update_diagnostics(&self, url: lsp_types::Url) -> Result<(), LspError> { let doc = self.document(&url)?; self.update_diagnostics_for_document(url, doc).await @@ -324,7 +324,7 @@ impl Session { /// Computes diagnostics for the file matching the provided url and publishes /// them to the client. Called from [`handlers::text_document`] when a file's /// contents changes. - #[tracing::instrument(level = "trace", skip_all, fields(url = display(&url), diagnostic_count), err)] + #[tracing::instrument(level = "debug", skip_all, fields(url = display(&url), diagnostic_count), err)] async fn update_diagnostics_for_document( &self, url: lsp_types::Url, @@ -371,7 +371,7 @@ impl Session { enabled_rules: Vec::new(), })?; - tracing::trace!("biome diagnostics: {:#?}", result.diagnostics); + tracing::debug!("biome diagnostics: {:#?}", result.diagnostics); let content = self.workspace.get_file_content(GetFileContentParams { project_key: doc.project_key, path: biome_path.clone(), @@ -471,7 +471,7 @@ impl Session { /// This function attempts to read the `biome.json` configuration file from /// the root URI and update the workspace settings accordingly - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] pub(crate) async fn load_workspace_settings(self: &Arc) { // Providing a custom configuration path will not allow to support workspaces if let Some(config_path) = &self.config_path { @@ -534,6 +534,7 @@ impl Session { .await; } + #[tracing::instrument(level = "debug", skip(self))] async fn load_biome_configuration_file( self: &Arc, base_path: ConfigurationPathHint, @@ -645,7 +646,7 @@ impl Session { } /// Requests "workspace/configuration" from client and updates Session config - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] pub(crate) async fn load_extension_settings(&self) { let item = lsp_types::ConfigurationItem { scope_uri: None, diff --git a/crates/biome_rowan/Cargo.toml b/crates/biome_rowan/Cargo.toml index 155ef2fc61c4..1be2e42ac575 100644 --- a/crates/biome_rowan/Cargo.toml +++ b/crates/biome_rowan/Cargo.toml @@ -18,7 +18,6 @@ hashbrown = { version = "0.14.5", features = ["inline-more"], default-feat rustc-hash = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true, optional = true } -tracing = { workspace = true } [dev-dependencies] iai = "0.1.1" diff --git a/crates/biome_rowan/src/ast/batch.rs b/crates/biome_rowan/src/ast/batch.rs index 628cef317026..5342d4ab6347 100644 --- a/crates/biome_rowan/src/ast/batch.rs +++ b/crates/biome_rowan/src/ast/batch.rs @@ -9,7 +9,6 @@ use std::{ collections::BinaryHeap, iter::{empty, once}, }; -use tracing::debug; pub trait BatchMutationExt: AstNode where @@ -298,7 +297,6 @@ where }); let parent_depth = parent.as_ref().map(|p| p.ancestors().count()).unwrap_or(0); - debug!("pushing change..."); self.changes.push(CommitChange { parent_depth, parent, diff --git a/crates/biome_service/src/configuration.rs b/crates/biome_service/src/configuration.rs index 35e5074cc84a..426ae63f68fd 100644 --- a/crates/biome_service/src/configuration.rs +++ b/crates/biome_service/src/configuration.rs @@ -28,6 +28,7 @@ use std::io::ErrorKind; use std::iter::FusedIterator; use std::ops::Deref; use std::path::Path; +use tracing::instrument; /// Information regarding the configuration that was found. /// @@ -142,6 +143,7 @@ impl LoadedConfiguration { } /// Load the partial configuration for this session of the CLI. +#[instrument(level = "debug", skip(fs))] pub fn load_configuration( fs: &dyn FileSystem, config_path: ConfigurationPathHint, @@ -169,6 +171,7 @@ type LoadConfig = Result, WorkspaceError>; /// - Otherwise, the function will try to traverse upwards the file system until it finds a `biome.json` or `biome.jsonc` /// file, or there aren't directories anymore. In this case, the function will not error but return an `Ok(None)`, which /// means Biome will use the default configuration. +#[instrument(level = "debug", skip(fs))] fn load_config(fs: &dyn FileSystem, base_path: ConfigurationPathHint) -> LoadConfig { // This path is used for configuration resolution from external packages. let external_resolution_base_path = match base_path { diff --git a/crates/biome_service/src/file_handlers/astro.rs b/crates/biome_service/src/file_handlers/astro.rs index d056366ba059..f65d0307dd31 100644 --- a/crates/biome_service/src/file_handlers/astro.rs +++ b/crates/biome_service/src/file_handlers/astro.rs @@ -121,7 +121,7 @@ fn parse( } } -#[tracing::instrument(level = "trace", skip(parse, settings))] +#[tracing::instrument(level = "debug", skip(parse, settings))] fn format( biome_path: &BiomePath, document_file_source: &DocumentFileSource, diff --git a/crates/biome_service/src/file_handlers/css.rs b/crates/biome_service/src/file_handlers/css.rs index a614e9d256d4..308652faa78a 100644 --- a/crates/biome_service/src/file_handlers/css.rs +++ b/crates/biome_service/src/file_handlers/css.rs @@ -447,7 +447,7 @@ fn format( ) -> Result { let options = settings.format_options::(biome_path, document_file_source); - tracing::debug!("Format with the following options: \n{}", options); + tracing::debug!("Format with the following options: {:?}", options); let tree = parse.syntax(); let formatted = format_node(options, &tree)?; diff --git a/crates/biome_service/src/file_handlers/graphql.rs b/crates/biome_service/src/file_handlers/graphql.rs index 01e1b7446283..66616269d189 100644 --- a/crates/biome_service/src/file_handlers/graphql.rs +++ b/crates/biome_service/src/file_handlers/graphql.rs @@ -372,7 +372,7 @@ fn format( ) -> Result { let options = settings.format_options::(biome_path, document_file_source); - tracing::debug!("Format with the following options: \n{}", options); + tracing::debug!("Format with the following options: {:?}", options); let tree = parse.syntax(); let formatted = format_node(options, &tree)?; @@ -461,7 +461,6 @@ fn lint(params: LintParams) -> LintResults { range: None, }; - info!("Analyze file {}", params.path.as_str()); let mut process_lint = ProcessLint::new(¶ms); let (_, analyze_diagnostics) = analyze(&tree, filter, &analyzer_options, |signal| { diff --git a/crates/biome_service/src/file_handlers/grit.rs b/crates/biome_service/src/file_handlers/grit.rs index a42b9bb751ab..fe9fbb073a14 100644 --- a/crates/biome_service/src/file_handlers/grit.rs +++ b/crates/biome_service/src/file_handlers/grit.rs @@ -343,7 +343,7 @@ fn format( ) -> Result { let options = settings.format_options::(biome_path, document_file_source); - tracing::debug!("Format with the following options: \n{}", options); + tracing::debug!("Format with the following options: {:?}", options); let tree = parse.syntax(); let formatted = format_node(options, &tree)?; diff --git a/crates/biome_service/src/file_handlers/html.rs b/crates/biome_service/src/file_handlers/html.rs index 6a051b90c751..0a3aa1f15f54 100644 --- a/crates/biome_service/src/file_handlers/html.rs +++ b/crates/biome_service/src/file_handlers/html.rs @@ -281,7 +281,7 @@ fn format( ) -> Result { let options = settings.format_options::(biome_path, document_file_source); - tracing::debug!("Format with the following options: \n{}", options); + tracing::debug!("Format with the following options: {:?}", options); let tree = parse.syntax(); let formatted = format_node(options, &tree)?; diff --git a/crates/biome_service/src/file_handlers/javascript.rs b/crates/biome_service/src/file_handlers/javascript.rs index 87d5a054d6d8..5fe8ab06ab08 100644 --- a/crates/biome_service/src/file_handlers/javascript.rs +++ b/crates/biome_service/src/file_handlers/javascript.rs @@ -55,7 +55,7 @@ use camino::Utf8Path; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::fmt::Debug; -use tracing::{debug, debug_span, error, info, trace, trace_span}; +use tracing::{debug, debug_span, error, trace_span}; #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] @@ -663,7 +663,6 @@ pub(crate) fn lint(params: LintParams) -> LintResults { range: None, }; - info!("Analyze file {}", params.path.as_str()); let mut process_lint = ProcessLint::new(¶ms); let (_, analyze_diagnostics) = analyze( &tree, @@ -725,7 +724,7 @@ pub(crate) fn code_actions(params: CodeActionsParams) -> PullActionsResult { }; }; - trace!("Javascript runs the analyzer"); + debug!("Javascript runs the analyzer"); analyze( &tree, filter, @@ -735,7 +734,7 @@ pub(crate) fn code_actions(params: CodeActionsParams) -> PullActionsResult { project_layout, |signal| { actions.extend(signal.actions().into_code_action_iter().map(|item| { - trace!("Pulled action category {:?}", item.category); + debug!("Pulled action category {:?}", item.category); CodeAction { category: item.category.clone(), rule_name: item @@ -904,7 +903,6 @@ pub(crate) fn fix_all(params: FixAllParams) -> Result Result { let options = settings.format_options::(biome_path, document_file_source); - - debug!("Options used for format: \n{}", options); - + debug!("{:?}", &options); let tree = parse.syntax(); - info!("Format file {}", biome_path.as_str()); let formatted = format_node(options, &tree)?; match formatted.print() { Ok(printed) => Ok(printed), @@ -927,7 +922,7 @@ pub(crate) fn format( } } -#[tracing::instrument(level = "trace", skip(parse, settings))] +#[tracing::instrument(level = "debug", skip(parse, settings, document_file_source))] pub(crate) fn format_range( biome_path: &BiomePath, document_file_source: &DocumentFileSource, @@ -936,13 +931,13 @@ pub(crate) fn format_range( range: TextRange, ) -> Result { let options = settings.format_options::(biome_path, document_file_source); - + debug!("{:?}", &options); let tree = parse.syntax(); let printed = biome_js_formatter::format_range(options, &tree, range)?; Ok(printed) } -#[tracing::instrument(level = "trace", skip(parse, settings))] +#[tracing::instrument(level = "debug", skip(parse, settings, document_file_source))] pub(crate) fn format_on_type( path: &BiomePath, document_file_source: &DocumentFileSource, @@ -951,7 +946,7 @@ pub(crate) fn format_on_type( offset: TextSize, ) -> Result { let options = settings.format_options::(path, document_file_source); - + debug!("{:?}", &options); let tree = parse.syntax(); let range = tree.text_range_with_trivia(); diff --git a/crates/biome_service/src/file_handlers/json.rs b/crates/biome_service/src/file_handlers/json.rs index 2c1137c5ea62..7f09dd5392c9 100644 --- a/crates/biome_service/src/file_handlers/json.rs +++ b/crates/biome_service/src/file_handlers/json.rs @@ -42,7 +42,7 @@ use biome_rowan::{AstNode, NodeCache}; use biome_rowan::{TextRange, TextSize, TokenAtOffset}; use camino::Utf8Path; use std::borrow::Cow; -use tracing::{debug_span, error, instrument, trace}; +use tracing::{debug_span, error, instrument}; #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] @@ -437,7 +437,7 @@ fn format( ) -> Result { let options = settings.format_options::(path, document_file_source); - tracing::debug!("Format with the following options: \n{}", options); + tracing::debug!("Format with the following options: {:?}", options); let tree = parse.syntax(); let formatted = format_node(options, &tree)?; @@ -610,7 +610,6 @@ fn code_actions(params: CodeActionsParams) -> PullActionsResult { return PullActionsResult { actions: vec![] }; }; - trace!("JSON runs the analyzer"); analyze(&tree, filter, &analyzer_options, file_source, |signal| { actions.extend(signal.actions().into_code_action_iter().map(|item| { CodeAction { diff --git a/crates/biome_service/src/file_handlers/svelte.rs b/crates/biome_service/src/file_handlers/svelte.rs index 38ad2648290d..38bb4386e28c 100644 --- a/crates/biome_service/src/file_handlers/svelte.rs +++ b/crates/biome_service/src/file_handlers/svelte.rs @@ -131,7 +131,7 @@ fn parse( } } -#[tracing::instrument(level = "trace", skip(parse, settings))] +#[tracing::instrument(level = "debug", skip(parse, settings))] fn format( biome_path: &BiomePath, document_file_source: &DocumentFileSource, diff --git a/crates/biome_service/src/file_handlers/vue.rs b/crates/biome_service/src/file_handlers/vue.rs index bfb7fcfeb39d..9468e0c880b1 100644 --- a/crates/biome_service/src/file_handlers/vue.rs +++ b/crates/biome_service/src/file_handlers/vue.rs @@ -131,7 +131,7 @@ fn parse( } } -#[tracing::instrument(level = "trace", skip(parse, settings))] +#[tracing::instrument(level = "debug", skip(parse, settings))] fn format( biome_path: &BiomePath, document_file_source: &DocumentFileSource, diff --git a/crates/biome_service/src/projects.rs b/crates/biome_service/src/projects.rs index 771705e3fb09..da7f702773b4 100644 --- a/crates/biome_service/src/projects.rs +++ b/crates/biome_service/src/projects.rs @@ -5,9 +5,10 @@ use camino::{Utf8Path, Utf8PathBuf}; use papaya::HashMap; use rustc_hash::FxBuildHasher; use serde::{Deserialize, Serialize}; +use std::fmt::Display; use std::num::NonZeroUsize; use std::sync::atomic::{AtomicUsize, Ordering}; -use tracing::trace; +use tracing::{debug, instrument}; /// Type that holds all the settings and information for different projects /// inside the workspace. @@ -26,6 +27,12 @@ pub struct Projects(HashMap); #[repr(transparent)] pub struct ProjectKey(NonZeroUsize); +impl Display for ProjectKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ProjectKey {}", self.0.get()) + } +} + impl ProjectKey { #[expect(clippy::new_without_default)] pub fn new() -> Self { @@ -53,8 +60,9 @@ impl Projects { /// /// Returns the key of the newly inserted project, or returns an existing /// project key if a project with the given path already existed. + #[instrument(skip(self, path), fields(path))] pub fn insert_project(&self, path: Utf8PathBuf) -> ProjectKey { - trace!("Insert workspace folder: {path:?}"); + debug!("Insert workspace folder {}", path.as_str()); let data = self.0.pin(); for (key, project_data) in data.iter() { diff --git a/crates/biome_service/src/settings.rs b/crates/biome_service/src/settings.rs index 713ead614b88..1f310ef8c2d5 100644 --- a/crates/biome_service/src/settings.rs +++ b/crates/biome_service/src/settings.rs @@ -42,6 +42,7 @@ use ignore::gitignore::{Gitignore, GitignoreBuilder}; use rustc_hash::FxHashMap; use std::borrow::Cow; use std::ops::Deref; +use tracing::instrument; /// Global settings for the entire project. #[derive(Clone, Debug, Default)] @@ -62,7 +63,7 @@ pub struct Settings { impl Settings { /// Merges the [Configuration] into the settings. - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] pub fn merge_with_configuration( &mut self, configuration: Configuration, @@ -655,6 +656,7 @@ impl WorkspaceSettingsHandle { } /// Resolve the formatting context for the given language + #[instrument(level = "debug", skip(file_source))] pub fn format_options( &self, path: &BiomePath, diff --git a/crates/biome_service/src/workspace.rs b/crates/biome_service/src/workspace.rs index fcf33df4c380..d59c23d4cf59 100644 --- a/crates/biome_service/src/workspace.rs +++ b/crates/biome_service/src/workspace.rs @@ -107,6 +107,15 @@ pub struct FileFeaturesResult { pub features_supported: HashMap, } +impl std::fmt::Display for FileFeaturesResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (feature, support_kind) in self.features_supported.iter() { + write!(f, "{}: {}, ", feature, support_kind)?; + } + Ok(()) + } +} + impl FileFeaturesResult { /// Sorted array of files that should not be processed no matter the cases. /// These files are handled by other tools. @@ -225,7 +234,7 @@ impl FileFeaturesResult { } debug!( - "The file has the following feature sets: \n{:?}", + "The file has the following feature sets: {:?}", &self.features_supported ); @@ -371,6 +380,18 @@ pub enum SupportKind { FileNotSupported, } +impl std::fmt::Display for SupportKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SupportKind::Supported => write!(f, "Supported"), + SupportKind::Ignored => write!(f, "Ignored"), + SupportKind::Protected => write!(f, "Protected"), + SupportKind::FeatureNotEnabled => write!(f, "FeatureNotEnabled"), + SupportKind::FileNotSupported => write!(f, "FileNotSupported"), + } + } +} + impl SupportKind { pub const fn is_supported(&self) -> bool { matches!(self, SupportKind::Supported) @@ -402,6 +423,18 @@ pub enum FeatureKind { Debug, } +impl std::fmt::Display for FeatureKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + FeatureKind::Format => write!(f, "Format"), + FeatureKind::Lint => write!(f, "Lint"), + FeatureKind::Search => write!(f, "Search"), + FeatureKind::Assist => write!(f, "Assist"), + FeatureKind::Debug => write!(f, "Debug"), + } + } +} + #[derive(Debug, Copy, Clone, Hash, serde::Serialize, serde::Deserialize, Eq, PartialEq)] #[serde( from = "smallvec::SmallVec<[FeatureKind; 6]>", diff --git a/crates/biome_service/src/workspace/scanner.rs b/crates/biome_service/src/workspace/scanner.rs index 2e278d15afc3..b2ba49ae47df 100644 --- a/crates/biome_service/src/workspace/scanner.rs +++ b/crates/biome_service/src/workspace/scanner.rs @@ -1,3 +1,6 @@ +use biome_diagnostics::serde::Diagnostic; +use biome_diagnostics::{Diagnostic as _, Error, Severity}; +use biome_fs::{BiomePath, PathInterner, TraversalContext, TraversalScope}; use camino::Utf8Path; use crossbeam::channel::{unbounded, Receiver, Sender}; use rayon::ThreadPoolBuilder; @@ -6,10 +9,7 @@ use std::panic::catch_unwind; use std::sync::{Once, RwLock}; use std::thread; use std::time::{Duration, Instant}; - -use biome_diagnostics::serde::Diagnostic; -use biome_diagnostics::{Diagnostic as _, Error, Severity}; -use biome_fs::{BiomePath, PathInterner, TraversalContext, TraversalScope}; +use tracing::instrument; use crate::diagnostics::Panic; use crate::projects::ProjectKey; @@ -26,6 +26,7 @@ pub(crate) struct ScanResult { pub duration: Duration, } +#[instrument(level = "debug", skip(workspace))] pub(crate) fn scan( workspace: &WorkspaceServer, project_key: ProjectKey, @@ -85,6 +86,7 @@ fn init_thread_pool() { /// Initiates the filesystem traversal tasks from the provided path and runs it to completion. /// /// Returns the duration of the process and the evaluated paths. +#[instrument(level = "debug", skip(ctx))] fn scan_folder(folder: &Utf8Path, ctx: ScanContext) -> Duration { let start = Instant::now(); let fs = ctx.workspace.fs(); diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 1092815f1fe9..9c01f2cbb4cd 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -45,7 +45,7 @@ use rustc_hash::{FxBuildHasher, FxHashMap}; use std::panic::RefUnwindSafe; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -use tracing::{debug, info, info_span, warn}; +use tracing::{info, instrument, warn}; pub(super) struct WorkspaceServer { /// features available throughout the application @@ -203,10 +203,11 @@ impl WorkspaceServer { } /// Gets the supported capabilities for a given file path. + #[instrument(level = "debug", skip(self), fields( + path = display(path.as_path()) + ))] fn get_file_capabilities(&self, path: &BiomePath) -> Capabilities { let language = self.get_file_source(path); - - debug!("File capabilities: {:?} {:?}", &language, &path); self.features.get_capabilities(path, language) } @@ -242,7 +243,7 @@ impl WorkspaceServer { /// Returns a previously inserted file source by index. /// /// File sources can be inserted using `insert_source()`. - #[tracing::instrument(level = "trace", skip(self), fields(return))] + #[tracing::instrument(level = "debug", skip_all)] fn get_source(&self, index: usize) -> Option { if index < self.file_sources.len() { Some(self.file_sources[index]) @@ -255,7 +256,7 @@ impl WorkspaceServer { /// /// Returns the index at which the file source can be retrieved using /// `get_source()`. - #[tracing::instrument(level = "trace", skip(self), fields(return))] + #[tracing::instrument(level = "debug", skip_all)] fn insert_source(&self, document_file_source: DocumentFileSource) -> usize { self.file_sources .iter() @@ -271,19 +272,24 @@ impl WorkspaceServer { self.open_file_internal(true, params) } - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self, params), fields( + project_key = display(params.project_key), + path = display(params.path.as_path()), + version = display(params.version), + ))] fn open_file_internal( &self, opened_by_scanner: bool, - OpenFileParams { + params: OpenFileParams, + ) -> Result<(), WorkspaceError> { + let OpenFileParams { project_key, path, content, version, document_file_source, persist_node_cache, - }: OpenFileParams, - ) -> Result<(), WorkspaceError> { + } = params; let path: Utf8PathBuf = path.into(); let mut source = document_file_source.unwrap_or(DocumentFileSource::from_path(&path)); @@ -605,7 +611,7 @@ impl Workspace for WorkspaceServer { /// ## Panics /// This function may panic if the internal settings mutex has been poisoned /// by another thread having previously panicked while holding the lock - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument(level = "debug", skip(self))] fn update_settings(&self, params: UpdateSettingsParams) -> Result<(), WorkspaceError> { let mut settings = self .projects @@ -639,6 +645,7 @@ impl Workspace for WorkspaceServer { Ok(self.projects.insert_project(path)) } + #[instrument(level = "debug", skip(self))] fn scan_project_folder( &self, params: ScanProjectFolderParams, @@ -833,10 +840,23 @@ impl Workspace for WorkspaceServer { } /// Retrieves the list of diagnostics associated with a file - #[tracing::instrument(level = "trace", skip(self))] + #[tracing::instrument( + level = "debug", + skip(self, params), + fields( + rule_categories = display(¶ms.categories), + path = display(¶ms.path), + project_key = debug(¶ms.project_key), + skip = debug(¶ms.skip), + only = debug(¶ms.only), + max_diagnostics = display(¶ms.max_diagnostics), + ) + )] fn pull_diagnostics( &self, - PullDiagnosticsParams { + params: PullDiagnosticsParams, + ) -> Result { + let PullDiagnosticsParams { project_key, path, categories, @@ -844,8 +864,7 @@ impl Workspace for WorkspaceServer { only, skip, enabled_rules, - }: PullDiagnosticsParams, - ) -> Result { + } = params; let parse = self.get_parse(&path)?; let (diagnostics, errors, skipped_diagnostics) = if let Some(lint) = self.get_file_capabilities(&path).analyzer.lint { @@ -853,27 +872,25 @@ impl Workspace for WorkspaceServer { .projects .get_settings(project_key) .ok_or_else(WorkspaceError::no_project)?; - info_span!("Pulling diagnostics", categories =? categories).in_scope(|| { - let results = lint(LintParams { - parse, - workspace: &settings.into(), - max_diagnostics: max_diagnostics as u32, - path: &path, - only, - skip, - language: self.get_file_source(&path), - categories, - project_layout: self.project_layout.clone(), - suppression_reason: None, - enabled_rules, - }); - - ( - results.diagnostics, - results.errors, - results.skipped_diagnostics, - ) - }) + let results = lint(LintParams { + parse, + workspace: &settings.into(), + max_diagnostics: max_diagnostics as u32, + path: &path, + only, + skip, + language: self.get_file_source(&path), + categories, + project_layout: self.project_layout.clone(), + suppression_reason: None, + enabled_rules, + }); + + ( + results.diagnostics, + results.errors, + results.skipped_diagnostics, + ) } else { let parse_diagnostics = parse.into_diagnostics(); let errors = parse_diagnostics @@ -884,7 +901,11 @@ impl Workspace for WorkspaceServer { (parse_diagnostics, errors, 0) }; - info!("Pulled {:?} diagnostic(s)", diagnostics.len()); + info!( + "Pulled {:?} diagnostic(s), skipped {:?} diagnostic(s)", + diagnostics.len(), + skipped_diagnostics + ); Ok(PullDiagnosticsResult { diagnostics: diagnostics .into_iter() @@ -900,19 +921,25 @@ impl Workspace for WorkspaceServer { /// Retrieves the list of code actions available for a given cursor /// position within a file - #[tracing::instrument(level = "trace", skip(self))] - fn pull_actions( - &self, - PullActionsParams { + #[tracing::instrument( + level = "debug", + skip_all, + fields( + only = debug(¶ms.only), + skip = debug(¶ms.skip), + range = debug(¶ms.range) + ) + )] + fn pull_actions(&self, params: PullActionsParams) -> Result { + let PullActionsParams { project_key, path, range, - suppression_reason, + suppression_reason: _, only, skip, enabled_rules, - }: PullActionsParams, - ) -> Result { + } = params; let capabilities = self.get_file_capabilities(&path); let code_actions = capabilities .analyzer @@ -941,6 +968,13 @@ impl Workspace for WorkspaceServer { /// Runs the given file through the formatter using the provided options /// and returns the resulting source code + #[instrument( + level = "debug", + skip_all, + fields( + path = display(¶ms.path), + ) + )] fn format_file(&self, params: FormatFileParams) -> Result { let capabilities = self.get_file_capabilities(¶ms.path); let format = capabilities @@ -962,6 +996,7 @@ impl Workspace for WorkspaceServer { format(¶ms.path, &document_file_source, parse, handle) } + #[instrument(level = "debug", skip(self, params))] fn format_range(&self, params: FormatRangeParams) -> Result { let capabilities = self.get_file_capabilities(¶ms.path); let format_range = capabilities @@ -989,6 +1024,7 @@ impl Workspace for WorkspaceServer { ) } + #[instrument(level = "debug", skip(self, params))] fn format_on_type(&self, params: FormatOnTypeParams) -> Result { let capabilities = self.get_file_capabilities(¶ms.path); let format_on_type = capabilities @@ -1017,9 +1053,18 @@ impl Workspace for WorkspaceServer { ) } - fn fix_file( - &self, - FixFileParams { + #[instrument( + level = "debug", + skip_all, + fields( + rule_categories = display(¶ms.rule_categories), + skip = debug(¶ms.skip), + only = debug(¶ms.only), + should_format = display(¶ms.should_format), + ) + )] + fn fix_file(&self, params: FixFileParams) -> Result { + let FixFileParams { project_key, path, fix_file_mode, @@ -1029,8 +1074,7 @@ impl Workspace for WorkspaceServer { enabled_rules, rule_categories, suppression_reason, - }: FixFileParams, - ) -> Result { + } = params; let capabilities = self.get_file_capabilities(&path); let fix_all = capabilities