Files
codex/codex-rs/tui/src/daemon_telemetry.rs
T
Eric Traut dbaead0106 Classify daemon auto-start failures in TUI telemetry (#59058)
## What changed

Add a `failure_reason` tag to failed TUI auto-start events in `codex.daemon.start`, distinguishing `install`, `spawn`, `early_child_exit`, `readiness_timeout`, and `other` failures. Preserve the original error diagnostics through a classified startup API.

Detect managed child exits during process identity recording and distinguish exits before readiness from readiness timeouts.

## Testing

Extend daemon tests to assert classification of installation failures, spawn failures, early child exits, and readiness timeouts.

GitOrigin-RevId: 7c626f278e9e7f2ecd61b3fd1a90829a113eb232
2026-11-03 04:20:50 +00:00

74 lines
2.5 KiB
Rust

//! Records automatic daemon startup and ensures launch observations survive setup failures.
use super::*;
use crate::terminal_size_telemetry::LaunchTags;
/// Record the connection mode or update handoff once. Other early exits are unconfirmed.
pub(super) struct Launch<F: FnOnce(&'static str) -> LaunchTags>(pub(super) Option<F>);
impl<F: FnOnce(&'static str) -> LaunchTags> Launch<F> {
pub(super) fn record(mut self, app_server_mode: &'static str) -> LaunchTags {
self.0
.take()
.map(|record| record(app_server_mode))
.unwrap_or_default()
}
}
impl<F: FnOnce(&'static str) -> LaunchTags> Drop for Launch<F> {
fn drop(&mut self) {
if let Some(record) = self.0.take() {
record("unconfirmed");
}
}
}
pub(super) async fn record_start(
config: &Config,
result: &Result<
codex_app_server_daemon::LifecycleOutput,
codex_app_server_daemon::telemetry::StartError,
>,
) {
use codex_app_server_daemon::LifecycleStatus;
let outcome = match result {
Ok(output) => match output.status {
LifecycleStatus::Started => "started",
LifecycleStatus::AlreadyRunning => "already_running",
LifecycleStatus::Restarted => "restarted",
LifecycleStatus::Stopped | LifecycleStatus::NotRunning | LifecycleStatus::Running => {
"unconfirmed"
}
},
Err(_) => "failed",
};
// Failure can happen before normal TUI telemetry is initialized. This short-lived
// provider uses exactly the same consent and identity construction as TUI startup.
let Ok(Ok(Some(otel))) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
codex_app_server_client::build_otel_provider(
config,
env!("CARGO_PKG_VERSION"),
/*service_name_override*/ None,
/*default_analytics_enabled*/ true,
)
})) else {
return;
};
if let Some(metrics) = otel.metrics() {
let mut tags = codex_app_server_daemon::telemetry::settings_tags(&config.codex_home)
.await
.to_vec();
tags.extend([
("initiation_source", "tui_auto_start"),
("outcome", outcome),
]);
if let Err(error) = result {
tags.push(("failure_reason", error.failure_reason()));
}
let _ = metrics.counter("codex.daemon.start", /*inc*/ 1, &tags);
}
let _ = otel
.shutdown_with_timeout(INTERACTIVE_OTEL_SHUTDOWN_TIMEOUT)
.await;
}