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

# OpenTelemetry

> Universal agent monitoring via OTLP — works with any language or framework. No sidecar required.

## How it works

OpenTelemetry (OTel) ingestion is built directly into the Muster backend. There is no sidecar connector to deploy — your agents export traces straight to your Muster instance over HTTPS.

This gives full coverage across:

* Any language (Python, Go, Java, Node.js, Rust)
* Any framework with OTel support (LangChain, CrewAI, LlamaIndex, custom agents)
* Agents that don't have a dedicated Muster connector

***

## What your developers do

Set three environment variables on your agent. That's it.

```bash theme={null}
OTEL_EXPORTER_OTLP_ENDPOINT=https://backend.YOUR-INSTANCE.getmuster.io/api/v1/otel
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer <muster-jwt-token>
OTEL_EXPORTER_OTLP_PROTOCOL=http/json
```

Get the JWT by calling `POST /auth/login` with a service account user, or copy it from your Muster dashboard → **Settings → API Access**.

No code changes are required if your agent already emits OTel spans.

***

## Adding OTel to a Python agent

If your agent doesn't yet emit OTel spans, add instrumentation once at startup:

```python theme={null}
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
import os

# Configure once at startup
provider = TracerProvider()
provider.add_span_processor(
    BatchSpanProcessor(OTLPSpanExporter(
        endpoint=os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] + "/v1/traces",
        headers={"Authorization": f"Bearer {os.environ['MUSTER_JWT']}"},
    ))
)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("my-agent", service_name="invoice-processor")

# Instrument your agent runs
def run_agent(input: str) -> str:
    with tracer.start_as_current_span("agent.run") as span:
        span.set_attribute("service.name", "invoice-processor")
        result = llm.invoke(input)
        span.set_attribute("gen_ai.usage.input_tokens", result.usage.prompt_tokens)
        span.set_attribute("gen_ai.usage.output_tokens", result.usage.completion_tokens)
        span.set_attribute("gen_ai.response.model", "gpt-4o")
        return result.content
```

<Tip>
  The `service.name` attribute becomes the agent name in Muster. New service names are auto-added to your Discovery queue for review.
</Tip>

***

## LangChain — zero-code instrumentation

If you use LangChain, one line enables automatic tracing of all chains and agents:

```python theme={null}
from opentelemetry.instrumentation.langchain import LangchainInstrumentor
LangchainInstrumentor().instrument()
# All chains, agents, and LLM calls now emit OTel spans automatically
```

Then set the three env vars above and Muster receives full trace data immediately.

***

## Check connection status

```bash theme={null}
GET /api/v1/otel/status
Authorization: Bearer <token>
```

Returns current connector status, executions received in the last 24 hours, and the exact env vars to use for your instance.

***

## What Muster does automatically

Once traces arrive:

| Action                  | Details                                                     |
| ----------------------- | ----------------------------------------------------------- |
| **Agent discovery**     | New `service.name` values appear in Discovery as UNREVIEWED |
| **Execution recording** | Each agent span stored as a connector execution             |
| **Cost tracking**       | Token usage attributes → cost events (for approved agents)  |
| **Quality inference**   | Check inference runs server-side on each execution          |

***

## Supported semantic conventions

| Attribute                     | Description                               |
| ----------------------------- | ----------------------------------------- |
| `service.name`                | Agent name (required)                     |
| `gen_ai.usage.input_tokens`   | Prompt token count (GenAI convention)     |
| `gen_ai.usage.output_tokens`  | Completion token count (GenAI convention) |
| `gen_ai.response.model`       | Model name                                |
| `gen_ai.request.model`        | Requested model name                      |
| `llm.usage.prompt_tokens`     | Prompt tokens (LangChain format)          |
| `llm.usage.completion_tokens` | Completion tokens (LangChain format)      |
| `llm.model_name`              | Model name (LangChain format)             |

***

## Supported agent span names

Muster identifies root agent executions from these span names (partial match):

`agent.run`, `agent.execute`, `agent.invoke`, `langchain.chain`, `langchain.agent`,
`crewai.task`, `crewai.agent`, `openai.chat`, `anthropic.messages`,
`bedrock.invoke`, `llm.completion`, `chain.run`, `tool.run`

To add custom span names, contact Elitery.

***

## Why OTel instead of a platform connector?

|                                       | OTel                | Platform connector |
| ------------------------------------- | ------------------- | ------------------ |
| Code changes                          | 3 env vars          | None               |
| Framework coverage                    | Universal           | Per-platform only  |
| Data richness                         | Full traces + spans | API metadata only  |
| Latency                               | Real-time           | Polling (minutes)  |
| Works on no-code tools (n8n, Flowise) | No                  | Yes                |

**Use OTel for custom-coded agents. Use platform connectors for no-code/low-code tools where you can't set env vars.**
