diff --git a/py/packages/genkit/src/genkit/core/trace/realtime_processor.py b/py/packages/genkit/src/genkit/core/trace/realtime_processor.py index 30cc6b52dc..c8fcf00ea4 100644 --- a/py/packages/genkit/src/genkit/core/trace/realtime_processor.py +++ b/py/packages/genkit/src/genkit/core/trace/realtime_processor.py @@ -75,6 +75,9 @@ from opentelemetry.sdk.trace.export import SpanExporter from genkit.core._compat import override +from genkit.core.logging import get_logger + +logger = get_logger(__name__) class RealtimeSpanProcessor(SpanProcessor): @@ -119,9 +122,23 @@ def on_start(self, span: Span, parent_context: Context | None = None) -> None: span: The span that was just started. parent_context: The parent context (unused). """ - # Export the span immediately (it won't have endTime yet) - # We ignore the result - we don't want to block span creation - _ = self._exporter.export([span]) + # Export the span immediately (it won't have endTime yet). + # Catch all exceptions so a failing exporter (e.g. Jaeger not + # running) never propagates into the application call stack. + # Without this guard, a ConnectionError here bubbles through + # the OTel gRPC server interceptor and kills the actual RPC. + try: + self._exporter.export([span]) + except ConnectionError: + logger.debug( + 'RealtimeSpanProcessor: export failed on_start (collector unreachable)', + exc_info=True, + ) + except Exception: # noqa: BLE001 — must never crash the caller + logger.warning( + 'RealtimeSpanProcessor: unexpected error during export on_start', + exc_info=True, + ) @override def on_end(self, span: ReadableSpan) -> None: @@ -132,8 +149,18 @@ def on_end(self, span: ReadableSpan) -> None: Args: span: The span that just ended. """ - # Export the completed span - _ = self._exporter.export([span]) + try: + self._exporter.export([span]) + except ConnectionError: + logger.debug( + 'RealtimeSpanProcessor: export failed on_end (collector unreachable)', + exc_info=True, + ) + except Exception: # noqa: BLE001 — must never crash the caller + logger.warning( + 'RealtimeSpanProcessor: unexpected error during export on_end', + exc_info=True, + ) @override def force_flush(self, timeout_millis: int = 30000) -> bool: