diff --git a/codex-rs/exec-server/src/client.rs b/codex-rs/exec-server/src/client.rs index 2e2f4ce450e1..e9ab5c719087 100644 --- a/codex-rs/exec-server/src/client.rs +++ b/codex-rs/exec-server/src/client.rs @@ -1310,6 +1310,7 @@ mod tests { } #[tokio::test(flavor = "current_thread")] + #[serial_test::serial(exec_server_tracing)] async fn process_start_propagates_caller_trace_context_across_background_task() { let (client_stdin, server_reader) = duplex(1 << 20); let (mut server_writer, client_stdout) = duplex(1 << 20); @@ -1360,6 +1361,15 @@ mod tests { trace }); + let tracer_provider = SdkTracerProvider::builder().build(); + let tracer = tracer_provider.tracer("exec-server-test"); + let subscriber = tracing_subscriber::registry().with( + tracing_opentelemetry::layer() + .with_tracer(tracer) + .with_filter(filter_fn(codex_otel::OtelProvider::trace_export_filter)), + ); + let _subscriber_guard = tracing::subscriber::set_default(subscriber); + let client = ExecServerClient::connect( JsonRpcConnection::from_stdio( client_stdout, @@ -1371,14 +1381,6 @@ mod tests { .await .expect("client should connect"); - let tracer_provider = SdkTracerProvider::builder().build(); - let tracer = tracer_provider.tracer("exec-server-test"); - let subscriber = tracing_subscriber::registry().with( - tracing_opentelemetry::layer() - .with_tracer(tracer) - .with_filter(filter_fn(codex_otel::OtelProvider::trace_export_filter)), - ); - let _subscriber_guard = tracing::subscriber::set_default(subscriber); tracing::callsite::rebuild_interest_cache(); let parent_span = tracing::info_span!("process-start-parent"); let expected_trace = codex_otel::span_w3c_trace_context(&parent_span) diff --git a/codex-rs/exec-server/src/remote.rs b/codex-rs/exec-server/src/remote.rs index 8870e0efe98c..ef200885a76b 100644 --- a/codex-rs/exec-server/src/remote.rs +++ b/codex-rs/exec-server/src/remote.rs @@ -711,6 +711,7 @@ mod tests { } #[tokio::test(flavor = "current_thread")] + #[serial_test::serial(exec_server_tracing)] async fn register_environment_posts_with_auth_provider_headers() { let provider = SdkTracerProvider::builder().build(); let tracer = provider.tracer("exec-server-test"); diff --git a/codex-rs/exec-server/src/rpc.rs b/codex-rs/exec-server/src/rpc.rs index 9671f25d3933..bde4e48f00d0 100644 --- a/codex-rs/exec-server/src/rpc.rs +++ b/codex-rs/exec-server/src/rpc.rs @@ -363,6 +363,10 @@ impl RpcClient { }) } + fn allocate_request_id(&self) -> RequestId { + RequestId::Integer(self.next_request_id.fetch_add(1, Ordering::SeqCst)) + } + #[tracing::instrument( name = "codex.exec_server.request", level = "info", @@ -370,6 +374,9 @@ impl RpcClient { fields( otel.kind = "client", otel.name = method, + rpc.system = "jsonrpc", + rpc.method = method, + rpc.request_id = tracing::field::Empty, method, ) )] @@ -379,9 +386,25 @@ impl RpcClient { T: DeserializeOwned, { let _call_slot = self.acquire_regular_call_slot()?; - self.call_inner(method, params, RpcCallTimeout::None).await + let request_id = self.allocate_request_id(); + tracing::Span::current().record("rpc.request_id", tracing::field::display(&request_id)); + self.call_inner(request_id, method, params, RpcCallTimeout::None) + .await } + #[tracing::instrument( + name = "codex.exec_server.request", + level = "info", + skip_all, + fields( + otel.kind = "client", + otel.name = method, + rpc.system = "jsonrpc", + rpc.method = method, + rpc.request_id = tracing::field::Empty, + method, + ) + )] pub(crate) async fn call_with_timeout
( &self, method: &str, @@ -393,8 +416,15 @@ impl RpcClient { T: DeserializeOwned, { let _call_slot = self.acquire_regular_call_slot()?; - self.call_inner(method, params, RpcCallTimeout::After(call_timeout)) - .await + let request_id = self.allocate_request_id(); + tracing::Span::current().record("rpc.request_id", tracing::field::display(&request_id)); + self.call_inner( + request_id, + method, + params, + RpcCallTimeout::After(call_timeout), + ) + .await } #[tracing::instrument( @@ -404,6 +434,9 @@ impl RpcClient { fields( otel.kind = "client", otel.name = method, + rpc.system = "jsonrpc", + rpc.method = method, + rpc.request_id = tracing::field::Empty, method, ) )] @@ -426,11 +459,15 @@ impl RpcClient { } }, }; - self.call_inner(method, params, RpcCallTimeout::None).await + let request_id = self.allocate_request_id(); + tracing::Span::current().record("rpc.request_id", tracing::field::display(&request_id)); + self.call_inner(request_id, method, params, RpcCallTimeout::None) + .await } async fn call_inner
(
&self,
+ request_id: RequestId,
method: &str,
params: &P,
call_timeout: RpcCallTimeout,
@@ -439,7 +476,6 @@ impl RpcClient {
P: Serialize,
T: DeserializeOwned,
{
- let request_id = RequestId::Integer(self.next_request_id.fetch_add(1, Ordering::SeqCst));
let (response_tx, response_rx) = oneshot::channel();
{
let mut pending = self.pending.lock().await;
@@ -967,6 +1003,99 @@ mod tests {
}
#[tokio::test(flavor = "current_thread")]
+ #[serial_test::serial(exec_server_tracing)]
+ async fn rpc_client_emits_spans_for_admission_failures() {
+ let span_exporter = InMemorySpanExporter::default();
+ let tracer_provider = SdkTracerProvider::builder()
+ .with_simple_exporter(span_exporter.clone())
+ .build();
+ let tracer = tracer_provider.tracer("exec-server-test");
+ let subscriber = tracing_subscriber::registry().with(
+ tracing_opentelemetry::layer()
+ .with_tracer(tracer)
+ .with_filter(filter_fn(codex_otel::OtelProvider::trace_export_filter)),
+ );
+ let _subscriber_guard = tracing::subscriber::set_default(subscriber);
+ tracing::callsite::rebuild_interest_cache();
+
+ let (outgoing_tx, _outgoing_rx) = tokio::sync::mpsc::channel(/*buffer*/ 1);
+ let (_incoming_tx, incoming_rx) = tokio::sync::mpsc::channel(/*buffer*/ 1);
+ let (_disconnected_tx, disconnected_rx) = tokio::sync::watch::channel(/*init*/ false);
+ let connection = JsonRpcConnection {
+ outgoing_tx,
+ incoming_rx,
+ disconnected_rx,
+ task_handles: Vec::new(),
+ transport: JsonRpcTransport::Plain,
+ };
+ let (client, _events_rx) = RpcClient::new(connection);
+ let _regular_call_slots = (0..MAX_IN_FLIGHT_REGULAR_CALLS)
+ .map(|_| {
+ client
+ .shared_call_slots
+ .try_acquire()
+ .expect("regular call slot")
+ })
+ .collect::