Skip to content

Commit 3a6f3d8

Browse files
authored
feat: add tracing instrumentation for performance measurement (#178)
## Summary - Add `#[tracing::instrument(level = "debug", skip_all)]` attributes to key performance-critical functions across `vite_task`, `vite_task_graph`, `vite_task_plan`, and `vite_workspace` crates - Add `tracing` as a dependency to `vite_task_graph` and `vite_workspace` crates (other crates already had it) - Fix a drop-order warning in `execute_synthetic` by binding match result to a variable ## Instrumented functions - **Session**: `init`, `init_with`, `main`, `ensure_task_graph_loaded`, `execute_synthetic`, `plan_from_cli_run`, `execute_graph` - **Execution**: `execute_expanded_graph`, `execute_leaf`, `execute_spawn`, `spawn_inherited`, `spawn_with_tracking` - **Cache**: `load_from_path`, `try_hit`, `update` - **Fingerprint**: `PostRunFingerprint::create`, `PostRunFingerprint::validate` - **Task graph**: `IndexedTaskGraph::load`, `load_task_graph` - **Planning**: `plan_query`, `plan_synthetic` - **Workspace**: `load_package_graph`, `discover_package_graph` ## Test plan - [x] `cargo check --all-targets` passes with no errors - [ ] CI passes on all platforms 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 9e1287e commit 3a6f3d8

File tree

11 files changed

+31
-3
lines changed

11 files changed

+31
-3
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vite_task/src/session/cache/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ impl Display for FingerprintMismatch {
7575
}
7676

7777
impl ExecutionCache {
78+
#[tracing::instrument(level = "debug", skip_all)]
7879
pub fn load_from_path(path: &AbsolutePath) -> anyhow::Result<Self> {
7980
tracing::info!("Creating task cache directory at {:?}", path);
8081
std::fs::create_dir_all(path)?;
@@ -130,6 +131,7 @@ impl ExecutionCache {
130131

131132
/// Try to hit cache with spawn fingerprint.
132133
/// Returns `Ok(Ok(cache_value))` on cache hit, `Ok(Err(cache_miss))` on miss.
134+
#[tracing::instrument(level = "debug", skip_all)]
133135
pub async fn try_hit(
134136
&self,
135137
cache_metadata: &CacheMetadata,
@@ -175,6 +177,7 @@ impl ExecutionCache {
175177
}
176178

177179
/// Update cache after successful execution.
180+
#[tracing::instrument(level = "debug", skip_all)]
178181
pub async fn update(
179182
&self,
180183
cache_metadata: &CacheMetadata,

crates/vite_task/src/session/execute/fingerprint.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ impl PostRunFingerprint {
7373
/// * `path_reads` - Map of paths that were read during execution
7474
/// * `base_dir` - Workspace root for resolving relative paths
7575
/// * `fingerprint_ignores` - Optional glob patterns to exclude from fingerprinting
76+
#[tracing::instrument(level = "debug", skip_all, name = "create_post_run_fingerprint")]
7677
pub fn create(
7778
path_reads: &HashMap<RelativePathBuf, PathRead>,
7879
base_dir: &AbsolutePath,
@@ -102,6 +103,7 @@ impl PostRunFingerprint {
102103

103104
/// Validates the fingerprint against current filesystem state.
104105
/// Returns `Some(mismatch)` if validation fails, `None` if valid.
106+
#[tracing::instrument(level = "debug", skip_all, name = "validate_post_run_fingerprint")]
105107
pub fn validate(
106108
&self,
107109
base_dir: &AbsolutePath,

crates/vite_task/src/session/execute/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ impl ExecutionContext<'_> {
7272
///
7373
/// Leaf-level errors are reported through the reporter and do not abort the graph.
7474
/// Cycle detection is handled at plan time, so this function cannot encounter cycles.
75+
#[tracing::instrument(level = "debug", skip_all)]
7576
#[expect(clippy::future_not_send, reason = "uses !Send types internally")]
7677
async fn execute_expanded_graph(
7778
&mut self,
@@ -117,6 +118,7 @@ impl ExecutionContext<'_> {
117118
///
118119
/// Creates a [`LeafExecutionReporter`] from the graph reporter and delegates
119120
/// to the appropriate execution method.
121+
#[tracing::instrument(level = "debug", skip_all)]
120122
#[expect(clippy::future_not_send, reason = "uses !Send types internally")]
121123
async fn execute_leaf(
122124
&mut self,
@@ -174,6 +176,7 @@ impl ExecutionContext<'_> {
174176
///
175177
/// Errors (cache lookup failure, spawn failure, cache update failure) are reported
176178
/// through `leaf_reporter.finish()` and do not abort the caller.
179+
#[tracing::instrument(level = "debug", skip_all)]
177180
#[expect(clippy::future_not_send, reason = "uses !Send types internally")]
178181
#[expect(
179182
clippy::too_many_lines,
@@ -371,6 +374,7 @@ pub async fn execute_spawn(
371374
///
372375
/// The child process will see `is_terminal() == true` for stdout/stderr when the
373376
/// parent is running in a terminal. This is expected behavior.
377+
#[tracing::instrument(level = "debug", skip_all)]
374378
async fn spawn_inherited(spawn_command: &SpawnCommand) -> anyhow::Result<SpawnResult> {
375379
let mut cmd = fspy::Command::new(spawn_command.program_path.as_path());
376380
cmd.args(spawn_command.args.iter().map(vite_str::Str::as_str));
@@ -427,6 +431,7 @@ impl Session<'_> {
427431
///
428432
/// Returns `Err(ExitStatus)` to indicate the caller should exit with the given status code.
429433
/// Returns `Ok(())` when all tasks succeeded.
434+
#[tracing::instrument(level = "debug", skip_all)]
430435
#[expect(clippy::future_not_send, reason = "uses !Send types internally")]
431436
pub(crate) async fn execute_graph(
432437
&self,

crates/vite_task/src/session/execute/spawn.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ pub struct SpawnTrackResult {
6464
/// - stdin is always `/dev/null` (piped mode is for non-interactive execution).
6565
/// - `stdout_writer`/`stderr_writer` receive the child's stdout/stderr output in real-time.
6666
/// - `track_result` if provided, will be populated with captured outputs and path accesses for caching. If `None`, tracking is disabled.
67+
#[tracing::instrument(level = "debug", skip_all)]
6768
#[expect(clippy::future_not_send, reason = "uses !Send dyn AsyncWrite writers internally")]
6869
#[expect(
6970
clippy::too_many_lines,

crates/vite_task/src/session/mod.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ impl TaskGraphLoader for LazyTaskGraph<'_> {
4242
async fn load_task_graph(
4343
&mut self,
4444
) -> Result<&vite_task_graph::IndexedTaskGraph, TaskGraphLoadError> {
45+
let _span = tracing::debug_span!("load_task_graph").entered();
4546
Ok(match self {
4647
Self::Uninitialized { workspace_root, config_loader } => {
4748
let graph = IndexedTaskGraph::load(workspace_root, *config_loader).await?;
@@ -159,6 +160,7 @@ impl<'a> Session<'a> {
159160
///
160161
/// Returns an error if the current directory cannot be determined or
161162
/// if workspace initialization fails.
163+
#[tracing::instrument(level = "debug", skip_all)]
162164
pub fn init(callbacks: SessionCallbacks<'a>) -> anyhow::Result<Self> {
163165
let envs = std::env::vars_os()
164166
.map(|(k, v)| (Arc::<OsStr>::from(k.as_os_str()), Arc::<OsStr>::from(v.as_os_str())))
@@ -171,6 +173,7 @@ impl<'a> Session<'a> {
171173
/// # Errors
172174
///
173175
/// Returns an error if the task graph cannot be loaded from the workspace configuration.
176+
#[tracing::instrument(level = "debug", skip_all)]
174177
#[expect(
175178
clippy::future_not_send,
176179
reason = "session is single-threaded, futures do not need to be Send"
@@ -186,6 +189,7 @@ impl<'a> Session<'a> {
186189
/// # Errors
187190
///
188191
/// Returns an error if workspace root cannot be found or PATH env cannot be prepended.
192+
#[tracing::instrument(level = "debug", skip_all)]
189193
#[expect(
190194
clippy::needless_pass_by_value,
191195
reason = "cwd is an Arc that gets cloned internally, pass by value is intentional"
@@ -222,6 +226,7 @@ impl<'a> Session<'a> {
222226
/// # Errors
223227
///
224228
/// Returns an error if planning or execution fails.
229+
#[tracing::instrument(level = "debug", skip_all)]
225230
#[expect(
226231
clippy::future_not_send,
227232
reason = "session is single-threaded, futures do not need to be Send"
@@ -500,6 +505,7 @@ impl<'a> Session<'a> {
500505
/// # Errors
501506
///
502507
/// Returns an error if planning or execution of the synthetic command fails.
508+
#[tracing::instrument(level = "debug", skip_all)]
503509
#[expect(
504510
clippy::future_not_send,
505511
reason = "session is single-threaded, futures do not need to be Send"
@@ -531,14 +537,14 @@ impl<'a> Session<'a> {
531537
reporter::PlainReporter::new(silent_if_cache_hit, Box::new(tokio::io::stdout()));
532538

533539
// Execute the spawn directly using the free function, bypassing the graph pipeline
534-
match execute::execute_spawn(
540+
let outcome = execute::execute_spawn(
535541
Box::new(plain_reporter),
536542
&spawn_execution,
537543
cache,
538544
&self.workspace_path,
539545
)
540-
.await
541-
{
546+
.await;
547+
match outcome {
542548
// Cache hit — no process was spawned, success
543549
execute::SpawnOutcome::CacheHit => Ok(ExitStatus::SUCCESS),
544550
// Process ran successfully
@@ -562,6 +568,7 @@ impl<'a> Session<'a> {
562568
/// # Errors
563569
///
564570
/// Returns an error if the plan request cannot be parsed or if planning fails.
571+
#[tracing::instrument(level = "debug", skip_all)]
565572
#[expect(
566573
clippy::future_not_send,
567574
reason = "session is single-threaded, futures do not need to be Send"
@@ -575,6 +582,7 @@ impl<'a> Session<'a> {
575582
}
576583

577584
/// Internal: plans execution from a resolved run command.
585+
#[tracing::instrument(level = "debug", skip_all)]
578586
#[expect(
579587
clippy::future_not_send,
580588
reason = "session is single-threaded, futures do not need to be Send"

crates/vite_task_graph/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rustc-hash = { workspace = true }
1515
serde = { workspace = true, features = ["derive"] }
1616
serde_json = { workspace = true }
1717
thiserror = { workspace = true }
18+
tracing = { workspace = true }
1819
vite_graph_ser = { workspace = true }
1920
vite_path = { workspace = true }
2021
vite_str = { workspace = true }

crates/vite_task_graph/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ impl IndexedTaskGraph {
180180
/// Returns [`TaskGraphLoadError`] if the package graph fails to load, a config file
181181
/// cannot be read, a task config cannot be resolved, a dependency specifier is invalid,
182182
/// or `cacheScripts` is set in a non-root package.
183+
#[tracing::instrument(level = "debug", skip_all)]
183184
#[expect(
184185
clippy::too_many_lines,
185186
reason = "graph loading is inherently sequential and multi-step"

crates/vite_task_plan/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ pub trait TaskGraphLoader {
187187
///
188188
/// # Errors
189189
/// Returns an error if task graph loading, query, or execution planning fails.
190+
#[tracing::instrument(level = "debug", skip_all)]
190191
#[expect(clippy::future_not_send, reason = "PlanRequestParser and TaskGraphLoader are !Send")]
191192
#[expect(clippy::implicit_hasher, reason = "FxHashMap is the only hasher used in this codebase")]
192193
pub async fn plan_query(
@@ -217,6 +218,7 @@ pub async fn plan_query(
217218
///
218219
/// # Errors
219220
/// Returns an error if the program is not found or path fingerprinting fails.
221+
#[tracing::instrument(level = "debug", skip_all)]
220222
#[expect(clippy::result_large_err, reason = "Error is large for diagnostics")]
221223
pub fn plan_synthetic(
222224
workspace_path: &Arc<AbsolutePath>,

crates/vite_workspace/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ serde = { workspace = true, features = ["derive"] }
1717
serde_json = { workspace = true, features = ["preserve_order"] }
1818
serde_yml = { workspace = true }
1919
thiserror = { workspace = true }
20+
tracing = { workspace = true }
2021
vec1 = { workspace = true, features = ["smallvec-v1"] }
2122
vite_glob = { workspace = true }
2223
vite_path = { workspace = true }

0 commit comments

Comments
 (0)