Files
2026-07-13 13:22:34 +08:00

76 lines
2.8 KiB
Python

import logging
from typing import Sequence
from opentelemetry.sdk.trace import ReadableSpan
from mlflow.entities.span import Span
from mlflow.entities.trace_info import TraceInfo
from mlflow.environment_variables import MLFLOW_ENABLE_ASYNC_TRACE_LOGGING
from mlflow.tracing.export.mlflow_v3 import MlflowV3SpanExporter
from mlflow.tracing.export.span_batcher import SpanBatcher
from mlflow.tracing.utils import get_active_spans_table_name
_logger = logging.getLogger(__name__)
class DatabricksUCTableSpanExporter(MlflowV3SpanExporter):
"""
An exporter implementation that logs the traces to Databricks Unity Catalog table.
"""
def __init__(self, tracking_uri: str | None = None) -> None:
super().__init__(tracking_uri)
# Track if we've raised an error for span export to avoid raising it multiple times.
self._has_raised_span_export_error = False
if hasattr(self, "_async_queue"):
self._span_batcher = SpanBatcher(
async_task_queue=self._async_queue,
log_spans_func=self._log_spans,
)
def _export_spans_incrementally(self, spans: Sequence[ReadableSpan]) -> None:
"""
Export spans incrementally as they complete.
Args:
spans: Sequence of ReadableSpan objects to export.
"""
location = get_active_spans_table_name()
if not location:
# This should not happen since this exporter is only used when a
# destination is set to UnityCatalog or UCSchemaLocation.
_logger.debug("No active spans table name found. Skipping span export.")
return
# Wrapping with MLflow span interface for easier downstream handling
spans = [Span(span) for span in spans]
if self._should_log_async():
for span in spans:
self._span_batcher.add_span(location=location, span=span)
else:
self._log_spans(location, spans)
def _log_spans(self, location: str, spans: list[Span]) -> None:
try:
self._client.log_spans(location, spans)
except Exception as e:
if self._has_raised_span_export_error:
_logger.debug(f"Failed to log spans to the trace server: {e}", exc_info=True)
else:
_logger.warning(f"Failed to log spans to the trace server: {e}")
self._has_raised_span_export_error = True
def _should_enable_async_logging(self) -> bool:
return MLFLOW_ENABLE_ASYNC_TRACE_LOGGING.get()
# Override this to False since spans are logged to UC table instead of artifacts.
def _should_log_spans_to_artifacts(self, trace_info: TraceInfo) -> bool:
return False
def flush(self) -> None:
self._span_batcher.shutdown()
self._async_queue.flush(terminate=True)