Skip to content

OpenTelemetry events - #2103

Merged
apanasenko-oai merged 8 commits into
openai:mainfrom
vishnu-oai:otel-tracing
Sep 29, 2025
Merged

OpenTelemetry events#2103
apanasenko-oai merged 8 commits into
openai:mainfrom
vishnu-oai:otel-tracing

Conversation

@vishnu-oai

@vishnu-oai vishnu-oai commented Aug 9, 2025

Copy link
Copy Markdown
Contributor

Title

otel

Codex can emit OpenTelemetry log events that
describe each run: outbound API requests, streamed responses, user input,
tool-approval decisions, and the result of every tool invocation. Export is
disabled by default so local runs remain self-contained. Opt in by adding an
[otel] table and choosing an exporter.

[otel]
environment = "staging"   # defaults to "dev"
exporter = "none"          # defaults to "none"; set to otlp-http or otlp-grpc to send events
log_user_prompt = false    # defaults to false; redact prompt text unless explicitly enabled

Codex tags every exported event with service.name = "codex-cli", the CLI
version, and an env attribute so downstream collectors can distinguish
dev/staging/prod traffic. Only telemetry produced inside the codex_otel
crate—the events listed below—is forwarded to the exporter.

Event catalog

Every event shares a common set of metadata fields: event.timestamp,
conversation.id, app.version, auth_mode (when available),
user.account_id (when available), terminal.type, model, and slug.

With OTEL enabled Codex emits the following event types (in addition to the
metadata above):

  • codex.api_request
    • cf_ray (optional)
    • attempt
    • duration_ms
    • http.response.status_code (optional)
    • error.message (failures)
  • codex.sse_event
    • event.kind
    • duration_ms
    • error.message (failures)
    • input_token_count (completion only)
    • output_token_count (completion only)
    • cached_token_count (completion only, optional)
    • reasoning_token_count (completion only, optional)
    • tool_token_count (completion only)
  • codex.user_prompt
    • prompt_length
    • prompt (redacted unless log_user_prompt = true)
  • codex.tool_decision
    • tool_name
    • call_id
    • decision (approved, approved_for_session, denied, or abort)
    • source (config or user)
  • codex.tool_result
    • tool_name
    • call_id
    • arguments
    • duration_ms (execution time for the tool)
    • success ("true" or "false")
    • output

Choosing an exporter

Set otel.exporter to control where events go:

  • none – leaves instrumentation active but skips exporting. This is the
    default.

  • otlp-http – posts OTLP log records to an OTLP/HTTP collector. Specify the
    endpoint, protocol, and headers your collector expects:

    [otel]
    exporter = { otlp-http = {
      endpoint = "https://otel.example.com/v1/logs",
      protocol = "binary",
      headers = { "x-otlp-api-key" = "${OTLP_TOKEN}" }
    }}
  • otlp-grpc – streams OTLP log records over gRPC. Provide the endpoint and any
    metadata headers:

    [otel]
    exporter = { otlp-grpc = {
      endpoint = "https://otel.example.com:4317",
      headers = { "x-otlp-meta" = "abc123" }
    }}

If the exporter is none nothing is written anywhere; otherwise you must run or point to your
own collector. All exporters run on a background batch worker that is flushed on
shutdown.

If you build Codex from source the OTEL crate is still behind an otel feature
flag; the official prebuilt binaries ship with the feature enabled. When the
feature is disabled the telemetry hooks become no-ops so the CLI continues to
function without the extra dependencies.

@vishnu-oai
vishnu-oai requested a review from Copilot August 11, 2025 17:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements OpenTelemetry (OTEL) tracing integration for the Codex application. The work adds telemetry configuration, a new telemetry crate with OTLP exporters, and session span tracking to enable observability across the TUI and exec binaries.

  • Adds codex-telemetry crate with OTLP file, HTTP, and gRPC exporters
  • Integrates telemetry layers into TUI and exec binaries with configurable exporters
  • Implements session span tracking with graceful shutdown to ensure trace export

Reviewed Changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
codex-rs/codex-telemetry/src/lib.rs New telemetry crate implementing OTLP exporters and tracing layer builders
codex-rs/core/src/telemetry_init.rs Configuration adapter between app config and telemetry crate
codex-rs/core/src/config_types.rs Telemetry configuration types for TOML deserialization
codex-rs/core/src/config.rs Integration of telemetry config with defaults and test updates
codex-rs/tui/src/lib.rs TUI binary telemetry integration with conditional layer composition
codex-rs/exec/src/lib.rs Exec binary telemetry integration and graceful shutdown handling
codex-rs/core/src/codex.rs Session span tracking with proper span lifecycle management
Comments suppressed due to low confidence (1)

codex-rs/codex-telemetry/src/lib.rs:20

  • [nitpick] Using PathBuf::new() creates an empty path which will be ignored according to the comment. Consider using a more explicit type like Option or a dedicated enum variant to make this intent clearer.
    use opentelemetry_sdk::export::trace::SpanData;

