Not using OpenTelemetry yet? For JavaScript and TypeScript, the
@glassray/tracing SDK is quicker to set up. It sends to the same endpoint without any OTel setup.Setup
1
Create an API key
In Glassray, go to Settings → Sources and add an OpenTelemetry source. Copy the API key it shows you. You only see it once.Store it where your app can read it, for example as the environment variable
GLASSRAY_API_KEY.2
Set up OTel and point it at Glassray
Set up the OTel SDK once, when your app starts. Its exporter needs two things: Glassray’s URL, Before a script exits, call Prefer environment variables? You can leave the URL and key out of the code and set them as environment variables instead. The names are the same in every language:Then create the exporter without a URL or headers, for example
https://app.glassray.ai/api/public/otel/v1/traces, and the header Authorization: Bearer <your-api-key>.- Node.js
- Python
- Ruby
tracing.ts
await provider.shutdown(). In a serverless function, call await provider.forceFlush() at the end of each request. Otherwise the last batch of spans is lost.This exporter sends protobuf. @opentelemetry/exporter-trace-otlp-http sends JSON, and works the same way.new OTLPTraceExporter() in Node.js or OTLPSpanExporter() in Python. You still need the rest of the setup code above.Want the same traces in another tool too? See Send traces to more than one tool.3
Tag your traces
Tags go on two kinds of span:Nested spans don’t need a parent passed in. A span you start while another is active becomes its child, even if the code is in another function or file.For prompt-cache tokens, a cost you already know, or other providers’ token conventions, see Usage and cost attributes.GenAI instrumentation libraries such as OpenLIT, OpenLLMetry and OpenInference build on OTel and can add model and token attributes to your LLM spans. They don’t add Glassray’s tags. See What Glassray reads from your spans for the full list.
- The root span, one per run. It says who the run was for and what it was asked.
glassray.agentis already set for the whole app by the setup code in the previous step. - A child span for each step, such as an LLM call. It says which model ran, how many tokens it used, and what went in and came out.
- Node.js
- Python
- Ruby
This step always needs code. Environment variables and a Collector only change where traces are sent. They can’t add tags that change per run, like
glassray.customer, because only your app knows which customer a run is for.4
Check that traces arrive
Run your app so it creates at least one trace, then open Traces in Glassray. If nothing shows up, check your exporter’s logs for one of the error codes.
Endpoint reference
- Format: OTLP over HTTP, as protobuf (
Content-Type: application/x-protobuf) or JSON (Content-Type: application/json). Most OTel SDKs send protobuf by default. Both work the same. - Compression: gzip (
Content-Encoding: gzip) or none. - gRPC: not supported.
- API key: must have the
traces:writepermission. Keys from an OpenTelemetry source have it.
What Glassray reads from your spans
Root-span attributes override resource attributes with the same name. Any other string, number or boolean attribute on the root span or the resource (such as
report_id) becomes a filter in the Traces view. Attributes on child spans don’t, and neither do OTel namespaces such as service.* or http.*. Glassray keeps up to 48 of these per trace, with values up to 256 characters. For the full list, including prompt-cache tokens and cost, see Bring your own OTel.
How ingest behaves
Success
A
200 means the batch was received. The response uses the same Content-Type as the request.Safe to retry
Sending the same spans again doesn’t create duplicates.
Batches are combined
Spans from the same trace sent in separate requests are merged into one trace. If your source has filters, see Limits.
Limits
- Requests larger than 32 MB are rejected with
413. - A single trace larger than 16 MiB is skipped. The rest of the batch is still stored.
- If your source has filters, traces that don’t match them aren’t stored. The request still returns
200. - Filters are checked on each request on its own. When an exporter sends a long run in several requests, a request that only has child spans is matched on a child span’s name. With a filter on the trace name, those child spans can be dropped before the root span arrives.
Error codes
Send traces to more than one tool
If you want the same traces in Glassray and another tool, for example Langfuse, run an OTel Collector. It’s a separate service that receives traces from your apps and forwards them to several tools at once.otel-collector-config.yaml
http://localhost:4318/v1/traces, with no API key. The Collector adds each tool’s key when it forwards traces.
Use the otlphttp exporter, not the gRPC otlp exporter: Glassray only accepts HTTP.