Skip to content

Commit 8fa18f9

Browse files
authored
fix(v8-runtime): log warm-pool and snapshot diagnostics through tracing (#2045)
1 parent 0ec5f92 commit 8fa18f9

4 files changed

Lines changed: 85 additions & 42 deletions

File tree

‎Cargo.lock‎

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

‎crates/v8-runtime/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ ciborium = "0.2"
2121
serde = "1.0"
2222
sha2 = "0.10"
2323
tokio = { version = "1", features = ["rt", "sync", "time"] }
24+
tracing = "0.1"
2425

2526
[build-dependencies]
2627
agentos-build-support = { workspace = true }

‎crates/v8-runtime/src/session.rs‎

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ impl WarmWorkerPool {
711711
target_count,
712712
);
713713
}) {
714-
eprintln!("ERR_AGENTOS_V8_WARM_REFILL: bounded executor rejected refill: {error}");
714+
tracing::warn!(%error, "ERR_AGENTOS_V8_WARM_REFILL: bounded executor rejected warm worker refill");
715715
self.state
716716
.lock()
717717
.expect("warm worker pool lock poisoned")
@@ -773,10 +773,10 @@ impl WarmWorkerPool {
773773
if let Some((evicted_key, worker)) = evicted {
774774
drop(worker.assignment_tx);
775775
let _ = worker.join_handle.join();
776-
eprintln!(
777-
"agentos-v8-runtime: warm worker evicted key={} heap={}",
778-
warm_key_prefix(&evicted_key),
779-
evicted_key.heap_limit_mb
776+
tracing::debug!(
777+
key = %warm_key_prefix(&evicted_key),
778+
heap_mb = evicted_key.heap_limit_mb,
779+
"warm worker evicted"
780780
);
781781
continue;
782782
}
@@ -802,11 +802,11 @@ impl WarmWorkerPool {
802802
break;
803803
}
804804
workers.push(worker);
805-
eprintln!(
806-
"agentos-v8-runtime: warm worker refilled key={} heap={} pool_size={}",
807-
warm_key_prefix(&key),
808-
key.heap_limit_mb,
809-
workers.len()
805+
tracing::debug!(
806+
key = %warm_key_prefix(&key),
807+
heap_mb = key.heap_limit_mb,
808+
pool_size = workers.len(),
809+
"warm worker refilled"
810810
);
811811
}
812812

@@ -845,22 +845,22 @@ fn spawn_warm_worker(
845845
match precreated {
846846
Ok(precreated) => {
847847
if ready_tx.send(Ok(())).is_err() {
848-
eprintln!("INFO_AGENTOS_STALE_WARM_WORKER: warm worker requester disconnected before startup completed");
848+
tracing::debug!("warm worker requester disconnected before startup completed");
849849
}
850850
if let Ok(assignment) = assignment_rx.recv() {
851851
session_thread(assignment, Some(precreated));
852852
}
853853
}
854854
Err(error) => {
855855
if ready_tx.send(Err(error)).is_err() {
856-
eprintln!("INFO_AGENTOS_STALE_WARM_WORKER: warm worker requester disconnected before startup failure was delivered");
856+
tracing::debug!("warm worker requester disconnected before startup failure was delivered");
857857
}
858858
}
859859
}
860860
}) {
861861
Ok(handle) => handle,
862862
Err(error) => {
863-
eprintln!("agentos-v8-runtime: warm worker spawn failed: {error}");
863+
tracing::warn!(%error, "warm worker spawn failed");
864864
return None;
865865
}
866866
};
@@ -871,19 +871,21 @@ fn spawn_warm_worker(
871871
join_handle,
872872
}),
873873
Ok(Err(error)) => {
874-
eprintln!(
875-
"agentos-v8-runtime: warm worker refill failed key={} heap={}: {error}",
876-
warm_key_prefix(&key),
877-
key.heap_limit_mb
874+
tracing::warn!(
875+
key = %warm_key_prefix(&key),
876+
heap_mb = key.heap_limit_mb,
877+
%error,
878+
"warm worker refill failed"
878879
);
879880
let _ = join_handle.join();
880881
None
881882
}
882883
Err(error) => {
883-
eprintln!(
884-
"agentos-v8-runtime: warm worker refill failed key={} heap={}: {error}",
885-
warm_key_prefix(&key),
886-
key.heap_limit_mb
884+
tracing::warn!(
885+
key = %warm_key_prefix(&key),
886+
heap_mb = key.heap_limit_mb,
887+
%error,
888+
"warm worker refill failed"
887889
);
888890
let _ = join_handle.join();
889891
None
@@ -954,11 +956,13 @@ fn normalize_wall_clock_limit_ms(wall_clock_limit_ms: Option<u32>) -> Option<u32
954956
fn signal_session_shutdown(sender: &Sender<()>, session_id: &str) {
955957
match sender.try_send(()) {
956958
Ok(()) => {}
957-
Err(crossbeam_channel::TrySendError::Full(())) => eprintln!(
958-
"INFO_AGENTOS_VM_SHUTDOWN_COALESCED: session={session_id} already has a pending shutdown signal"
959+
Err(crossbeam_channel::TrySendError::Full(())) => tracing::debug!(
960+
session = %session_id,
961+
"VM shutdown coalesced: a shutdown signal is already pending"
959962
),
960-
Err(crossbeam_channel::TrySendError::Disconnected(())) => eprintln!(
961-
"INFO_AGENTOS_STALE_VM_SHUTDOWN: session={session_id} executor already disconnected"
963+
Err(crossbeam_channel::TrySendError::Disconnected(())) => tracing::debug!(
964+
session = %session_id,
965+
"VM shutdown skipped: executor already disconnected"
962966
),
963967
}
964968
}
@@ -1519,21 +1523,21 @@ impl SessionManager {
15191523
let key = warm_pool_key(&hint.bridge_code, &hint.userland_code, hint.heap_limit_mb);
15201524
let Some(worker) = self.warm_pool.claim(&key) else {
15211525
record_warm_worker_miss();
1522-
eprintln!(
1523-
"agentos-v8-runtime: warm worker pool-empty key={} heap={}",
1524-
warm_key_prefix(&key),
1525-
key.heap_limit_mb
1526+
tracing::debug!(
1527+
key = %warm_key_prefix(&key),
1528+
heap_mb = key.heap_limit_mb,
1529+
"warm worker pool empty"
15261530
);
15271531
return Err(assignment);
15281532
};
15291533

15301534
match worker.assignment_tx.send(assignment) {
15311535
Ok(()) => {
15321536
record_warm_worker_hit();
1533-
eprintln!(
1534-
"agentos-v8-runtime: warm worker claimed key={} heap={}",
1535-
warm_key_prefix(&key),
1536-
key.heap_limit_mb
1537+
tracing::debug!(
1538+
key = %warm_key_prefix(&key),
1539+
heap_mb = key.heap_limit_mb,
1540+
"warm worker claimed"
15371541
);
15381542
Ok((worker.join_handle, true))
15391543
}
@@ -1653,9 +1657,10 @@ impl SessionManager {
16531657
quarantined.session_id, quarantined.output_generation
16541658
);
16551659
} else {
1656-
eprintln!(
1657-
"INFO_AGENTOS_VM_EXECUTOR_QUARANTINE_RELEASED: session={} generation={:?}",
1658-
quarantined.session_id, quarantined.output_generation
1660+
tracing::debug!(
1661+
session = %quarantined.session_id,
1662+
generation = ?quarantined.output_generation,
1663+
"VM executor quarantine released"
16591664
);
16601665
}
16611666
self.call_id_router
@@ -2474,9 +2479,9 @@ fn session_thread(
24742479
// that fails (unsupported platform, spawn failure),
24752480
// degrade to a fresh isolate that evaluates the
24762481
// bridge in-context rather than failing the session.
2477-
eprintln!(
2478-
"agentos-v8-runtime: snapshot creation failed, \
2479-
falling back to fresh isolate: {message}"
2482+
tracing::warn!(
2483+
%message,
2484+
"snapshot creation failed; falling back to a fresh isolate"
24802485
);
24812486
None
24822487
}
@@ -2485,9 +2490,7 @@ fn session_thread(
24852490
let mut iso = match snapshot_blob {
24862491
Some(blob) => {
24872492
from_snapshot = true;
2488-
eprintln!(
2489-
"agentos-v8-runtime: restored session isolate from_snapshot=true"
2490-
);
2493+
tracing::debug!("restored session isolate from snapshot");
24912494
let phase_start = Instant::now();
24922495
// rusty_v8 0.130's CreateParams::snapshot_blob
24932496
// takes owned 'static data, so this copy remains
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { expect, test } from "vitest";
2+
import { AgentOs } from "../src/index.js";
3+
4+
// The sidecar's stderr is forwarded to the host's stderr, so anything it prints
5+
// lands in the user's terminal. This reaches into internals only to read the
6+
// copy the client keeps of that stream.
7+
function sidecarStderr(vm: AgentOs): string {
8+
return (
9+
vm as unknown as {
10+
_sidecarClient: {
11+
protocolClient: { sidecarProcess: { stderrText(): string } };
12+
};
13+
}
14+
)._sidecarClient.protocolClient.sidecarProcess.stderrText();
15+
}
16+
17+
test("ordinary JavaScript and shell runs print no runtime diagnostics to the terminal", async () => {
18+
// A dedicated sidecar keeps output from other tests out of the captured stream.
19+
const sidecar = await AgentOs.createSidecar();
20+
const vm = await AgentOs.create({
21+
sidecar: { kind: "explicit", handle: sidecar },
22+
});
23+
try {
24+
// Several runs claim, refill, and evict warm V8 workers.
25+
for (let i = 0; i < 3; i++) {
26+
expect(await vm.javascript.evaluate(`${i} + 1`)).toMatchObject({
27+
outcome: "succeeded",
28+
value: i + 1,
29+
});
30+
}
31+
expect((await vm.exec("echo hi")).stdout).toBe("hi\n");
32+
33+
expect(sidecarStderr(vm)).toBe("");
34+
} finally {
35+
await vm.dispose();
36+
await sidecar.dispose();
37+
}
38+
}, 120_000);

0 commit comments

Comments
 (0)