> ## Documentation Index
> Fetch the complete documentation index at: https://langwatch.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Running LangWatch with Other OTel SDKs

> How to configure LangWatch for complete trace isolation when running alongside another OpenTelemetry-based SDK.

When you run LangWatch alongside another OTel-based SDK, both SDKs may hook
into the same global `TracerProvider`. This causes **cross-contamination** —
LLM traces appear in the other tool and application traces appear in LangWatch.

Both LangWatch SDKs support **true provider isolation** using a dedicated
`TracerProvider`. LangWatch attaches its exporter to the dedicated provider and
does not touch the global — each SDK operates independently.

## Python

Pass a dedicated `TracerProvider` to `langwatch.setup()`. LangWatch uses it
exclusively, leaving the global provider for the other SDK:

```python theme={null}
from opentelemetry.sdk.trace import TracerProvider
import langwatch

# The other OTel SDK initializes first and owns the global provider.
# Create a dedicated provider for LangWatch:
langwatch_provider = TracerProvider()

langwatch.setup(
    api_key="...",
    tracer_provider=langwatch_provider,
)
```

That's it. From here:

* `@langwatch.trace()` creates spans on the dedicated provider automatically
* The other SDK continues using the global provider
* Neither SDK sees the other's spans

### Full example

```python theme={null}
from opentelemetry.sdk.trace import TracerProvider
import langwatch

langwatch_provider = TracerProvider()
langwatch.setup(api_key="...", tracer_provider=langwatch_provider)

@langwatch.trace(name="llm-call")
def chat(user_message: str):
    response = client.chat.completions.create(
        model="gpt-4.1-nano",
        messages=[{"role": "user", "content": user_message}],
    )
    return response.choices[0].message.content
```

## TypeScript

Pass a dedicated `NodeTracerProvider` via the `tracerProvider` option.
LangWatch attaches its exporter to it and skips global provider detection:

```typescript theme={null}
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { setupObservability } from "langwatch/observability/node";

// The other OTel SDK initializes first and owns the global provider.
// Create a dedicated provider for LangWatch:
const lwProvider = new NodeTracerProvider();

setupObservability({
  tracerProvider: lwProvider,
  langwatch: { apiKey: process.env.LANGWATCH_API_KEY },
});
```

Use `lwProvider.getTracer()` to create spans that go to LangWatch only:

```typescript theme={null}
const tracer = lwProvider.getTracer("my-llm-service");

await tracer.startActiveSpan("llm-call", async (span) => {
  span.setAttribute("gen_ai.system", "openai");
  span.setAttribute("langwatch.input", userMessage);
  // Your LLM call here
  span.setAttribute("langwatch.output", response);
  span.end();
});
```

Only spans created through `lwProvider.getTracer()` go to LangWatch. The global
provider is untouched.

<Note>
  Auto-instrumentations that emit spans through the global OTel API (like Vercel
  AI SDK's `experimental_telemetry`) send spans to the global provider, not the
  dedicated one. For those, use `lwProvider.getTracer()` directly to create LLM
  spans on the isolated provider.
</Note>

## Alternative: Coexistence (TypeScript)

If you don't need full isolation and just want LangWatch to receive spans from
an existing provider, use `advanced.attachToExistingProvider`:

```typescript theme={null}
setupObservability({
  langwatch: { apiKey: process.env.LANGWATCH_API_KEY },
  advanced: { attachToExistingProvider: true },
});
```

This adds LangWatch's processor to the shared global provider. LangWatch
receives **all** spans — use the `LangWatchTraceExporter` filter options to
scope to LLM-only spans. See the
[TypeScript SDK reference](/docs/integration/typescript/reference) for filter
configuration.

## Alternative: Filtering (Python)

If you prefer to share the global provider in Python, use `span_exclude_rules`:

```python theme={null}
from langwatch.domain import SpanProcessingExcludeRule
import langwatch

langwatch.setup(
    span_exclude_rules=[
        SpanProcessingExcludeRule(
            field_name="span_name",
            match_operation="starts_with",
            match_value="GET",
        ),
        SpanProcessingExcludeRule(
            field_name="span_name",
            match_operation="starts_with",
            match_value="POST",
        ),
    ],
)
```

This is a name-based blocklist. The dedicated provider approach is more reliable.
