How do I attach an end user to a trace with OpenInference instrumentors?
Rius reads the end user from the user.id attribute on each span, so you attach a user by setting that string on every span of the trace. The exporter wiring for OpenInference instrumentors sets no user, so you add user.id yourself, root span included. A span with no user attribute counts as anonymous on the Users page.
How it works
- Rius groups traces by user from the
user.idattribute. This is the key OpenInference uses for users, and Rius stores it as sent. - If
user.idis missing, Rius falls back toenduser.id, then toenduser.pseudo.id. A span with none of the three is anonymous. - Rius reads the user per span, not per trace. Set the value on every span in scope, not only the root, or the spans without it count as anonymous.
- The exporter wiring for OpenInference instrumentors, shown in step 1 below, carries no user setting. There is also no environment variable for the user, by design.
- Rius normalizes the other OpenInference attributes (model, tokens, messages) onto
gen_ai.*keys at ingest. The user needs no such mapping, becauseuser.idis already the key Rius reads. - The Users page then lists each end user your agents served, with their traces, failures, cost and time to first token.
Set it up
- Wire the instrumentor to the Rius ingest endpoint. This is the standard OpenInference setup for Rius, and it raises the span attribute limit to 4096 so a long span keeps all its attributes, including the user. For the full wiring, see the OpenInference page.
from opentelemetry.sdk.trace import SpanLimits, TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from openinference.instrumentation.openai import OpenAIInstrumentor
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>"},
)
)
)
OpenAIInstrumentor().instrument(tracer_provider=provider)- Pick an identifier for the person your request serves. Use an opaque id, such as an internal user id, in preference to an email address.
- Set that value as the string attribute
user.idon every span of the request, including the root span. Wrap the request in OpenInference’susing_user(user_id)fromopeninference-instrumentation, which stampsuser.idon the spans OpenInference creates. A root span you start from a plain OpenTelemetry tracer misses it, so set the attribute on that span yourself. The trace list shows each trace under the user of its root span.
from openinference.instrumentation import using_user
tracer = provider.get_tracer(__name__)
with using_user(user_id):
with tracer.start_as_current_span("handle-turn", attributes={"user.id": user_id}):
run_agent(message) # your agent's own code- If you also group conversations, set
session.idon every span in the same way. Without it, each trace is its own session. - Send one request, open the trace in the console, and check that the user appears on the Users page. A trace that shows as anonymous means
user.id,enduser.idandenduser.pseudo.idwere all missing on the spans Rius read.
For the full list of attributes Rius reads, see the span attribute reference. The general wiring options for any OpenTelemetry sender are in Trace LLM calls with OpenTelemetry.
What you get
- Each end user appears on the Users page with their traces, failures, cost and time to first token.
- Cost per user is available because Rius computes cost on the model-call spans from the model and token counts. You never send a cost attribute.
- Traces from the same person group together, and traces without a user stay visible as anonymous rather than disappearing.
- Because
user.idis stored as sent, you can search and filter on that exact key in the trace list. - The user rides alongside the model, token and message data that Rius normalizes from the OpenInference attributes, so one trace answers both what happened and who it happened to.
When something else fits
- If your app is Python or TypeScript and you can use the Rius SDK, it ships the same OpenInference instrumentors as integrations. There you set the user with
rius.userin Python orwithUserin TypeScript, and the SDK stampsuser.idfor you. - If your spans already carry
enduser.idorenduser.pseudo.id, you do not need to rename them. Rius reads those keys whenuser.idis absent. - If you send through a Collector, make sure no processor strips the user attribute on the way.
Related
Last updated on