mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Cache OS discovery for user agents and telemetry (#47038)
## Why OS discovery can launch subprocesses on Linux. Repeating it for requests, analytics events, and metrics provider creation adds unnecessary work for stable metadata. ## What changed Cache `os_info::get()` with a separate `LazyLock` in user-agent generation, analytics runtime metadata, and metrics OS resource attributes. Continue reading the user-agent originator and suffix on each call so updates remain effective. ## Testing Add an isolated Linux regression test that races initial user-agent lookups and repeats calls, verifies `lsb_release` and `getconf` each run once, and checks that subsequent originator and suffix updates appear in the user agent. GitOrigin-RevId: 337e45ff88b863d2a4aa387a8e7d2e291c045906
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use std::sync::LazyLock;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::facts::AppInvocation;
|
||||
@@ -1496,7 +1497,9 @@ fn analytics_hook_source(source: HookSource) -> &'static str {
|
||||
}
|
||||
|
||||
pub(crate) fn current_runtime_metadata() -> CodexRuntimeMetadata {
|
||||
let os_info = os_info::get();
|
||||
// Runtime metadata is stable; avoid launching OS discovery subprocesses per event.
|
||||
static OS_INFO: LazyLock<os_info::Info> = LazyLock::new(os_info::get);
|
||||
let os_info = &*OS_INFO;
|
||||
CodexRuntimeMetadata {
|
||||
codex_rs_version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
runtime_os: std::env::consts::OS.to_string(),
|
||||
|
||||
@@ -148,8 +148,11 @@ pub fn is_first_party_chat_originator(originator_value: &str) -> bool {
|
||||
}
|
||||
|
||||
pub fn get_codex_user_agent() -> String {
|
||||
// OS discovery can spawn subprocesses on Linux. Reuse it across requests,
|
||||
// while continuing to read the mutable originator and suffix below.
|
||||
static OS_INFO: LazyLock<os_info::Info> = LazyLock::new(os_info::get);
|
||||
let build_version = env!("CARGO_PKG_VERSION");
|
||||
let os_info = os_info::get();
|
||||
let os_info = &*OS_INFO;
|
||||
let originator = originator();
|
||||
let prefix = format!(
|
||||
"{}/{build_version} ({} {}; {}) {}",
|
||||
|
||||
@@ -46,6 +46,71 @@ fn test_get_codex_user_agent() {
|
||||
assert!(user_agent.starts_with(&prefix));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn os_discovery_is_cached_without_freezing_user_agent_overrides() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
const PROBE_DIR: &str = "CODEX_TEST_USER_AGENT_PROBE_DIR";
|
||||
if std::env::var_os(PROBE_DIR).is_some() {
|
||||
// Race the first lookup as well as exercising repeated requests.
|
||||
std::thread::scope(|scope| {
|
||||
for _ in 0..8 {
|
||||
scope.spawn(|| {
|
||||
for _ in 0..4 {
|
||||
assert!(get_codex_user_agent().contains("Ubuntu 99.7.3"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
set_default_originator("cached-os-test".to_string()).expect("set originator");
|
||||
*USER_AGENT_SUFFIX.lock().expect("suffix lock") = Some("updated-client".to_string());
|
||||
let user_agent = get_codex_user_agent();
|
||||
assert!(user_agent.starts_with("cached-os-test/"));
|
||||
assert!(user_agent.ends_with(" (updated-client)"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Use a fresh process so neither the cached OS nor the mutable overrides can
|
||||
// be initialized by another test. Only the child's environment is changed.
|
||||
let temp = tempfile::tempdir().expect("probe directory");
|
||||
for (program, output) in [
|
||||
("lsb_release", "Distributor ID: Ubuntu\nRelease: 99.7.3\n"),
|
||||
("getconf", "64\n"),
|
||||
] {
|
||||
let script = temp.path().join(program);
|
||||
std::fs::write(
|
||||
&script,
|
||||
format!(
|
||||
"#!/bin/sh\nprintf '{program}\\n' >> \"${PROBE_DIR}/calls\"\nprintf '{output}'\n"
|
||||
),
|
||||
)
|
||||
.expect("write probe");
|
||||
std::fs::set_permissions(&script, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("make probe executable");
|
||||
}
|
||||
let output = std::process::Command::new(std::env::current_exe().expect("test executable"))
|
||||
.args([
|
||||
"--exact",
|
||||
concat!(
|
||||
module_path!(),
|
||||
"::os_discovery_is_cached_without_freezing_user_agent_overrides"
|
||||
)
|
||||
.trim_start_matches("codex_login::"),
|
||||
"--nocapture",
|
||||
])
|
||||
.env(PROBE_DIR, temp.path())
|
||||
.env("PATH", temp.path())
|
||||
.env_remove(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR)
|
||||
.output()
|
||||
.expect("run isolated user-agent test");
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
let calls = std::fs::read_to_string(temp.path().join("calls")).expect("probe calls");
|
||||
let mut calls = calls.lines().collect::<Vec<_>>();
|
||||
calls.sort_unstable();
|
||||
assert_eq!(calls, vec!["getconf", "lsb_release"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_first_party_originator_matches_known_values() {
|
||||
assert_eq!(is_first_party_originator(DEFAULT_ORIGINATOR), true);
|
||||
|
||||
@@ -36,6 +36,7 @@ use opentelemetry_semantic_conventions as semconv;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::Weak;
|
||||
@@ -537,7 +538,9 @@ impl MetricsClient {
|
||||
}
|
||||
|
||||
fn os_resource_attributes() -> Vec<KeyValue> {
|
||||
let os_info = os_info::get();
|
||||
// Provider creation must not repeat OS discovery subprocesses.
|
||||
static OS_INFO: LazyLock<os_info::Info> = LazyLock::new(os_info::get);
|
||||
let os_info = &*OS_INFO;
|
||||
let os_type_raw = os_info.os_type().to_string();
|
||||
let os_type = sanitize_metric_tag_value(os_type_raw.as_str());
|
||||
let os_version_raw = os_info.version().to_string();
|
||||
|
||||
Reference in New Issue
Block a user