Keep runtime summary metrics out of Statsig exports (#37874)

## What changed

- Exclude conversation turn count, response-engine latency, and turn token usage metrics from the built-in Statsig exporter, alongside the existing API and tool-call exclusions.
- Apply the Statsig exclusion list consistently to counters, histograms, gauges, observable gauges, and duration histograms.
- Preserve all excluded metrics for custom OTLP exporters and extend the loopback coverage to verify they are exported.

GitOrigin-RevId: e49f9aa1433126a3de8dbed76a4d2688f2831af8
This commit is contained in:
pradeepg-oai
2026-08-10 19:53:01 +00:00
committed by copyberry
parent 63002bdb26
commit 46f5d6eba9
4 changed files with 105 additions and 12 deletions
+18 -5
View File
@@ -107,7 +107,7 @@ pub(super) struct MetricsClientInner {
histograms: Mutex<HashMap<String, Histogram<f64>>>,
duration_histograms: Mutex<HashMap<InstrumentKey, Histogram<f64>>>,
runtime_reader: Option<Arc<ManualReader>>,
runtime_only_metrics: &'static [&'static str],
statsig_disabled_metrics: &'static [&'static str],
default_tags: BTreeMap<String, String>,
}
@@ -128,7 +128,7 @@ impl MetricsClientInner {
}
let attributes = self.attributes(tags)?;
if self.runtime_only_metrics.contains(&name) {
if self.statsig_disabled_metrics.contains(&name) {
return Ok(());
}
@@ -156,6 +156,10 @@ impl MetricsClientInner {
validate_metric_name(name)?;
let attributes = self.attributes(tags)?;
if self.statsig_disabled_metrics.contains(&name) {
return Ok(());
}
let mut histograms = self
.histograms
.lock()
@@ -177,6 +181,10 @@ impl MetricsClientInner {
validate_metric_name(name)?;
let attributes = self.attributes(tags)?;
if self.statsig_disabled_metrics.contains(&name) {
return Ok(());
}
let mut gauges = self
.gauges
.lock()
@@ -206,6 +214,11 @@ impl MetricsClientInner {
) -> Result<()> {
validate_metric_name(name)?;
let attributes = self.attributes(tags)?;
if self.statsig_disabled_metrics.contains(&name) {
return Ok(());
}
let _gauge = self
.meter
.i64_observable_gauge(name.to_string())
@@ -227,7 +240,7 @@ impl MetricsClientInner {
validate_metric_name(name)?;
let attributes = self.attributes(tags)?;
if self.runtime_only_metrics.contains(&name) {
if self.statsig_disabled_metrics.contains(&name) {
return Ok(());
}
@@ -305,7 +318,7 @@ impl MetricsClient {
exporter,
export_interval,
runtime_reader,
runtime_only_metrics,
statsig_disabled_metrics,
default_tags,
} = config;
@@ -351,7 +364,7 @@ impl MetricsClient {
histograms: Mutex::new(HashMap::new()),
duration_histograms: Mutex::new(HashMap::new()),
runtime_reader,
runtime_only_metrics,
statsig_disabled_metrics,
default_tags,
}),
active: None,
+19 -6
View File
@@ -2,19 +2,32 @@ use crate::config::OtelExporter;
use crate::metrics::Result;
use crate::metrics::names::API_CALL_COUNT_METRIC;
use crate::metrics::names::API_CALL_DURATION_METRIC;
use crate::metrics::names::RESPONSES_API_ENGINE_IAPI_TTFT_DURATION_METRIC;
use crate::metrics::names::RESPONSES_API_ENGINE_SERVICE_TBT_DURATION_METRIC;
use crate::metrics::names::RESPONSES_API_ENGINE_SERVICE_TTFT_DURATION_METRIC;
use crate::metrics::names::TOOL_CALL_COUNT_METRIC;
use crate::metrics::names::TOOL_CALL_DURATION_METRIC;
use crate::metrics::names::TURN_TOKEN_USAGE_METRIC;
use crate::metrics::validation::validate_tag_key;
use crate::metrics::validation::validate_tag_value;
use opentelemetry_sdk::metrics::InMemoryMetricExporter;
use std::collections::BTreeMap;
use std::time::Duration;
const RUNTIME_ONLY_METRICS: &[&str] = &[
const CONVERSATION_TURN_COUNT_METRIC: &str = "codex.conversation.turn.count";
// Metrics intentionally not sent through Codex's built-in Statsig route.
// Keep this as an exact-name list so custom OTLP exporters still receive them.
const STATSIG_DISABLED_METRICS: &[&str] = &[
API_CALL_COUNT_METRIC,
API_CALL_DURATION_METRIC,
CONVERSATION_TURN_COUNT_METRIC,
RESPONSES_API_ENGINE_IAPI_TTFT_DURATION_METRIC,
RESPONSES_API_ENGINE_SERVICE_TBT_DURATION_METRIC,
RESPONSES_API_ENGINE_SERVICE_TTFT_DURATION_METRIC,
TOOL_CALL_COUNT_METRIC,
TOOL_CALL_DURATION_METRIC,
TURN_TOKEN_USAGE_METRIC,
];
#[derive(Clone, Debug)]
@@ -31,7 +44,7 @@ pub struct MetricsConfig {
pub(crate) exporter: MetricsExporter,
pub(crate) export_interval: Option<Duration>,
pub(crate) runtime_reader: bool,
pub(crate) runtime_only_metrics: &'static [&'static str],
pub(crate) statsig_disabled_metrics: &'static [&'static str],
pub(crate) default_tags: BTreeMap<String, String>,
}
@@ -42,8 +55,8 @@ impl MetricsConfig {
service_version: impl Into<String>,
exporter: OtelExporter,
) -> Self {
let runtime_only_metrics = if matches!(exporter, OtelExporter::Statsig) {
RUNTIME_ONLY_METRICS
let statsig_disabled_metrics = if matches!(exporter, OtelExporter::Statsig) {
STATSIG_DISABLED_METRICS
} else {
&[]
};
@@ -54,7 +67,7 @@ impl MetricsConfig {
exporter: MetricsExporter::Otlp(exporter),
export_interval: None,
runtime_reader: false,
runtime_only_metrics,
statsig_disabled_metrics,
default_tags: BTreeMap::new(),
}
}
@@ -73,7 +86,7 @@ impl MetricsConfig {
exporter: MetricsExporter::InMemory(exporter),
export_interval: None,
runtime_reader: false,
runtime_only_metrics: &[],
statsig_disabled_metrics: &[],
default_tags: BTreeMap::new(),
}
}
+22 -1
View File
@@ -551,8 +551,12 @@ mod tests {
use crate::metrics::API_CALL_COUNT_METRIC;
use crate::metrics::API_CALL_DURATION_METRIC;
use crate::metrics::MetricsExporter;
use crate::metrics::RESPONSES_API_ENGINE_IAPI_TTFT_DURATION_METRIC;
use crate::metrics::RESPONSES_API_ENGINE_SERVICE_TBT_DURATION_METRIC;
use crate::metrics::RESPONSES_API_ENGINE_SERVICE_TTFT_DURATION_METRIC;
use crate::metrics::TOOL_CALL_COUNT_METRIC;
use crate::metrics::TOOL_CALL_DURATION_METRIC;
use crate::metrics::TURN_TOKEN_USAGE_METRIC;
use opentelemetry_sdk::metrics::InMemoryMetricExporter;
use pretty_assertions::assert_eq;
use std::path::PathBuf;
@@ -658,7 +662,7 @@ mod tests {
}
#[test]
fn statsig_runtime_only_metrics_are_not_exported() -> Result<(), Box<dyn Error>> {
fn statsig_disabled_metrics_are_not_exported() -> Result<(), Box<dyn Error>> {
let exporter = InMemoryMetricExporter::default();
let mut config = MetricsConfig::otlp(
"test",
@@ -671,8 +675,25 @@ mod tests {
metrics.counter(API_CALL_COUNT_METRIC, /*inc*/ 1, &[])?;
metrics.record_duration(API_CALL_DURATION_METRIC, Duration::from_millis(100), &[])?;
metrics.counter("codex.conversation.turn.count", /*inc*/ 1, &[])?;
metrics.record_duration(
RESPONSES_API_ENGINE_IAPI_TTFT_DURATION_METRIC,
Duration::from_millis(100),
&[],
)?;
metrics.record_duration(
RESPONSES_API_ENGINE_SERVICE_TBT_DURATION_METRIC,
Duration::from_millis(100),
&[],
)?;
metrics.record_duration(
RESPONSES_API_ENGINE_SERVICE_TTFT_DURATION_METRIC,
Duration::from_millis(100),
&[],
)?;
metrics.counter(TOOL_CALL_COUNT_METRIC, /*inc*/ 1, &[])?;
metrics.record_duration(TOOL_CALL_DURATION_METRIC, Duration::from_millis(25), &[])?;
metrics.histogram(TURN_TOKEN_USAGE_METRIC, /*value*/ 100, &[])?;
metrics.counter("codex.turns", /*inc*/ 1, &[])?;
metrics.shutdown()?;
@@ -192,12 +192,33 @@ fn otlp_http_exporter_sends_metrics_to_collector() -> Result<()> {
Duration::from_millis(100),
&[("status", "200")],
)?;
metrics.counter("codex.conversation.turn.count", /*inc*/ 1, &[])?;
metrics.record_duration(
"codex.responses_api_engine_iapi_ttft.duration_ms",
Duration::from_millis(100),
&[],
)?;
metrics.record_duration(
"codex.responses_api_engine_service_tbt.duration_ms",
Duration::from_millis(100),
&[],
)?;
metrics.record_duration(
"codex.responses_api_engine_service_ttft.duration_ms",
Duration::from_millis(100),
&[],
)?;
metrics.counter("codex.tool.call", /*inc*/ 1, &[("tool", "test")])?;
metrics.record_duration(
"codex.tool.call.duration_ms",
Duration::from_millis(42),
&[("tool", "test")],
)?;
metrics.histogram(
"codex.turn.token_usage",
/*value*/ 100,
&[("token_type", "total")],
)?;
metrics.gauge_with_description(
"codex.active",
"Number of active Codex operations.",
@@ -243,11 +264,36 @@ fn otlp_http_exporter_sends_metrics_to_collector() -> Result<()> {
"expected API-request duration not found; body prefix: {}",
&body.chars().take(2000).collect::<String>()
);
assert!(
body.contains("\"codex.conversation.turn.count\""),
"expected conversation turn count not found; body prefix: {}",
&body.chars().take(2000).collect::<String>()
);
assert!(
body.contains("\"codex.responses_api_engine_iapi_ttft.duration_ms\""),
"expected engine IAPI TTFT duration not found; body prefix: {}",
&body.chars().take(2000).collect::<String>()
);
assert!(
body.contains("\"codex.responses_api_engine_service_tbt.duration_ms\""),
"expected engine service TBT duration not found; body prefix: {}",
&body.chars().take(2000).collect::<String>()
);
assert!(
body.contains("\"codex.responses_api_engine_service_ttft.duration_ms\""),
"expected engine service TTFT duration not found; body prefix: {}",
&body.chars().take(2000).collect::<String>()
);
assert!(
body.contains("\"codex.tool.call\""),
"expected tool-call counter not found; body prefix: {}",
&body.chars().take(2000).collect::<String>()
);
assert!(
body.contains("\"codex.turn.token_usage\""),
"expected turn-token histogram not found; body prefix: {}",
&body.chars().take(2000).collect::<String>()
);
assert!(
body.contains("\"codex.tool.call.duration_ms\""),
"expected tool-call duration not found; body prefix: {}",