Because Phoenix is built on OpenTelemetry, you can run into situations where other OpenTelemetry traces unrelated to your LLM calls are logged into Phoenix. You can also run into cases where Phoenix traces are sent to other OTEL endpoints.
You have a few options to avoid these cases:
Filtering spans based on specific criteria
Selectively suppressing instrumentation on specific calls
Disable instrumentation on the offending package
Defining your own FilterProcessor
Filtering all Spans that meet a criteria
A good option if you want to keep both instrumentations running simultaneously is to define a custom ConditionalSpanProcessor. This custom processor can use conditions you define to selectively distribute spans.
This example uses a ConsoleSpanExporter as the second exporter alongside Phoenix. You could replace this with another exporter of a different kind.
from opentelemetry.sdk.trace.export import SpanExporter, SpanProcessor
# Custom SpanProcessor that only exports spans based on a condition
class ConditionalSpanProcessor(SpanProcessor):
def __init__(self, exporter: SpanExporter, condition: callable):
self.exporter = exporter
self.condition = condition
def on_start(self, span, parent_context):
pass
def on_end(self, span):
# Only export spans that meet the condition
if self.condition(span):
self.exporter.export([span])
def shutdown(self):
self.exporter.shutdown()
def force_flush(self, timeout_millis=None):
self.exporter.force_flush(timeout_millis)
# Define conditions for sending spans to specific exporters
def console_condition(span):
return "console" in span.name # Example: send to Console if "console" is in the span name
def phoenix_condition(span):
# return "phoenix" in span.name # Example: send to Phoenix if "phoenix" is in the span name
return not console_condition(span) # Example: send to Phoenix if "console" is not in the span name
You can set your conditions to be related to any criteria on your spans.
With this defined, you can now create instances for each of your destinations:
import os
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
from opentelemetry import trace as trace_api
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from dotenv import load_dotenv
load_dotenv()
from openinference.instrumentation.openai import OpenAIInstrumentor
def instrument():
# Add Phoenix API Key to the headers for tracing and API access
PHOENIX_API_KEY = os.getenv("PHOENIX_API_KEY")
os.environ["PHOENIX_CLIENT_HEADERS"] = f"api_key={PHOENIX_API_KEY}"
tracer_provider = TracerProvider()
# Create the Console exporter
console_exporter = ConsoleSpanExporter()
# Add the Console exporter to the tracer provider
tracer_provider.add_span_processor(
ConditionalSpanProcessor(console_exporter, console_condition)
)
# Create the Phoenix exporter. Replace endpoint with your endpoint if self-hosting
otlp_exporter = OTLPSpanExporter(endpoint="https://app.phoenix.arize.com/v1/traces")
# Add the Phoenix exporter to the tracer provider
tracer_provider.add_span_processor(
ConditionalSpanProcessor(otlp_exporter, phoenix_condition)
)
# Set the tracer provider
trace_api.set_tracer_provider(tracer_provider)
# Auto-instrumentors can still be used
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider, skip_dep_check=True)
From here, spans will be filtered based on the criteria you've set:
import openai
def run_app():
# Create a tracer
tracer = trace_api.get_tracer(__name__)
# Example of creating and exporting spans
with tracer.start_as_current_span("console-span"):
print("This span will be exported to Console only.")
# This request will only be exported to Phoenix
client = openai.OpenAI()
client.chat.completions.create(model="gpt-4o-mini", messages=[{"role": "user", "content": "Hello, world!"}])
Selectively suppress individual calls
If you instead want to selectively suppress instrumentation of individual calls, you can do so using the _SUPPRESS_INSTRUMENTATION_KEY:
from opentelemetry.context import _SUPPRESS_INSTRUMENTATION_KEY, attach, detach, set_value
token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
# your method call(s) that you want suppressed
detach(token)
Disabling Instrumentation
Certain libraries will automatically enable instrumentation if they detect the presence of the OTEL libraries. For example, google-cloud-bigquery displays this behavior.
If you simply want to suppress traces from one of these libraries, most libraries offer an option to suppress their traces.
For example, BigQuery traces can be surpressed with:
Phoenix does not automatically instrument any libraries unless you've called one of our auto-instrumentors. To disable tracing of one of those auto-instrumentors, see Disabling Instrumentation
Defining your own Filter Processor
If you've defined your own OTEL collector, you can instead set up app-wide criteria to block traces that meet certain criteria using a Filter Processor. This approach is more involved, as it requires defining your own OTEL span collector.