How do I track every tool call in OpenHands?
Rius tracks every OpenHands tool call once you set three OpenTelemetry environment variables, with no plugin and no code changes. Each conversation becomes one trace, and each tool call, such as terminal, file_editor or finish, is a span with its input and output. The OpenHands guide has the full setup, and Coding agents compares OpenHands with the other agents Rius traces.
How it works
The OpenHands SDK already sends OpenTelemetry. When it finds the OTLP variables in its environment, it turns tracing on by itself and sends the spans to Rius. There is no Rius code in your script.
One conversation is one trace. Inside it, each agent.step holds the model call for that step and the tool call it led to:
- A terminal call is a Tool span with the command as input and the output, including the exit code.
- A file edit is a Tool span for
file_editor, with its input and output. - The final
finishcall is a Tool span too. - Each model call sits beside its tool call, with model, tokens, cost, prompt and answer.
The conversation id becomes the Rius session, so a multi-turn conversation groups together. The agent name comes from the script name, or from agent.name metadata if you pass it.
Because every tool call is a Tool span, the Tool Loop alert can see them. It is on in every workspace and fires when the same tool is called with the same arguments at least three times in a row in one trace. That covers a loop that OpenHands itself finished normally.
Set it up
- In the Rius console, open Settings → API keys and create a key with the Send telemetry scope. The key is shown once.
- Set the three variables in the environment where the OpenHands SDK runs. The OpenHands guide covers the rest of the setup, including installing the SDK.
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://ingest.eu.console.rius-glassflow.com/v1/traces
export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer%20ri_..."- Get the details exactly right, because a mistake means Rius never sees the spans and the agent runs on as if nothing happened. Use
http/protobuf, since the exporter defaults to gRPC. Write the full/v1/tracespath. Write the space afterBeareras%20. Make sure noLMNR_*variable is set, because any of them takes precedence over the OTEL settings.env | grep LMNR_should print nothing. - Run your agent from its own directory. To name it, pass
observability_metadatawith anagent.namevalue when you create theConversation. To see each developer on the Users page, passuser_idthere too. - Open the trace list in the console. The conversation appears under your agent name within a few seconds. Open it to see each tool call in the span tree. In the Spans tab, the All kinds menu filters to the Tool kind, so you see only tool calls.
- Optional: attach a channel (Slack, email or a webhook) to the Tool Loop alert to get notified when an agent repeats the same call.
If you run the OpenHands web app in Docker, the variables do not reach the agent from the app container. Pass them in OH_AGENT_SERVER_ENV on the app container instead, as the guide shows.
What you get
- Every tool call in a conversation as its own span, with input and output, in the order the agent made them.
- The terminal command and its exit code for each shell call.
- Model calls beside the tool calls, with tokens, cost, prompt and answer, so you can see what the model saw before it chose a tool.
- Sessions that group multi-turn conversations, and a per-developer view on the Users page when you pass
user_id. - Tool Loop alerts for repeated identical calls, delivered to the channels you attach.
What OpenHands does not report:
- A failed command is not an error span. A non-zero exit code appears in the tool output, not in the span status, so error-rate alerts do not count it.
- Web-app conversations have every tool call and every model call with its prompt and answer, but no tokens or cost, because the web app streams its model calls.
- Models on the OpenAI Responses API, such as GPT-4.1 and GPT-5, carry no token data.
- OpenHands Cloud cannot send to Rius, because Cloud agents take no custom environment variables.
OpenHands always sends prompts and output. There is no per-folder opt-in as there is with the plugin.
When something else fits
- If you use Claude Code, Codex (beta) or Cursor (beta) instead, the Rius plugin traces tool calls there, and folders send content only when you opt in. For a team rollout, see Track Claude Code usage and cost across a team.
- If you run OpenHands Cloud, you cannot send its tool calls to Rius. Use the OpenHands SDK with the three variables instead.