Node and Typescript SDKs to emit request logs to Stainless X-ray.
npm add @stainlessdev/xray-emitterThen using it in Express, for example:
import express from 'express';
import { createEmitter } from '@stainlessdev/xray-emitter/express';
import { getXrayContext } from '@stainlessdev/xray-emitter/node';
const app = express();
const xray = createEmitter({ serviceName: 'my-service' });
app.use(xray);
app.use((req, _res, next) => {
const ctx = getXrayContext(req);
ctx?.setActor('tenant-123', 'user-123');
next();
});
app.get('/', (_req, res) => {
res.send('ok');
});| Framework | Import | Docs | Example |
|---|---|---|---|
| Express | @stainlessdev/xray-emitter/express |
README | example |
| Fastify | @stainlessdev/xray-emitter/fastify |
README | example |
| Hono | @stainlessdev/xray-emitter/hono |
README | example |
| Next.js | @stainlessdev/xray-emitter/next |
README | example |
| Remix | @stainlessdev/xray-emitter/remix |
README | example |
| Effect | @stainlessdev/xray-emitter/effect |
README | example |
Lower-level adapters:
| Adapter | Import | Docs | Example |
|---|---|---|---|
Node.js (node:http) |
@stainlessdev/xray-emitter/node |
README | example |
| Fetch / Edge | @stainlessdev/xray-emitter/fetch |
README | example |
| Core | @stainlessdev/xray-emitter |
README | — |
X-ray does not read standard OTEL environment variables. Configure an endpoint by passing
endpointUrl or setting STAINLESS_XRAY_ENDPOINT_URL. If both are set, endpointUrl wins. An
error is thrown if no endpoint is configured.
The core module (@stainlessdev/xray-emitter) is runtime-agnostic; use it only if you supply a
custom exporter instance to createEmitter.
X-ray resolves request IDs from response headers when the span is closed. Configure the header name with requestId.header (default: request-id, emitted as Request-Id). Resolution order is: explicit requestId on the normalized request → response header lookup → UUIDv7 when missing.
If the configured response header is missing when the response is finalized, X-ray will set it (using the explicit ID when provided, otherwise a generated UUIDv7). Existing response headers are not overwritten.
wrapFetch may replace the request/response objects while capturing bodies. wrapFetchPreserve keeps the original request/response whenever possible, but it may replace the response if it needs to inject a missing Request-Id header.
pnpm install
pnpm build
pnpm test