From 0942db59fd287349aa693f98466dc521c0523612 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 27 Feb 2026 17:49:08 -0700 Subject: [PATCH 1/2] Verify codex exec metrics --- codex-rs/exec/src/lib.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index e8ab6f8af74..c0a559e067f 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -74,6 +74,8 @@ use codex_core::default_client::set_default_originator; use codex_core::find_thread_path_by_id_str; use codex_core::find_thread_path_by_name_str; +const DEFAULT_ANALYTICS_ENABLED: bool = true; + enum InitialOperation { UserTurn { items: Vec, @@ -316,7 +318,12 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result } let otel = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), None, false) + codex_core::otel_init::build_provider( + &config, + env!("CARGO_PKG_VERSION"), + None, + DEFAULT_ANALYTICS_ENABLED, + ) })) { Ok(Ok(otel)) => otel, Ok(Err(e)) => { @@ -927,6 +934,11 @@ mod tests { use super::*; use pretty_assertions::assert_eq; + #[test] + fn exec_defaults_analytics_to_enabled() { + assert_eq!(DEFAULT_ANALYTICS_ENABLED, true); + } + #[test] fn builds_uncommitted_review_request() { let request = build_review_request(ReviewArgs { From 525f22f460a99797cc4c47aec5d4ffdff98427ed Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 27 Feb 2026 17:54:53 -0700 Subject: [PATCH 2/2] Confirm codex exec telemetry --- codex-rs/mcp-server/src/lib.rs | 111 +++++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 19 deletions(-) diff --git a/codex-rs/mcp-server/src/lib.rs b/codex-rs/mcp-server/src/lib.rs index 554daf9a8cf..6e4be0a91ce 100644 --- a/codex-rs/mcp-server/src/lib.rs +++ b/codex-rs/mcp-server/src/lib.rs @@ -21,6 +21,7 @@ use tracing::debug; use tracing::error; use tracing::info; use tracing_subscriber::EnvFilter; +use tracing_subscriber::prelude::*; mod codex_tool_config; mod codex_tool_runner; @@ -45,6 +46,8 @@ pub use crate::patch_approval::PatchApprovalResponse; /// is a balance between throughput and memory usage – 128 messages should be /// plenty for an interactive CLI. const CHANNEL_CAPACITY: usize = 128; +const DEFAULT_ANALYTICS_ENABLED: bool = true; +const OTEL_SERVICE_NAME: &str = "codex_mcp_server"; type IncomingMessage = JsonRpcMessage; @@ -52,12 +55,44 @@ pub async fn run_main( arg0_paths: Arg0DispatchPaths, cli_config_overrides: CliConfigOverrides, ) -> IoResult<()> { - // Install a simple subscriber so `tracing` output is visible. Users can - // control the log level with `RUST_LOG`. - tracing_subscriber::fmt() + // Parse CLI overrides once and derive the base Config eagerly so later + // components do not need to work with raw TOML values. + let cli_kv_overrides = cli_config_overrides.parse_overrides().map_err(|e| { + std::io::Error::new( + ErrorKind::InvalidInput, + format!("error parsing -c overrides: {e}"), + ) + })?; + let config = Config::load_with_cli_overrides(cli_kv_overrides) + .await + .map_err(|e| { + std::io::Error::new(ErrorKind::InvalidData, format!("error loading config: {e}")) + })?; + + let otel = codex_core::otel_init::build_provider( + &config, + env!("CARGO_PKG_VERSION"), + Some(OTEL_SERVICE_NAME), + DEFAULT_ANALYTICS_ENABLED, + ) + .map_err(|e| { + std::io::Error::new( + ErrorKind::InvalidData, + format!("error loading otel config: {e}"), + ) + })?; + + let fmt_layer = tracing_subscriber::fmt::layer() .with_writer(std::io::stderr) - .with_env_filter(EnvFilter::from_default_env()) - .init(); + .with_filter(EnvFilter::from_default_env()); + let otel_logger_layer = otel.as_ref().and_then(|provider| provider.logger_layer()); + let otel_tracing_layer = otel.as_ref().and_then(|provider| provider.tracing_layer()); + + let _ = tracing_subscriber::registry() + .with(fmt_layer) + .with(otel_logger_layer) + .with(otel_tracing_layer) + .try_init(); // Set up channels. let (incoming_tx, mut incoming_rx) = mpsc::channel::(CHANNEL_CAPACITY); @@ -86,20 +121,6 @@ pub async fn run_main( } }); - // Parse CLI overrides once and derive the base Config eagerly so later - // components do not need to work with raw TOML values. - let cli_kv_overrides = cli_config_overrides.parse_overrides().map_err(|e| { - std::io::Error::new( - ErrorKind::InvalidInput, - format!("error parsing -c overrides: {e}"), - ) - })?; - let config = Config::load_with_cli_overrides(cli_kv_overrides) - .await - .map_err(|e| { - std::io::Error::new(ErrorKind::InvalidData, format!("error loading config: {e}")) - })?; - // Task: process incoming messages. let processor_handle = tokio::spawn({ let outgoing_message_sender = OutgoingMessageSender::new(outgoing_tx); @@ -152,3 +173,55 @@ pub async fn run_main( Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use codex_core::config::ConfigBuilder; + use codex_core::config::types::OtelExporterKind; + use pretty_assertions::assert_eq; + use std::collections::HashMap; + use tempfile::TempDir; + + #[test] + fn mcp_server_defaults_analytics_to_enabled() { + assert_eq!(DEFAULT_ANALYTICS_ENABLED, true); + } + + #[tokio::test] + async fn mcp_server_builds_otel_provider_with_logs_traces_and_metrics() -> anyhow::Result<()> { + let codex_home = TempDir::new()?; + let mut config = ConfigBuilder::default() + .codex_home(codex_home.path().to_path_buf()) + .build() + .await?; + let exporter = OtelExporterKind::OtlpGrpc { + endpoint: "http://localhost:4317".to_string(), + headers: HashMap::new(), + tls: None, + }; + config.otel.exporter = exporter.clone(); + config.otel.trace_exporter = exporter.clone(); + config.otel.metrics_exporter = exporter; + config.analytics_enabled = None; + + let provider = codex_core::otel_init::build_provider( + &config, + "0.0.0-test", + Some(OTEL_SERVICE_NAME), + DEFAULT_ANALYTICS_ENABLED, + ) + .map_err(|err| anyhow::anyhow!(err.to_string()))? + .expect("otel provider"); + + assert!(provider.logger.is_some(), "expected log exporter"); + assert!( + provider.tracer_provider.is_some(), + "expected trace exporter" + ); + assert!(provider.metrics().is_some(), "expected metrics exporter"); + provider.shutdown(); + + Ok(()) + } +}