The batch span processor needs a tokio runtime for gRPC transport and periodic flush. Async services already have one via #[tokio::main], but sync training binaries (hyperopt, train, evaluate) don't. Previous approach (making binaries async with #[tokio::main]) caused "Cannot start a runtime from within a runtime" panics because the ML crate's internal code creates its own tokio runtimes for block_on(). New approach: build_otel_tracer() detects runtime context via Handle::try_current(). If absent, it creates a dedicated 1-worker multi-thread runtime stored in a process-lifetime OnceLock. The worker thread actively polls the OTLP batch export task. Reverts training binaries to sync fn main() so internal runtime creation (hyperopt adapters, DQN/PPO trainers) continues working as before. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
28 KiB
28 KiB