mirror of
https://github.com/openai/codex.git
synced 2026-05-03 04:42:20 +03:00
Enable analytics in codex exec and codex mcp-server (#13083)
Addresses #12913 `codex exec` was not correctly defaulting to Otel metrics to enabled `codex mcp-server` completely lacked an Otel collector Summary: - default to enabling analytics when `codex exec` initializes OpenTelemetry so the CLI actually reports metrics again - add a regression test that proves the flag remains enabled by default - added Otel collector to `codex mcp-server`
This commit is contained in:
@@ -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<ClientRequest, Value, ClientNotification>;
|
||||
|
||||
@@ -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::<IncomingMessage>(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(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user