Files
codex/codex-rs/otel/src/config.rs
T
bbrown-oai 31b233c7c6 codex-otel: add configurable trace metadata (#21556)
Add Codex config for static trace span attributes and structured W3C
tracestate field upserts. The config flows through OtelSettings so
callers can attach trace metadata without touching every span call site.

Apply span attributes with an SDK span processor so every exported
trace span carries the configured metadata. Model tracestate as nested
member fields so configured keys can be upserted while unrelated
propagated state in the same member is preserved.

Validate configured tracestate before installing provider-global state,
including header-unsafe values the SDK does not reject by itself. This
keeps Codex from propagating malformed trace context from config.

Update the config schema, public docs, and OTLP loopback coverage for
config parsing, span export, propagation, and invalid-header rejection.
2026-05-07 16:06:57 -07:00

120 lines
3.6 KiB
Rust

use std::collections::BTreeMap;
use std::collections::HashMap;
use std::path::PathBuf;
use codex_utils_absolute_path::AbsolutePathBuf;
use serde::Deserialize;
use serde::Serialize;
pub(crate) const STATSIG_OTLP_HTTP_ENDPOINT: &str = "https://ab.chatgpt.com/otlp/v1/metrics";
pub(crate) const STATSIG_API_KEY_HEADER: &str = "statsig-api-key";
pub(crate) const STATSIG_API_KEY: &str = "client-MkRuleRQBd6qakfnDYqJVR9JuXcY57Ljly3vi5JVUIO";
pub(crate) fn resolve_exporter(exporter: &OtelExporter) -> OtelExporter {
match exporter {
OtelExporter::Statsig => {
// Keep the built-in Statsig default off in debug builds so
// incremental local development and test runs do not emit
// best-effort OTEL traffic unless a test or binary opts into an
// explicit exporter configuration.
if cfg!(debug_assertions) {
return OtelExporter::None;
}
OtelExporter::OtlpHttp {
endpoint: STATSIG_OTLP_HTTP_ENDPOINT.to_string(),
headers: HashMap::from([(
STATSIG_API_KEY_HEADER.to_string(),
STATSIG_API_KEY.to_string(),
)]),
protocol: OtelHttpProtocol::Json,
tls: None,
}
}
_ => exporter.clone(),
}
}
/// Validates configured span attributes before they are attached to exported spans.
pub fn validate_span_attributes(attributes: &BTreeMap<String, String>) -> std::io::Result<()> {
if attributes.keys().any(String::is_empty) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"configured span attribute key must not be empty",
));
}
Ok(())
}
#[derive(Clone, Debug)]
pub struct OtelSettings {
pub environment: String,
pub service_name: String,
pub service_version: String,
pub codex_home: PathBuf,
pub exporter: OtelExporter,
pub trace_exporter: OtelExporter,
pub metrics_exporter: OtelExporter,
pub runtime_metrics: bool,
pub span_attributes: BTreeMap<String, String>,
pub tracestate: BTreeMap<String, BTreeMap<String, String>>,
}
/// Resolved Statsig metrics settings that another process can use to recreate
/// the built-in metrics exporter configuration without receiving generic
/// exporter credentials in-process.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatsigMetricsSettings {
pub environment: String,
}
#[derive(Clone, Debug)]
pub enum OtelHttpProtocol {
/// HTTP protocol with binary protobuf
Binary,
/// HTTP protocol with JSON payload
Json,
}
#[derive(Clone, Debug, Default)]
pub struct OtelTlsConfig {
pub ca_certificate: Option<AbsolutePathBuf>,
pub client_certificate: Option<AbsolutePathBuf>,
pub client_private_key: Option<AbsolutePathBuf>,
}
#[derive(Clone, Debug)]
pub enum OtelExporter {
None,
/// Statsig metrics ingestion exporter using Codex-internal defaults.
///
/// This is intended for metrics only.
Statsig,
OtlpGrpc {
endpoint: String,
headers: HashMap<String, String>,
tls: Option<OtelTlsConfig>,
},
OtlpHttp {
endpoint: String,
headers: HashMap<String, String>,
protocol: OtelHttpProtocol,
tls: Option<OtelTlsConfig>,
},
}
#[cfg(test)]
mod tests {
use super::OtelExporter;
use super::resolve_exporter;
#[test]
fn statsig_default_metrics_exporter_is_disabled_in_debug_builds() {
assert!(matches!(
resolve_exporter(&OtelExporter::Statsig),
OtelExporter::None
));
}
}