Comment thread codex-rs/codex-telemetry/src/lib.rs Outdated
Comment thread codex-rs/codex-telemetry/src/lib.rs Outdated
Comment thread codex-rs/core/src/config.rs Outdated
Comment thread codex-rs/exec/src/lib.rs Outdated
@vishnu-oai
vishnu-oai requested a review from bolinfest August 11, 2025 17:44
@vishnu-oai
vishnu-oai marked this pull request as ready for review August 11, 2025 17:44
@vishnu-oai vishnu-oai changed the title [wip] OTEL tracing OpenTelemetry tracing Aug 11, 2025
@regismesquita

Copy link
Copy Markdown

Hi @vishnu-oai, this PR has been quiet for a while.
I’m preparing a broader rollout that requires OTEL support, is this branch functional?

Is there any plan at OpenAI to prioritise and merge it?

Thanks for any update you can share.

@apanasenko-oai

Copy link
Copy Markdown
Contributor

Hi @vishnu-oai, this PR has been quiet for a while. I’m preparing a broader rollout that requires OTEL support, is this branch functional?

Is there any plan at OpenAI to prioritise and merge it?

Thanks for any update you can share.

Hi @regismesquita ,

We are working on this and next week will make more progress. Stay tuned.

Thanks

@bolinfest
bolinfest requested review from apanasenko-oai and removed request for bolinfest September 10, 2025 17:51
@apanasenko-oai
apanasenko-oai requested review from pakrym-oai and removed request for apanasenko-oai September 10, 2025 20:20
@apanasenko-oai
apanasenko-oai force-pushed the otel-tracing branch 6 times, most recently from 5606d4c to 8d3967a Compare September 13, 2025 00:24
@apanasenko-oai apanasenko-oai changed the title OpenTelemetry tracing OpenTelemetry events Sep 13, 2025
@apanasenko-oai
apanasenko-oai force-pushed the otel-tracing branch 8 times, most recently from d74f8b6 to 9d3e066 Compare September 15, 2025 21:50
Comment thread docs/config.md
Comment thread docs/config.md Outdated
Comment thread codex-rs/core/src/codex.rs Outdated
Comment thread codex-rs/core/src/codex.rs Outdated
Comment thread codex-rs/tui/src/lib.rs Outdated
Comment thread codex-rs/tui/src/lib.rs

if let Some(provider) = otel.as_ref() {
let otel_layer = OpenTelemetryTracingBridge::new(&provider.logger).with_filter(
tracing_subscriber::filter::filter_fn(codex_core::otel_init::codex_export_filter),

@pakrym-oai pakrym-oai Sep 25, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can build_provider also return a layer with filter applied or have a method to update tracing_subscriber::registry ? so we don't spread duplicates of registration code and keep it encapsulated?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with_filter produce very complicated return type, I can try to come up with something later in follow up.

Comment thread codex-rs/otel/src/lib.rs
#[cfg(feature = "otel")]
pub mod otel_provider;

#[cfg(not(feature = "otel"))]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have a feature for this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we always enable it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some people might want to build cli without otel, but I can drop this optionality.

@@ -89,7 +109,8 @@ async fn run_stream(sse_body: &str) -> Vec<ResponseEvent> {
while let Some(event) = stream.next().await {
match event {
Ok(ev) => events.push(ev),
Err(e) => panic!("stream event error: {e}"),
// We still collect the error to exercise telemetry and complete the task.
Err(_e) => break,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the containing method return Result?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will return results, here we breaking while loop of whiting on stream of events

Comment thread codex-rs/core/tests/chat_completions_payload.rs
Comment thread codex-rs/core/tests/suite/otel.rs
Comment thread codex-rs/core/tests/suite/client.rs

@jif-oai jif-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More things to say on the PR but the general comment is why creating everything directly on OTEL. I feel like using tracing::instrument and maybe some prometheuse metrics (not 100% on this one, maybe the current OTEL is better but worth it to try) would be way simpler and handle a lot of things for us

user_explicitly_approved_this_action: false,
})
}
SafetyCheck::AutoApprove {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the link with this PR ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, what do you mean?

Comment thread codex-rs/core/src/codex.rs Outdated
Comment thread codex-rs/core/src/codex.rs Outdated
@apanasenko-oai

Copy link
Copy Markdown
Contributor

More things to say on the PR but the general comment is why creating everything directly on OTEL. I feel like using tracing::instrument and maybe some prometheuse metrics (not 100% on this one, maybe the current OTEL is better but worth it to try) would be way simpler and handle a lot of things for us

While tracing::instrument is indeed simpler to apply (I even used it in a prior commit), this change is more focused on event logging than on tracing code execution. The goal is to keep these events stable and treat them as part of a contract. By handling logging manually, we gain finer control over what gets logged and when.

@apanasenko-oai
apanasenko-oai force-pushed the otel-tracing branch 6 times, most recently from bf9c740 to 8f04205 Compare September 27, 2025 00:23

@pakrym-oai pakrym-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you

@apanasenko-oai
apanasenko-oai merged commit 04c1782 into openai:main Sep 29, 2025
19 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 29, 2025
debug!("failed to parse ErrorResponse: {e}");
let error = format!("failed to parse ErrorResponse: {e}");
debug!(error);
response_error = Some(CodexErr::Stream(error, None))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this change made to set response_error in this case?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to catch those errors and report them as error events.

Comment thread codex-rs/core/src/client.rs
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants