Dual Tracing into Databricks Unity Catalog and Arize AX
This notebook demonstrates split-stream OpenTelemetry tracing for agents running on Databricks: the same spans are exported to Arize AX for real-time observability and to Unity Catalog Delta tables for governed storage.
This notebook will only run in a Databricks workspace environment.
Link to Notebook Tutorial
In this notebook you learn to:
Configure a single OpenTelemetry TracerProvider with two span processors — one exporting to Arize AX and one exporting to Databricks Unity Catalog
Stream OTLP spans to Arize AX for real-time trace exploration, evaluation, and monitoring
Stream the same OTLP spans into Unity Catalog Delta tables for retention, governance, and SQL analytics
Instrument an external-style OpenAI + LangChain tool-calling agent with OpenInference auto-instrumentation
Bind an MLflow experiment to Unity Catalog so traces are queryable via Databricks SQL
Verify ingest on both streams — SQL against the UC spans table and the trace explorer in Arize AX
Access Arize AX and Databricks keys from Databricks Secrets
Create an Arize AX API key and Space ID for the items below. Use Databricks Secrets for secure access of keys, then map them to environment variables.Run this configuration section after the install cell restarts Python. Set the widgets below or map secrets to environment variables in your cluster policy:
This creates the {prefix}_otel_spans table (and related tables) and links the MLflow experiment for optional trace UI browsing. The full table name is used later as the export target for the Databricks span processor.
This is the core of the pattern: a single OpenTelemetry TracerProvider with two span processors. Spans are produced once by the instrumented agent and fanned out to both destinations.Stream 1 — Arize AX. The Arize exporter ships OTLP spans over gRPC by default for sub-second ingest. The arize-otel package provides the exporter, endpoint, and transport helpers. The arize_project_name is set as a resource attribute and is required for spans to land in the correct project.Stream 2 — Databricks Unity Catalog. A standard OTLP exporter posts the same spans to the Databricks OTel collector endpoint (/api/2.0/otel/v1/traces). The X-Databricks-UC-Table-Name header routes spans into the Unity Catalog Delta table created above.
With the dual-export TracerProvider in place, a single line of OpenInference auto-instrumentation captures every LLM call and tool invocation as OTLP spans — which the two processors then fan out to Arize AX and Unity Catalog. The demo workload is an OpenAI + LangChain tool-calling agent with a simple policy-lookup tool.
from langchain.agents import create_agentfrom langchain_core.tools import toolfrom langchain_openai import ChatOpenAIfrom openinference.instrumentation.langchain import LangChainInstrumentor# 1 line auto instrumentationLangChainInstrumentor().instrument(tracer_provider=tracer_provider)@tooldef lookup_policy(topic: str) -> str: """Return a short internal policy snippet for the given topic.""" policies = { "travel": "Demo travel expenses under $500 are pre-approved for partner workshops.", "security": "All agent outputs must stay within approved data boundaries.", } return policies.get(topic.lower(), f"No policy on file for '{topic}'.")llm = ChatOpenAI(model=openai_model, temperature=0)tools = [lookup_policy]agent = create_agent( llm, tools=tools, system_prompt=( "You are an enterprise assistant. Use tools when you need policy details. Be concise." ),)print("Running demo agent...")result = agent.invoke({"messages": [{"role": "user", "content": demo_user_message}]})messages = result.get("messages", [])print("\n--- Agent response ---")print(messages[-1].content if messages else result)
Force the OTLP batches to be delivered before verification. This is required for short notebook runs where the batch processors may not flush on their own before the cell finishes.
shutdown_tracer(tracer_provider)print("Tracer shut down and spans flushed.")
Spans should appear in {catalog}.{schema}.{prefix}_otel_spans within a short delay after export. Query the governed Delta table directly with Spark SQL.
import timetime.sleep(15)display( spark.sql(f""" SELECT trace_id, span_id, name, kind, start_time_unix_nano, end_time_unix_nano, (end_time_unix_nano - start_time_unix_nano) / 1e6 AS duration_ms FROM {uc_spans_table} ORDER BY start_time_unix_nano DESC LIMIT 10 """))
Open your Arize project (the arize_project_name widget value) in the AX UI. In the Arize AX platform you can see agent execution details, tool invocations, latency breakdown by component, token usage and costs, and metadata captured for each span. From here you can layer on online evaluations, dashboards, and monitors.
In the workspace Experiments page, open {experiment_name} → Traces tab (select your SQL warehouse). This is optional — SQL on the UC spans table is the primary governed-store proof.
Use Databricks SQL on the Unity Catalog span tables for analytics. For operational apps, treat Unity Catalog as the system of record and expose a permissioned read path via Lakebase if sub-second app reads are needed.
With spans flowing into both Arize AX and Unity Catalog, you have one telemetry source feeding real-time observability and governed long-term storage. From here, set up online evaluations in Arize AX to score your agent’s quality continuously, build custom metrics from trace attributes, and run SQL analytics over the governed UC tables.