How do I send traces from the OpenTelemetry Python SDK to Rius?
Rius accepts traces from the OpenTelemetry Python SDK over OTLP/HTTP, so you point the exporter at the Rius ingest endpoint with an Authorization Bearer header. You need no Rius SDK and no re-instrumentation. Set the endpoint, the http/protobuf protocol and your API key, run one traced call, and the trace appears in the console with cost computed by Rius.
How it works
The Rius ingest endpoint is standard OTLP/HTTP, so the exporter you already use in Python works as it is. Three things matter:
- Endpoint. For the
euregion the traces URL ishttps://ingest.eu.console.rius-glassflow.com/v1/traces. Hosts carry your workspace’s region in the subdomain, so use the host that matches your workspace. - Protocol. Rius takes OTLP over HTTP as
http/protobuf(recommended) orhttp/json. There is no gRPC endpoint, so an exporter that speaks gRPC cannot send to Rius. - Authorization. The API key goes in an
Authorizationheader asBearerfollowed by your key. The key also decides which workspace the traces land in.
There are two ways to configure the Python SDK. With the standard OTEL_EXPORTER_OTLP_* environment variables, you give the base URL and the exporter appends /v1/traces itself. With an OTLPSpanExporter built in code, you give the full /v1/traces URL, because that exporter does not append the path.
Rius reads OpenTelemetry GenAI attributes (gen_ai.*) and normalizes the OpenLLMetry and OpenInference conventions onto them at ingest. Model, token and cost analytics therefore work for spans from those instrumentors too. You never send a cost value. Rius computes it from the model and token counts on the span.
Set it up
- Create an API key in the Rius console . Keys start with
ri_. - Set the standard exporter variables for the Python SDK. These are the same variables every OpenTelemetry SDK honors, and the endpoint is the base URL without the path:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.eu.console.rius-glassflow.com"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer <your API key>"- Set the service name with
OTEL_SERVICE_NAME, for examplemy-agent, so the agent is easy to find. Also raise the attribute limit withOTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=4096. The default of 128 attributes is too low once a span carries messages or tool definitions as one attribute per field, and long LLM spans lose data without the higher limit. - Register a tracer provider with an OTLP exporter, because the variables take effect only through one. Created with no arguments,
OTLPSpanExporter()reads them. To configure the exporter in code instead, pass the full/v1/tracesendpoint and the header, as here:
from opentelemetry import trace
from opentelemetry.sdk.trace import SpanLimits, TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
provider = TracerProvider(span_limits=SpanLimits(max_span_attributes=4096))
provider.add_span_processor(
BatchSpanProcessor(
OTLPSpanExporter(
endpoint="https://ingest.eu.console.rius-glassflow.com/v1/traces",
headers={"Authorization": "Bearer <your API key>"},
)
)
)
trace.set_tracer_provider(provider)- Make sure your spans carry what Rius reads for an LLM call:
gen_ai.request.model,gen_ai.usage.input_tokensandgen_ai.usage.output_tokens, plusgen_ai.operation.name. With the input and output messages added, the span is a full generation. Nest spans the normal OpenTelemetry way and wrap each agent run in one root span, because the root’s name, duration and status drive the run-level views. - Run one traced LLM call, then check that it arrived (see below).
The Vanilla OpenTelemetry SDKs page covers this setup for any language, and the Interoperability section has the other sender guides.
What you get
Once the first trace arrives, open Traces in the console and open the new trace. Check these in order:
- The LLM span shows a model name, token counts and a cost. Cost is a list-price estimate in US dollars from LiteLLM’s public model-price catalog, not a billed amount.
- Child spans nest under your root span in one trace.
- The service name is the one you set.
If nothing arrives, a 401 from the endpoint means the API key is wrong or revoked. If a span arrives without a model or tokens, attributes were usually lost on the way. Look for a Collector attributes or transform processor that strips gen_ai.* keys, or a span over OpenTelemetry’s default limit of 128 attributes. Raising the limit as shown above fixes the second case.
Because you send plain OTLP, code written this way keeps working unchanged if you later adopt a native Rius SDK. The full comparison of sender paths is in Trace LLM calls with OpenTelemetry.
When something else fits
- If you have no tracing yet, the Rius Python SDK is one
init()call. It also adds message normalization, a raised attribute limit, export-time privacy controls and heartbeats on top of plain OpenTelemetry. - If you already run an OpenTelemetry Collector, add an
otlphttpexporter entry to it instead of changing each service. Useotlphttp, nototlp, which speaks gRPC. - If your spans come from OpenLLMetry or OpenInference instrumentors, their own guides under Interoperability cover those exporters.