Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e192c20
V!
jif-oai Dec 18, 2025
358079d
V2
jif-oai Dec 19, 2025
5c8e7d0
Some updates in doc
jif-oai Dec 19, 2025
a4ed08a
Some nits
jif-oai Dec 19, 2025
cbdcc98
Rework harness structure
jif-oai Dec 19, 2025
f3699c5
More validation
jif-oai Dec 19, 2025
b041858
client to tokio
jif-oai Dec 19, 2025
2f18fc5
Move to otel
jif-oai Dec 19, 2025
11c6045
Unification
jif-oai Dec 21, 2025
14736ca
Unification 2
jif-oai Dec 22, 2025
d347354
Test base
jif-oai Dec 22, 2025
3d5ee77
Test base 1
jif-oai Dec 22, 2025
421d785
Move to tokio
jif-oai Dec 22, 2025
ff00555
Simplifications
jif-oai Dec 22, 2025
b2a8904
Further simplifications
jif-oai Dec 22, 2025
6bcbe8e
Batch events in worker
jif-oai Dec 22, 2025
cea563e
flatten metrics
jif-oai Dec 22, 2025
ddff652
Basic emission
jif-oai Dec 22, 2025
17e16f3
Drop warning
jif-oai Dec 22, 2025
feb4c0f
Merge remote-tracking branch 'origin/main' into jif/metric-lib
jif-oai Dec 22, 2025
b9d174e
Fix merge
jif-oai Dec 22, 2025
035712c
Lots of simplifications
jif-oai Dec 22, 2025
c597249
Make export symetric
jif-oai Dec 22, 2025
d40fec9
SIMPLEEEER
jif-oai Dec 22, 2025
dafbfe3
Add a few metrics
jif-oai Dec 22, 2025
e312462
Use a trait
jif-oai Dec 22, 2025
0fcdcc0
Further cleaning
jif-oai Dec 22, 2025
0b2164c
Better error
jif-oai Dec 22, 2025
ee43071
Improve readme
jif-oai Dec 22, 2025
bd5daad
Cleaning
jif-oai Dec 22, 2025
1220572
Fixes
jif-oai Dec 22, 2025
8e48bea
Fix tests
jif-oai Dec 22, 2025
6ba7133
Merge branch 'main' into jif/metric-lib
jif-oai Dec 23, 2025
80457cd
Fix merge
jif-oai Dec 23, 2025
88e6096
Move to otel
jif-oai Dec 23, 2025
6900a94
NIT
jif-oai Dec 23, 2025
062351d
Update doc
jif-oai Dec 23, 2025
4c16066
Go to u64
jif-oai Dec 23, 2025
968109b
Add a test
jif-oai Dec 23, 2025
67547c0
Fix tests
jif-oai Dec 23, 2025
32b2b6a
Merge remote-tracking branch 'origin/main' into jif/metric-lib
jif-oai Dec 26, 2025
9f682f0
Merge remote-tracking branch 'origin/main' into jif/metric-lib
jif-oai Jan 5, 2026
f34623b
Fix merge
jif-oai Jan 5, 2026
277d5a7
Merge branch 'main' into jif/metric-lib
jif-oai Jan 7, 2026
5f344d2
Add timer
jif-oai Jan 7, 2026
cc4864f
Merge remote-tracking branch 'origin/main' into jif/metric-lib
jif-oai Jan 7, 2026
dd1c025
Disable metrics on analytics false
jif-oai Jan 7, 2026
4212bf9
Merge remote-tracking branch 'origin/jif/metric-lib' into jif/metric-lib
jif-oai Jan 7, 2026
bc7fa5d
Drop error at client level and just log
jif-oai Jan 7, 2026
76d2e7f
Merge remote-tracking branch 'origin/main' into jif/metric-lib
jif-oai Jan 7, 2026
f792d75
Merge remote-tracking branch 'origin/main' into jif/metric-lib
jif-oai Jan 7, 2026
28eec16
fix merge
jif-oai Jan 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion codex-rs/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ keyring = { workspace = true, features = ["sync-secret-service"] }
assert_cmd = { workspace = true }
assert_matches = { workspace = true }
codex-arg0 = { workspace = true }
codex-core = { path = ".", features = ["deterministic_process_ids"] }
codex-core = { path = ".", default-features = false, features = ["deterministic_process_ids"] }
codex-otel = { workspace = true, features = ["disable-default-metrics-exporter"] }
codex-utils-cargo-bin = { workspace = true }
core_test_support = { workspace = true }
ctor = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use codex_api::common::Reasoning;
use codex_api::create_text_param_for_request;
use codex_api::error::ApiError;
use codex_app_server_protocol::AuthMode;
use codex_otel::otel_manager::OtelManager;
use codex_otel::OtelManager;
use codex_protocol::ThreadId;
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
use codex_protocol::models::ResponseItem;
Expand Down
3 changes: 2 additions & 1 deletion codex-rs/core/src/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ use crate::user_instructions::UserInstructions;
use crate::user_notification::UserNotification;
use crate::util::backoff;
use codex_async_utils::OrCancelExt;
use codex_otel::otel_manager::OtelManager;
use codex_otel::OtelManager;
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
use codex_protocol::models::ContentItem;
use codex_protocol::models::ResponseInputItem;
Expand Down Expand Up @@ -655,6 +655,7 @@ impl Session {
terminal::user_agent(),
session_configuration.session_source.clone(),
);
config.features.emit_metrics(&otel_manager);

