From a057eeb9834e185120317ef0399d0ff4ff0e1bcf Mon Sep 17 00:00:00 2001 From: RapidPoseidon Date: Mon, 6 Jul 2026 13:43:08 +0000 Subject: [PATCH] feat(tracing): send OTLP traces to the target environment's collector The SDK tracer and logger hard-coded the prod collector (otlp-sdk.rapidata.ai) and RapidataClient disabled OTLP entirely for any non-prod environment, so traces from the test environment were silently dropped. Derive the collector host from the client's environment (otlp-sdk.), matching how the api./auth. endpoints are built, and enable OTLP for environments that actually have a deployed collector (prod rapidata.ai + test rabbitdata.ch). Local dev (rapidata.dev) has no collector, so it stays disabled to avoid noisy failed exports. Co-Authored-By: Claude Opus 4.8 Co-Authored-By: lino --- src/rapidata/rapidata_client/config/logger.py | 4 +++- src/rapidata/rapidata_client/config/logging_config.py | 3 +++ src/rapidata/rapidata_client/config/tracer.py | 4 +++- src/rapidata/rapidata_client/rapidata_client.py | 11 +++++++++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/rapidata/rapidata_client/config/logger.py b/src/rapidata/rapidata_client/config/logger.py index 62e196d8c..039dcf617 100644 --- a/src/rapidata/rapidata_client/config/logger.py +++ b/src/rapidata/rapidata_client/config/logger.py @@ -46,6 +46,7 @@ def __init__(self, name: str = "rapidata"): self._otlp_handler = None self._otlp_enabled = True # Default to enabled self._otlp_attached = False + self._environment = "rapidata.ai" # Register this logger to receive configuration updates register_config_handler(self._handle_config_update) @@ -75,7 +76,7 @@ def _ensure_otlp_initialized(self) -> None: set_logger_provider(logger_provider) exporter = OTLPLogExporter( - endpoint="https://otlp-sdk.rapidata.ai/v1/logs", + endpoint=f"https://otlp-sdk.{self._environment}/v1/logs", timeout=30, ) @@ -104,6 +105,7 @@ def _update_logger(self, config: LoggingConfig) -> None: """Update the logger based on the new configuration.""" self._otlp_enabled = config.enable_otlp self._otlp_attached = False + self._environment = config.environment # Console handler with configurable level console_handler = logging.StreamHandler() diff --git a/src/rapidata/rapidata_client/config/logging_config.py b/src/rapidata/rapidata_client/config/logging_config.py index e07dfa208..f557f0e1a 100644 --- a/src/rapidata/rapidata_client/config/logging_config.py +++ b/src/rapidata/rapidata_client/config/logging_config.py @@ -42,6 +42,8 @@ class LoggingConfig(BaseModel): silent_mode (bool): Whether to disable the prints and progress bars. Does NOT affect the logging. Defaults to False. enable_otlp (bool): Whether to enable OpenTelemetry trace logs. Defaults to True. Can also be disabled via the RAPIDATA_DISABLE_OTLP=1 environment variable. + environment (str): The API environment the client targets, used to derive the + OTLP collector host (``otlp-sdk.``). Set by RapidataClient. """ @model_validator(mode="before") @@ -56,6 +58,7 @@ def _apply_env_vars(cls, data: Any) -> Any: format: str = Field(default="%(asctime)s - %(name)s - %(levelname)s - %(message)s") silent_mode: bool = Field(default=False) enable_otlp: bool = Field(default_factory=_default_enable_otlp) + environment: str = Field(default="rapidata.ai") def __init__(self, **kwargs): super().__init__(**kwargs) diff --git a/src/rapidata/rapidata_client/config/tracer.py b/src/rapidata/rapidata_client/config/tracer.py index 47c22a17f..a824cd848 100644 --- a/src/rapidata/rapidata_client/config/tracer.py +++ b/src/rapidata/rapidata_client/config/tracer.py @@ -132,6 +132,7 @@ def __init__(self, name: str = __name__): self._real_tracer = None self._no_op_tracer = NoOpTracer() self._enabled = True # Default to enabled + self._environment = "rapidata.ai" self.session_id: str | None = None self.client_id: str | None = None self.email: str | None = None @@ -146,6 +147,7 @@ def _handle_config_update(self, config: LoggingConfig) -> None: def _update_tracer(self, config: LoggingConfig) -> None: """Update the tracer based on the new configuration.""" self._enabled = config.enable_otlp + self._environment = config.environment def _ensure_initialized(self) -> None: """Lazily initialize OTLP tracing on first use.""" @@ -168,7 +170,7 @@ def _ensure_initialized(self) -> None: self._tracer_provider = TracerProvider(resource=resource) exporter = OTLPSpanExporter( - endpoint="https://otlp-sdk.rapidata.ai/v1/traces", + endpoint=f"https://otlp-sdk.{self._environment}/v1/traces", timeout=30, ) diff --git a/src/rapidata/rapidata_client/rapidata_client.py b/src/rapidata/rapidata_client/rapidata_client.py index e9e15d916..fb5aed018 100644 --- a/src/rapidata/rapidata_client/rapidata_client.py +++ b/src/rapidata/rapidata_client/rapidata_client.py @@ -58,6 +58,11 @@ class _UserInfoCacheEntry: _userinfo_cache: dict[tuple[str, str], _UserInfoCacheEntry] = {} _userinfo_cache_lock = threading.Lock() +# Environments with a deployed SDK OTLP collector at otlp-sdk.. +# Other environments (e.g. local rapidata.dev) have no collector, so OTLP is +# left disabled there to avoid noisy failed exports. +_OTLP_COLLECTOR_ENVIRONMENTS = frozenset({"rapidata.ai", "rabbitdata.ch"}) + class RapidataClient: """The Rapidata client is the main entry point for interacting with the Rapidata API. It allows you to create orders and validation sets.""" @@ -134,11 +139,13 @@ def __init__( self._client_id = client_id self._environment = environment + rapidata_config.logging.environment = environment + if environment not in _OTLP_COLLECTOR_ENVIRONMENTS: + rapidata_config.logging.enable_otlp = False + with tracer.start_as_current_span("RapidataClient.__init__"): logger.debug("Checking version") self._check_version() - if environment != "rapidata.ai": - rapidata_config.logging.enable_otlp = False logger.debug("Initializing OpenAPIService") self._openapi_service = OpenAPIService(