mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
Configure telemetry via congif.toml
This commit is contained in:
committed by
Anton Panasenko
parent
ad78dce1d9
commit
148dda758f
3
codex-rs/Cargo.lock
generated
3
codex-rs/Cargo.lock
generated
@@ -743,6 +743,7 @@ dependencies = [
|
||||
"maplit",
|
||||
"mcp-types",
|
||||
"openssl-sys",
|
||||
"opentelemetry_sdk",
|
||||
"os_info",
|
||||
"portable-pty",
|
||||
"predicates",
|
||||
@@ -3247,6 +3248,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"opentelemetry",
|
||||
"reqwest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3263,6 +3265,7 @@ dependencies = [
|
||||
"opentelemetry-proto",
|
||||
"opentelemetry_sdk",
|
||||
"prost",
|
||||
"reqwest",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"tonic",
|
||||
|
||||
@@ -32,7 +32,8 @@ tracing-subscriber = { version = "0.3", features = ["registry", "fmt"], optional
|
||||
tracing-opentelemetry = { version = "0.27", optional = true }
|
||||
opentelemetry = { version = "0.26", features = ["trace"], optional = true }
|
||||
opentelemetry_sdk = { version = "0.26", features = ["trace", "rt-tokio"], optional = true }
|
||||
opentelemetry-otlp = { version = "0.26", features = ["tonic", "http-proto"], optional = true }
|
||||
# Enable HTTP exporter protocol and the reqwest HTTP client backend
|
||||
opentelemetry-otlp = { version = "0.26", features = ["tonic", "http-proto", "reqwest-client"], optional = true }
|
||||
opentelemetry-proto = { version = "0.26", features = ["gen-tonic"], optional = true }
|
||||
prost = { version = "0.13", optional = true }
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"], optional = true }
|
||||
|
||||
@@ -64,6 +64,7 @@ uuid = { version = "1", features = ["serde", "v4"] }
|
||||
which = "6"
|
||||
wildmatch = "2.5.0"
|
||||
codex-telemetry = { path = "../codex-telemetry", features = ["otel"] }
|
||||
opentelemetry_sdk = { version = "0.26", features = ["trace"] }
|
||||
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
|
||||
@@ -159,8 +159,8 @@ pub struct Config {
|
||||
/// Responses API.
|
||||
pub model_reasoning_effort: Option<ReasoningEffort>,
|
||||
|
||||
/// If not "none", the value to use for `reasoning.summary` when making a
|
||||
/// request using the Responses API.
|
||||
/// If true, a short textual description of the agent's internal reasoning
|
||||
/// will be included in model responses.
|
||||
pub model_reasoning_summary: ReasoningSummary,
|
||||
|
||||
/// Optional verbosity control for GPT-5 models (Responses API `text.verbosity`).
|
||||
@@ -194,6 +194,9 @@ pub struct Config {
|
||||
/// All characters are inserted as they are received, and no buffering
|
||||
/// or placeholder replacement will occur for fast keypress bursts.
|
||||
pub disable_paste_burst: bool,
|
||||
|
||||
/// Telemetry configuration (exporter type, endpoint, headers, etc.).
|
||||
pub telemetry: crate::config_types::TelemetryConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -705,6 +708,9 @@ pub struct ConfigToml {
|
||||
/// All characters are inserted as they are received, and no buffering
|
||||
/// or placeholder replacement will occur for fast keypress bursts.
|
||||
pub disable_paste_burst: Option<bool>,
|
||||
|
||||
/// Telemetry configuration.
|
||||
pub telemetry: Option<crate::config_types::TelemetryConfigToml>,
|
||||
}
|
||||
|
||||
impl From<ConfigToml> for UserSavedConfig {
|
||||
@@ -1053,6 +1059,24 @@ impl Config {
|
||||
.as_ref()
|
||||
.map(|t| t.notifications.clone())
|
||||
.unwrap_or_default(),
|
||||
telemetry: {
|
||||
use crate::config_types::TelemetryConfig;
|
||||
use crate::config_types::TelemetryConfigToml;
|
||||
use crate::config_types::TelemetryExporterKind;
|
||||
let t: TelemetryConfigToml = cfg.telemetry.unwrap_or_default();
|
||||
let enabled = t.enabled.unwrap_or(true);
|
||||
let exporter = t.exporter.unwrap_or(TelemetryExporterKind::OtlpFile);
|
||||
let endpoint = t.endpoint;
|
||||
let headers = t.headers.unwrap_or_default();
|
||||
let rotate_mb = t.rotate_mb.or(Some(100));
|
||||
TelemetryConfig {
|
||||
enabled,
|
||||
exporter,
|
||||
endpoint,
|
||||
headers,
|
||||
rotate_mb,
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(config)
|
||||
}
|
||||
@@ -1631,6 +1655,13 @@ model_verbosity = "high"
|
||||
active_profile: Some("o3".to_string()),
|
||||
disable_paste_burst: false,
|
||||
tui_notifications: Default::default(),
|
||||
telemetry: crate::config_types::TelemetryConfig {
|
||||
enabled: true,
|
||||
exporter: crate::config_types::TelemetryExporterKind::OtlpFile,
|
||||
endpoint: None,
|
||||
headers: HashMap::new(),
|
||||
rotate_mb: Some(100),
|
||||
},
|
||||
},
|
||||
o3_profile_config
|
||||
);
|
||||
@@ -1689,6 +1720,13 @@ model_verbosity = "high"
|
||||
active_profile: Some("gpt3".to_string()),
|
||||
disable_paste_burst: false,
|
||||
tui_notifications: Default::default(),
|
||||
telemetry: crate::config_types::TelemetryConfig {
|
||||
enabled: true,
|
||||
exporter: crate::config_types::TelemetryExporterKind::OtlpFile,
|
||||
endpoint: None,
|
||||
headers: HashMap::new(),
|
||||
rotate_mb: Some(100),
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(expected_gpt3_profile_config, gpt3_profile_config);
|
||||
@@ -1762,6 +1800,13 @@ model_verbosity = "high"
|
||||
active_profile: Some("zdr".to_string()),
|
||||
disable_paste_burst: false,
|
||||
tui_notifications: Default::default(),
|
||||
telemetry: crate::config_types::TelemetryConfig {
|
||||
enabled: true,
|
||||
exporter: crate::config_types::TelemetryExporterKind::OtlpFile,
|
||||
endpoint: None,
|
||||
headers: HashMap::new(),
|
||||
rotate_mb: Some(100),
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(expected_zdr_profile_config, zdr_profile_config);
|
||||
@@ -1821,6 +1866,13 @@ model_verbosity = "high"
|
||||
active_profile: Some("gpt5".to_string()),
|
||||
disable_paste_burst: false,
|
||||
tui_notifications: Default::default(),
|
||||
telemetry: crate::config_types::TelemetryConfig {
|
||||
enabled: true,
|
||||
exporter: crate::config_types::TelemetryExporterKind::OtlpFile,
|
||||
endpoint: None,
|
||||
headers: HashMap::new(),
|
||||
rotate_mb: Some(100),
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(expected_gpt5_profile_config, gpt5_profile_config);
|
||||
|
||||
@@ -76,6 +76,48 @@ pub enum HistoryPersistence {
|
||||
None,
|
||||
}
|
||||
|
||||
// ===== Telemetry configuration =====
|
||||
|
||||
/// Which telemetry exporter to use.
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum TelemetryExporterKind {
|
||||
None,
|
||||
OtlpFile,
|
||||
OtlpHttp,
|
||||
OtlpGrpc,
|
||||
}
|
||||
|
||||
/// Telemetry settings loaded from config.toml. Fields are optional so we can apply defaults.
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Default)]
|
||||
pub struct TelemetryConfigToml {
|
||||
/// Enable or disable telemetry entirely. Defaults to true.
|
||||
pub enabled: Option<bool>,
|
||||
|
||||
/// Exporter to use. Defaults to `otlp-file`.
|
||||
pub exporter: Option<TelemetryExporterKind>,
|
||||
|
||||
/// Endpoint for HTTP/GRPC exporters. Example: "http://localhost:4318".
|
||||
pub endpoint: Option<String>,
|
||||
|
||||
/// Optional headers for HTTP/GRPC exporters.
|
||||
#[serde(default)]
|
||||
pub headers: Option<HashMap<String, String>>,
|
||||
|
||||
/// Rotation size (MiB) for file exporter.
|
||||
pub rotate_mb: Option<u64>,
|
||||
}
|
||||
|
||||
/// Effective telemetry settings after defaults are applied.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TelemetryConfig {
|
||||
pub enabled: bool,
|
||||
pub exporter: TelemetryExporterKind,
|
||||
pub endpoint: Option<String>,
|
||||
pub headers: HashMap<String, String>,
|
||||
pub rotate_mb: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Notifications {
|
||||
|
||||
@@ -100,3 +100,5 @@ pub use codex_protocol::models::LocalShellExecAction;
|
||||
pub use codex_protocol::models::LocalShellStatus;
|
||||
pub use codex_protocol::models::ReasoningItemContent;
|
||||
pub use codex_protocol::models::ResponseItem;
|
||||
|
||||
pub mod telemetry_init;
|
||||
|
||||
58
codex-rs/core/src/telemetry_init.rs
Normal file
58
codex-rs/core/src/telemetry_init.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use opentelemetry_sdk::trace::Tracer;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::config_types::TelemetryExporterKind as Kind;
|
||||
use codex_telemetry as telemetry;
|
||||
|
||||
/// Build an OpenTelemetry tracer and guard from the app Config.
|
||||
///
|
||||
/// Returns `None` when telemetry is disabled.
|
||||
pub fn build_otel_layer_from_config(
|
||||
config: &Config,
|
||||
service_name: &str,
|
||||
service_version: &str,
|
||||
) -> Option<(telemetry::Guard, Tracer)> {
|
||||
let exporter = match config.telemetry.exporter {
|
||||
Kind::None => telemetry::Exporter::None,
|
||||
Kind::OtlpFile => telemetry::Exporter::OtlpFile {
|
||||
path: PathBuf::new(),
|
||||
rotate_mb: config.telemetry.rotate_mb,
|
||||
},
|
||||
Kind::OtlpHttp => telemetry::Exporter::OtlpHttp {
|
||||
endpoint: config
|
||||
.telemetry
|
||||
.endpoint
|
||||
.clone()
|
||||
.unwrap_or_else(|| "http://localhost:4318".to_string()),
|
||||
headers: config
|
||||
.telemetry
|
||||
.headers
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
},
|
||||
Kind::OtlpGrpc => telemetry::Exporter::OtlpGrpc {
|
||||
endpoint: config
|
||||
.telemetry
|
||||
.endpoint
|
||||
.clone()
|
||||
.unwrap_or_else(|| "http://localhost:4317".to_string()),
|
||||
headers: config
|
||||
.telemetry
|
||||
.headers
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
},
|
||||
};
|
||||
|
||||
telemetry::build_layer(&telemetry::Settings {
|
||||
enabled: config.telemetry.enabled,
|
||||
exporter,
|
||||
service_name: service_name.to_string(),
|
||||
service_version: service_version.to_string(),
|
||||
codex_home: Some(config.codex_home.clone()),
|
||||
})
|
||||
}
|
||||
@@ -169,16 +169,11 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
let config = Config::load_with_cli_overrides(cli_kv_overrides, overrides)?;
|
||||
|
||||
// Build OTEL layer and compose into subscriber.
|
||||
let telemetry = telemetry::build_layer(&telemetry::Settings {
|
||||
enabled: true,
|
||||
exporter: telemetry::Exporter::OtlpFile {
|
||||
path: PathBuf::new(),
|
||||
rotate_mb: Some(100),
|
||||
},
|
||||
service_name: "codex".to_string(),
|
||||
service_version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
codex_home: Some(config.codex_home.clone()),
|
||||
});
|
||||
let telemetry = codex_core::telemetry_init::build_otel_layer_from_config(
|
||||
&config,
|
||||
"codex",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
);
|
||||
let _telemetry_guard = if let Some((guard, tracer)) = telemetry {
|
||||
let otel_layer = tracing_opentelemetry::OpenTelemetryLayer::new(tracer);
|
||||
// Build env_filter separately and attach via with_filter.
|
||||
|
||||
@@ -165,6 +165,13 @@ pub async fn run_main(
|
||||
}
|
||||
};
|
||||
|
||||
// Build OTEL layer and compose into subscriber.
|
||||
let telemetry = codex_core::telemetry_init::build_otel_layer_from_config(
|
||||
&config,
|
||||
"codex",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
);
|
||||
|
||||
// we load config.toml here to determine project state.
|
||||
#[allow(clippy::print_stderr)]
|
||||
let config_toml = {
|
||||
|
||||
Reference in New Issue
Block a user