# LangGraph / LangChain

> Record graph and agent execution, model messages, tool calls and usage.

The OpenTelemetry LangChain instrumentor records LangChain and LangGraph execution through their callbacks. It captures agent and workflow invocations, model requests and tool calls without wrapping each node manually. Enable message capture to inspect model inputs, outputs and tool payloads in Radiant.

## Configure export

Use your existing Python application and the endpoint and ingestion key from [Set up your project](/docs/platform/set-up-your-project.md). Install the exporter and instrumentor alongside LangChain and LangGraph:

**Terminal**

```sh
python -m pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http \
  "opentelemetry-instrumentation-genai-langchain[instruments]==1.3b0"
```

This example uses the upstream instrumentor at version `1.3b0`. The `instruments` extra installs a compatible LangChain package, including for applications that currently depend only on LangGraph. Keep your existing graph, model provider and tool definitions.

**Application environment**

```sh
export RADIANT_TRACES_ENDPOINT='https://YOUR_INGESTION_HOST/v1/traces'
export RADIANT_INGESTION_KEY='YOUR_INGESTION_KEY'
export OTEL_SERVICE_NAME='YOUR_APPLICATION_NAME'
```

Initialize the provider and instrumentor at startup, before creating or invoking your graph. If the application already has an OpenTelemetry SDK provider, add the exporter to it instead of replacing that provider.

**tracing.py**

```python
import os
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor


provider = TracerProvider(
    resource=Resource.create({"service.name": os.environ["OTEL_SERVICE_NAME"]})
)
provider.add_span_processor(
    BatchSpanProcessor(
        OTLPSpanExporter(
            endpoint=os.environ["RADIANT_TRACES_ENDPOINT"],
            headers={"Authorization": "Bearer " + os.environ["RADIANT_INGESTION_KEY"]},
        )
    )
)
trace.set_tracer_provider(provider)


from opentelemetry.instrumentation.genai.langchain import LangChainInstrumentor


LangChainInstrumentor().instrument()
```

Import this module during startup, then invoke the existing graph or runnable normally. The instrumentor connects model and tool operations to their enclosing invocation. Remove manual spans that duplicate operations recorded by this instrumentor; you do not need a second OpenAI client instrumentor for model calls made through LangChain.

## Capture model and tool content

Set this before starting the application to include model input/output messages and tool arguments/results on spans:

**Application environment**

```sh
export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT='SPAN_ONLY'
```

Content capture is off by default. Enable it for payloads you intend to export. Model identity, timing and usage are recorded independently of message content.

Call `provider.shutdown()` through the application’s shutdown lifecycle to flush buffered spans, retaining the provider across requests. Keep streaming invocations alive until their streams are consumed.

## Identify agents and conversations

The instrumentor distinguishes workflows from agents. A general `StateGraph` is a workflow; LangChain’s `create_agent(...)` produces an agent invocation. Give an agent a stable `name` in its existing definition so its runs share one catalog identity. A workflow containing model calls does not become an agent merely because it calls a model.

Pass the conversation identifier in the invocation’s existing runnable configuration:

**At the existing invocation**

```python
result = agent.invoke(
    request_state,
    config={"metadata": {"thread_id": conversation_id}},
)
```

Merge `thread_id` into your existing configuration rather than replacing other settings. The instrumentor maps this LangGraph metadata value to `gen_ai.conversation.id`. Reuse it across turns in one conversation; the invocation for each turn remains a separate run. The same metadata works with `graph.invoke(...)`, `ainvoke(...)` and LangChain runnables.

## Add application context

Use OpenTelemetry resource attributes for process-wide labels:

**Application environment**

```sh
export OTEL_RESOURCE_ATTRIBUTES='deployment.environment.name=production,app.release=2026.10.30'
```

For request-specific attributes, add a normal OpenTelemetry request span around the existing invocation. Native agent, model and tool spans remain beneath it. See [attribute scope](/docs/platform/connect-your-agent/opentelemetry.md#add-attributes) before attaching application data.

## Inspect the execution

Run an existing request and find the service in **Logs**. Agent invocations appear in **All agent runs**; general workflow spans remain available in **All traces**. Open an invocation to inspect its model requests and tool calls. With content capture enabled, a model record’s **Thread** shows its messages, and tool records show their recorded arguments and results. **Conversations** groups agent runs by their conversation identifier.

See the upstream [LangChain instrumentor](https://github.com/open-telemetry/opentelemetry-python-genai/tree/main/instrumentation/opentelemetry-instrumentation-genai-langchain) for supported versions and callback behavior. The [LangGraph documentation](https://docs.langchain.com/oss/python/langgraph/overview) covers graph execution. For missing records, use [tracing troubleshooting](/docs/platform/reference/telemetry.md#troubleshoot-tracing).
