Tag TUI startup metrics with terminal and multiplexer categories (#43937)

## What changed

Add `terminal_name` and `multiplexer` attributes to `codex.tui.start` alongside `app_server_mode`. Use fixed terminal categories rather than versioned or user-provided identifiers, with `unknown` for unrecognized terminals. Report the multiplexer as `tmux`, `zellij`, or `none`.

## Testing

Extend the CLI worktree test to parse metric payloads and assert the exact startup attributes for an unrecognized terminal without a multiplexer. Keep coverage that no metrics are sent when analytics is disabled.

GitOrigin-RevId: 024c82653098b9bddd6c5e8d7133ff798b58dcd1
This commit is contained in:
Eric Traut
2026-09-08 22:39:16 +00:00
committed by copyberry
parent 6ab3ae5323
commit 1032738aa0
2 changed files with 72 additions and 12 deletions
+42 -11
View File
@@ -214,12 +214,18 @@ trust_level = "trusted"
env.insert("NO_PROXY".into(), "127.0.0.1,localhost".into());
env.insert("no_proxy".into(), "127.0.0.1,localhost".into());
env.insert("TERM".into(), "xterm-256color".into());
env.insert("TERM_PROGRAM".into(), "unrecognized-terminal".into());
env.insert("OTEL_METRIC_EXPORT_INTERVAL".into(), "100".into());
for key in [
"CODEX_EXEC_SERVER_URL",
"CODEX_ACCESS_TOKEN",
"OPENAI_API_KEY",
"CODEX_API_KEY",
"TMUX",
"TMUX_PANE",
"ZELLIJ",
"ZELLIJ_SESSION_NAME",
"ZELLIJ_VERSION",
] {
env.remove(key);
}
@@ -461,17 +467,42 @@ trust_level = "trusted"
.context("requests")?
.into_iter()
.filter(|request| request.url.path() == "/metrics")
.flat_map(|request| request.body)
.collect::<Vec<_>>();
assert!(
if analytics {
String::from_utf8_lossy(&metrics).contains("codex.tui.start")
} else {
metrics.is_empty()
},
"analytics={analytics}, metrics_bytes={}",
metrics.len()
);
.map(|request| serde_json::from_slice::<Value>(&request.body))
.collect::<serde_json::Result<Vec<_>>>()?;
if analytics {
let point = metrics
.iter()
.flat_map(|payload| payload["resourceMetrics"].as_array().into_iter().flatten())
.flat_map(|resource| resource["scopeMetrics"].as_array().into_iter().flatten())
.flat_map(|scope| scope["metrics"].as_array().into_iter().flatten())
.filter(|metric| metric["name"] == "codex.tui.start")
.flat_map(|metric| metric["sum"]["dataPoints"].as_array().into_iter().flatten())
.next()
.context("codex.tui.start data point")?;
let tags = point["attributes"]
.as_array()
.context("codex.tui.start attributes")?
.iter()
.map(|attribute| {
Ok((
attribute["key"].as_str().context("metric tag key")?,
attribute["value"]["stringValue"]
.as_str()
.context("metric tag value")?,
))
})
.collect::<anyhow::Result<HashMap<_, _>>>()?;
assert_eq!(
tags,
HashMap::from([
("app_server_mode", "in_process"),
("terminal_name", "unknown"),
("multiplexer", "none"),
])
);
} else {
assert!(metrics.is_empty(), "analytics disabled");
}
let (body, checkout, metadata) = observed
.with_context(|| {
format!(
+30 -1
View File
@@ -4,6 +4,8 @@
//! configuration and app-server initialization remain responsive to safe local editing.
use super::*;
use codex_terminal_detection::Multiplexer;
use codex_terminal_detection::TerminalName;
pub(super) async fn run_main_inner(
mut cli: Cli,
@@ -456,10 +458,37 @@ pub(super) async fn run_main_inner(
AppServerTarget::LocalDaemon { .. } => "local_daemon",
AppServerTarget::Remote { .. } => "remote",
};
// Use a fixed category, not the versioned or user-provided terminal identifier.
let terminal_info = codex_terminal_detection::terminal_info();
let terminal_name = match terminal_info.name {
TerminalName::AppleTerminal => "apple_terminal",
TerminalName::Ghostty => "ghostty",
TerminalName::Iterm2 => "iterm2",
TerminalName::WarpTerminal => "warp",
TerminalName::VsCode => "vscode",
TerminalName::WezTerm => "wezterm",
TerminalName::Kitty => "kitty",
TerminalName::Alacritty => "alacritty",
TerminalName::Konsole => "konsole",
TerminalName::GnomeTerminal => "gnome_terminal",
TerminalName::Vte => "vte",
TerminalName::WindowsTerminal => "windows_terminal",
TerminalName::Dumb => "dumb",
TerminalName::Unknown => "unknown",
};
let multiplexer = match terminal_info.multiplexer {
Some(Multiplexer::Tmux { .. }) => "tmux",
Some(Multiplexer::Zellij { .. }) => "zellij",
None => "none",
};
let _ = metrics.counter(
"codex.tui.start",
/*inc*/ 1,
&[("app_server_mode", app_server_mode)],
&[
("app_server_mode", app_server_mode),
("terminal_name", terminal_name),
("multiplexer", multiplexer),
],
);
let telemetry =
codex_rollout::sqlite_telemetry_recorder(metrics.clone(), otel_originator.as_str());