mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
## Why When the app server started with tracing disabled, its subscriber lacked the trace bridge needed to export spans after an account switch enabled tracing. Disabling tracing also left the previous global tracer provider installed. ## What changed - Always install the reloadable trace layer so account switches can enable trace export without restarting the app server. - Replace the global tracer provider with a no-op provider when trace export is disabled, including when all telemetry exporters are disabled. ## Testing Add an app-server regression test that starts with tracing disabled, enables a requirements-managed trace exporter on account switch, and verifies export with the parent trace ID preserved. Add a provider unit test verifying that disabling telemetry stops new spans from recording. GitOrigin-RevId: 3561b748a06e6d7c819f82d9db5889dafd124ad8
114 lines
4.4 KiB
Rust
114 lines
4.4 KiB
Rust
use crate::OTEL_SERVICE_NAME;
|
|
use crate::config_manager::ConfigManager;
|
|
use codex_login::AuthManager;
|
|
use codex_otel::OtelProvider;
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
use tokio::task::JoinHandle;
|
|
use tokio_util::sync::CancellationToken;
|
|
use tracing::Subscriber;
|
|
use tracing::info;
|
|
use tracing::warn;
|
|
use tracing_subscriber::Layer;
|
|
use tracing_subscriber::registry::LookupSpan;
|
|
use tracing_subscriber::reload;
|
|
|
|
type OtelExportLayer<S> = Option<Box<dyn Layer<S> + Send + Sync + 'static>>;
|
|
type OtelReloadLayers<S> = (
|
|
Vec<Box<dyn Layer<S> + Send + Sync + 'static>>,
|
|
reload::Handle<OtelExportLayer<S>, S>,
|
|
);
|
|
|
|
pub(crate) fn layers<S>(provider: Option<&OtelProvider>) -> OtelReloadLayers<S>
|
|
where
|
|
S: Subscriber + for<'span> LookupSpan<'span> + Send + Sync + 'static,
|
|
{
|
|
let logger_export_layer: OtelExportLayer<S> = provider
|
|
.and_then(OtelProvider::logger_export_layer)
|
|
.map(Layer::boxed);
|
|
let (logger_layer, logger_handle) = reload::Layer::new(logger_export_layer);
|
|
|
|
let layers: Vec<Box<dyn Layer<S> + Send + Sync + 'static>> = vec![
|
|
logger_layer
|
|
.with_filter(tracing_subscriber::filter::filter_fn(
|
|
OtelProvider::log_export_filter,
|
|
))
|
|
.boxed(),
|
|
// The subscriber graph is fixed after startup. Keep the global tracer
|
|
// bridge installed even when tracing starts disabled so an account
|
|
// switch can install a trace exporter later.
|
|
OtelProvider::reloadable_tracing_layer(OTEL_SERVICE_NAME).boxed(),
|
|
];
|
|
(layers, logger_handle)
|
|
}
|
|
|
|
pub(crate) fn spawn<S>(
|
|
mut provider: Option<OtelProvider>,
|
|
logger_reload_handle: reload::Handle<OtelExportLayer<S>, S>,
|
|
config_manager: ConfigManager,
|
|
auth_manager: Arc<AuthManager>,
|
|
default_analytics_enabled: bool,
|
|
shutdown_token: CancellationToken,
|
|
) -> JoinHandle<()>
|
|
where
|
|
S: Subscriber + for<'span> LookupSpan<'span> + Send + Sync + 'static,
|
|
{
|
|
let mut auth_changes = auth_manager.auth_change_receiver();
|
|
|
|
tokio::spawn(async move {
|
|
loop {
|
|
tokio::select! {
|
|
_ = shutdown_token.cancelled() => break,
|
|
changed = auth_changes.changed() => {
|
|
if changed.is_err() {
|
|
break;
|
|
}
|
|
|
|
// Account handlers install the new cloud loader after publishing auth changes.
|
|
tokio::time::sleep(Duration::from_millis(/*millis*/ 50)).await;
|
|
|
|
let config = match config_manager.load_latest_config(/*fallback_cwd*/ None).await {
|
|
Ok(config) => config,
|
|
Err(error) => {
|
|
warn!(%error, "failed to reload telemetry config after account change");
|
|
continue;
|
|
}
|
|
};
|
|
let next_provider = match codex_core::otel_init::build_provider(
|
|
&config,
|
|
env!("CARGO_PKG_VERSION"),
|
|
Some(OTEL_SERVICE_NAME),
|
|
default_analytics_enabled,
|
|
) {
|
|
Ok(provider) => provider,
|
|
Err(error) => {
|
|
warn!(%error, "failed to rebuild telemetry exporters after account change");
|
|
continue;
|
|
}
|
|
};
|
|
if let Err(error) = logger_reload_handle.reload(
|
|
next_provider
|
|
.as_ref()
|
|
.and_then(OtelProvider::logger_export_layer)
|
|
.map(Layer::boxed),
|
|
) {
|
|
warn!(%error, "failed to install telemetry exporters after account change");
|
|
continue;
|
|
}
|
|
if let Some(previous_provider) = std::mem::replace(&mut provider, next_provider) {
|
|
drop(tokio::task::spawn_blocking(move || previous_provider.shutdown()));
|
|
}
|
|
info!(
|
|
event.name = "codex.app_server.otel_reloaded",
|
|
"reloaded telemetry exporters after account change"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(provider) = provider {
|
|
let _ = tokio::task::spawn_blocking(move || provider.shutdown()).await;
|
|
}
|
|
})
|
|
}
|