Judgment Labs Logo
Tracing Providers

Dual Export

Send traces from existing observability providers to Judgment.

Dual export lets you keep an existing OpenTelemetry-compatible tracing provider while sending the same spans to Judgment. Use this when your current tool exposes an OpenTelemetry span processor, tracer provider, or OTLP exporter, such as Logfire, Langfuse, LangSmith, or Braintrust.

If your current provider does not expose OpenTelemetry or OTLP export, use Judgment tracing directly instead.

Setup

Create a standard OpenTelemetry OTLP exporter for Judgment, then attach it to your existing provider's tracing pipeline.

uv add opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http

Set your Judgment credentials:

export JUDGMENT_API_KEY="<JUDGMENT_API_KEY>"
export JUDGMENT_ORG_ID="<JUDGMENT_ORG_ID>"
export JUDGMENT_PROJECT_ID="<JUDGMENT_PROJECT_ID>"

To find JUDGMENT_PROJECT_ID, open the project in the Judgment app and copy the project ID from the URL, or press Ctrl-K and search for the project ID shortcut.

Create this local helper in your app:

judgment_otlp.py
import os

from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor


def create_judgment_processor() -> BatchSpanProcessor:
    return BatchSpanProcessor(
        OTLPSpanExporter(
            endpoint="https://api.judgmentlabs.ai/otel/v1/traces",
            headers={
                "Authorization": f"Bearer {os.environ['JUDGMENT_API_KEY']}",
                "X-Organization-Id": os.environ["JUDGMENT_ORG_ID"],
                "X-Project-Id": os.environ["JUDGMENT_PROJECT_ID"],
            },
        )
    )

Dual export sends the same spans to two backends. Apply the same data scrubbing and sampling policy you use for production tracing.

Provider Examples

Pydantic Logfire

Logfire accepts extra OpenTelemetry span processors through additional_span_processors.

logfire_dual_export.py
import logfire
from openai import OpenAI

from judgment_otlp import create_judgment_processor

logfire.configure(
    token="<LOGFIRE_WRITE_TOKEN>",
    service_name="my-agent",
    environment="production",
    send_to_logfire=True,
    additional_span_processors=[create_judgment_processor()],
)

logfire.instrument_openai()

client = OpenAI()
response = client.chat.completions.create(
    model="gpt-5.2",
    messages=[{"role": "user", "content": "Hello"}],
)

print(response.choices[0].message.content)

Requires a logfire version whose logfire.configure() accepts additional_span_processors. If that argument is missing, upgrade logfire.

Your OpenAI spans now go to both Logfire and Judgment.

Langfuse

Langfuse accepts a custom OpenTelemetry tracer provider. Add Judgment to that provider, then pass the same provider to Langfuse.

langfuse_dual_export.py
import os

from langfuse import Langfuse
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider

from judgment_otlp import create_judgment_processor

provider = TracerProvider()
provider.add_span_processor(create_judgment_processor())

trace.set_tracer_provider(provider)

langfuse = Langfuse(
    public_key=os.environ["LANGFUSE_PUBLIC_KEY"],
    secret_key=os.environ["LANGFUSE_SECRET_KEY"],
    tracer_provider=provider,
)

Requires Langfuse Python SDK v3+ for OpenTelemetry-native tracing. This example uses Langfuse(..., tracer_provider=provider).

Keep your existing Langfuse instrumentation. Langfuse adds its own processor to the shared provider, so spans created on this provider are seen by both Langfuse and Judgment.

LangSmith

LangSmith supports sending OpenTelemetry traces to an alternate provider. Point that alternate OTLP exporter at Judgment and leave LANGSMITH_OTEL_ONLY unset so traces still go to LangSmith.

langsmith_dual_export.py
import os

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider

from judgment_otlp import create_judgment_processor

os.environ["LANGSMITH_OTEL_ENABLED"] = "true"
os.environ["LANGSMITH_TRACING"] = "true"

provider = TracerProvider()
provider.add_span_processor(create_judgment_processor())
trace.set_tracer_provider(provider)

prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
model = ChatOpenAI()
chain = prompt | model

result = chain.invoke({"topic": "programming"})
print(result.content)

Requires langsmith[otel] >= 0.4.1 for alternate-provider / hybrid export. LangSmith recommends >= 0.4.25 for OTEL fixes.

Set LANGSMITH_OTEL_ONLY="true" only if you want to send traces to Judgment without also sending them to LangSmith.

Braintrust

Braintrust exposes an OpenTelemetry span processor. Add Judgment beside it on the same provider.

braintrust_dual_export.py
import os

from braintrust.otel import BraintrustSpanProcessor
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider

from judgment_otlp import create_judgment_processor

provider = TracerProvider()
provider.add_span_processor(
    BraintrustSpanProcessor(parent="project_name:my-braintrust-project")
)
provider.add_span_processor(create_judgment_processor())

trace.set_tracer_provider(provider)

Install braintrust[otel] for BraintrustSpanProcessor. Braintrust's OTel compatibility mode requires braintrust[otel] >= 0.3.1; distributed tracing requires >= 0.3.5.

Your existing OpenTelemetry or Braintrust-compatible instrumentation now exports to both Braintrust and Judgment.

If you already route traces through an OpenTelemetry Collector, add Judgment as another collector exporter instead of configuring dual export in application code.

Next Steps

  • Tracing - Learn how Judgment displays traces, spans, sessions, and LLM metadata.
  • OpenTelemetry integrations - Add automatic LLM instrumentation before dual exporting.