otel_manager.conversation_starts(
config.model_provider.name.as_str(),
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1440,11 +1440,13 @@ impl Config {
.unwrap_or(DEFAULT_OTEL_ENVIRONMENT.to_string());
let exporter = t.exporter.unwrap_or(OtelExporterKind::None);
let trace_exporter = t.trace_exporter.unwrap_or_else(|| exporter.clone());
let metrics_exporter = t.metrics_exporter.unwrap_or(OtelExporterKind::Statsig);
OtelConfig {
log_user_prompt,
environment,
exporter,
trace_exporter,
metrics_exporter,
}
},
};
Expand Down
8 changes: 8 additions & 0 deletions codex-rs/core/src/config/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ pub struct OtelTlsConfig {
#[serde(rename_all = "kebab-case")]
pub enum OtelExporterKind {
None,
Statsig,
OtlpHttp {
endpoint: String,
#[serde(default)]
Expand Down Expand Up @@ -337,6 +338,11 @@ pub struct OtelConfigToml {

/// Optional trace exporter
pub trace_exporter: Option<OtelExporterKind>,

/// Optional metrics exporter
///
/// Defaults to `statsig` outside of tests.
pub metrics_exporter: Option<OtelExporterKind>,
}

/// Effective OTEL settings after defaults are applied.
Expand All @@ -346,6 +352,7 @@ pub struct OtelConfig {
pub environment: String,
pub exporter: OtelExporterKind,
pub trace_exporter: OtelExporterKind,
pub metrics_exporter: OtelExporterKind,
}

impl Default for OtelConfig {
Expand All @@ -355,6 +362,7 @@ impl Default for OtelConfig {
environment: DEFAULT_OTEL_ENVIRONMENT.to_owned(),
exporter: OtelExporterKind::None,
trace_exporter: OtelExporterKind::None,
metrics_exporter: OtelExporterKind::Statsig,
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions codex-rs/core/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

use crate::config::ConfigToml;
use crate::config::profile::ConfigProfile;
use codex_otel::OtelManager;
use serde::Deserialize;
use serde::Serialize;
use std::collections::BTreeMap;
Expand Down Expand Up @@ -199,6 +200,21 @@ impl Features {
.map(|usage| (usage.alias.as_str(), usage.feature))
}

pub fn emit_metrics(&self, otel: &OtelManager) {
for feature in FEATURES {
if self.enabled(feature.id) != feature.default_enabled {
otel.counter(
"codex.feature.state",
1,
&[
("feature", feature.key),
("value", &self.enabled(feature.id).to_string()),
],
);
}
}
}

/// Apply a table of key -> bool toggles (e.g. from TOML).
pub fn apply_map(&mut self, m: &BTreeMap<String, bool>) {
for (k, v) in m {
Expand Down
9 changes: 8 additions & 1 deletion codex-rs/core/src/otel_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use codex_otel::config::OtelExporter;
use codex_otel::config::OtelHttpProtocol;
use codex_otel::config::OtelSettings;
use codex_otel::config::OtelTlsConfig as OtelTlsSettings;
use codex_otel::otel_provider::OtelProvider;
use codex_otel::traces::otel_provider::OtelProvider;
use std::error::Error;

/// Build an OpenTelemetry provider from the app Config.
Expand All @@ -18,6 +18,7 @@ pub fn build_provider(
) -> Result<Option<OtelProvider>, Box<dyn Error>> {
let to_otel_exporter = |kind: &Kind| match kind {
Kind::None => OtelExporter::None,
Kind::Statsig => OtelExporter::Statsig,
Kind::OtlpHttp {
endpoint,
headers,
Expand Down Expand Up @@ -63,6 +64,11 @@ pub fn build_provider(

let exporter = to_otel_exporter(&config.otel.exporter);
let trace_exporter = to_otel_exporter(&config.otel.trace_exporter);
let metrics_exporter = if config.analytics {
to_otel_exporter(&config.otel.metrics_exporter)
} else {
OtelExporter::None
};

OtelProvider::from(&OtelSettings {
service_name: originator().value.to_owned(),
Expand All @@ -71,6 +77,7 @@ pub fn build_provider(
environment: config.otel.environment.to_string(),
exporter,
trace_exporter,
metrics_exporter,
})
}

Expand Down
2 changes: 1 addition & 1 deletion codex-rs/core/src/state/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::skills::SkillsManager;
use crate::tools::sandboxing::ApprovalStore;
use crate::unified_exec::UnifiedExecProcessManager;
use crate::user_notification::UserNotifier;
use codex_otel::otel_manager::OtelManager;
use codex_otel::OtelManager;
use tokio::sync::Mutex;
use tokio::sync::RwLock;
use tokio_util::sync::CancellationToken;
Expand Down
8 changes: 8 additions & 0 deletions codex-rs/core/src/tasks/compact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,16 @@ impl SessionTask for CompactTask {
session.as_ref(),
&ctx.client.get_provider(),
) {
let _ = session
.services
.otel_manager
.counter("codex.task.compact.remote", 1, &[]);
crate::compact_remote::run_remote_compact_task(session, ctx).await
} else {
let _ = session
.services
.otel_manager
.counter("codex.task.compact.local", 1, &[]);
crate::compact::run_compact_task(session, ctx, input).await
}

Expand Down
6 changes: 6 additions & 0 deletions codex-rs/core/src/tasks/review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ impl SessionTask for ReviewTask {
input: Vec<UserInput>,
cancellation_token: CancellationToken,
) -> Option<String> {
let _ = session
.session
.services
.otel_manager
.counter("codex.task.review", 1, &[]);

// Start sub-codex conversation and get the receiver for events.
let output = match start_review_conversation(
session.clone(),
Expand Down
5 changes: 5 additions & 0 deletions codex-rs/core/src/tasks/undo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ impl SessionTask for UndoTask {
_input: Vec<UserInput>,
cancellation_token: CancellationToken,
) -> Option<String> {
let _ = session
.session
.services
.otel_manager
.counter("codex.task.undo", 1, &[]);
let sess = session.clone_session();
sess.send_event(
ctx.as_ref(),
Expand Down
6 changes: 6 additions & 0 deletions codex-rs/core/src/tasks/user_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ impl SessionTask for UserShellCommandTask {
_input: Vec<UserInput>,
cancellation_token: CancellationToken,
) -> Option<String> {
let _ = session
.session
.services
.otel_manager
.counter("codex.task.user_shell", 1, &[]);

let event = EventMsg::TaskStarted(TaskStartedEvent {
model_context_window: turn_context.client.get_model_context_window(),
});
Expand Down
5 changes: 3 additions & 2 deletions codex-rs/core/src/tools/orchestrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::tools::sandboxing::ToolCtx;
use crate::tools::sandboxing::ToolError;
use crate::tools::sandboxing::ToolRuntime;
use crate::tools::sandboxing::default_exec_approval_requirement;
use codex_otel::ToolDecisionSource;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::ReviewDecision;

Expand Down Expand Up @@ -45,8 +46,8 @@ impl ToolOrchestrator {
let otel = turn_ctx.client.get_otel_manager();
let otel_tn = &tool_ctx.tool_name;
let otel_ci = &tool_ctx.call_id;
let otel_user = codex_otel::otel_manager::ToolDecisionSource::User;
let otel_cfg = codex_otel::otel_manager::ToolDecisionSource::Config;
let otel_user = ToolDecisionSource::User;
let otel_cfg = ToolDecisionSource::Config;

// 1) Approval
let mut already_approved = false;
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/core/tests/chat_completions_payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use codex_core::Prompt;
use codex_core::ResponseItem;
use codex_core::WireApi;
use codex_core::models_manager::manager::ModelsManager;
use codex_otel::otel_manager::OtelManager;
use codex_otel::OtelManager;
use codex_protocol::ThreadId;
use codex_protocol::models::ReasoningItemContent;
use codex_protocol::protocol::SessionSource;
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/core/tests/chat_completions_sse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use codex_core::ResponseEvent;
use codex_core::ResponseItem;
use codex_core::WireApi;
use codex_core::models_manager::manager::ModelsManager;
use codex_otel::otel_manager::OtelManager;
use codex_otel::OtelManager;
use codex_protocol::ThreadId;
use codex_protocol::models::ReasoningItemContent;
use codex_protocol::protocol::SessionSource;
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/core/tests/responses_headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use codex_core::ResponseEvent;
use codex_core::ResponseItem;
use codex_core::WireApi;
use codex_core::models_manager::manager::ModelsManager;
use codex_otel::otel_manager::OtelManager;
use codex_otel::OtelManager;
use codex_protocol::ThreadId;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::protocol::SessionSource;
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/core/tests/suite/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use codex_core::models_manager::manager::ModelsManager;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_core::protocol::SessionSource;
use codex_otel::otel_manager::OtelManager;
use codex_otel::OtelManager;
use codex_protocol::ThreadId;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::Verbosity;
Expand Down
18 changes: 12 additions & 6 deletions codex-rs/otel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,47 @@ path = "src/lib.rs"
[lints]
workspace = true

[features]
## Disables the built-in default metrics exporter.
##
## Intended for use from `dev-dependencies` so unit/integration tests never
## attempt to export metrics over the network.
disable-default-metrics-exporter = []

[dependencies]
chrono = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-api = { workspace = true }
codex-protocol = { workspace = true }
eventsource-stream = { workspace = true }
opentelemetry = { workspace = true, features = ["logs", "trace"] }
opentelemetry = { workspace = true, features = ["logs", "metrics", "trace"] }
opentelemetry-appender-tracing = { workspace = true }
opentelemetry-otlp = { workspace = true, features = [
"grpc-tonic",
"http-proto",
"http-json",
"logs",
"metrics",
"trace",
"reqwest-blocking-client",
"reqwest-rustls",
"tls",
"tls-roots",
]}
opentelemetry-semantic-conventions = { workspace = true }
opentelemetry_sdk = { workspace = true, features = [
"logs",
"rt-tokio",
"trace",
]}
opentelemetry_sdk = { workspace = true, features = ["logs", "metrics", "rt-tokio", "testing", "trace"] }
http = { workspace = true }
reqwest = { workspace = true, features = ["blocking", "rustls-tls"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
strum_macros = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
tracing-opentelemetry = { workspace = true }
tracing-subscriber = { workspace = true }

[dev-dependencies]
opentelemetry_sdk = { workspace = true, features = ["testing"] }
pretty_assertions = { workspace = true }
Loading
Loading