5.6 KiB
Instrumentation guide
This document describes the recommended instrumentation contract for this
receiver. It is available at /instrumentation.md so frontend and backend
agents can read it before integrating telemetry.
Shared identity
Use the same values across all signals from one deployed application:
service.name: stable application name, for examplecheckout-web.service.release: immutable deploy identifier, for example2026.09.18-a1b2c3.service.environment:production,staging, ordevelopment.
The release must exactly match the source-map upload release.
Browser frontend
Load the RUM script and initialize it once, before the application starts:
<script src="https://otel.example.com/static/otel-rum.js"></script>
<script>
const rum = OtelRUM.init({
serviceName: "checkout-web",
release: "2026.09.18-a1b2c3",
environment: "production",
baseUrl: "https://otel.example.com"
});
</script>
The collector captures uncaught errors, unhandled promise rejections, failed HTTP requests, breadcrumbs, Web Vitals, and optional trace context. Correlate frontend requests with backend spans when possible:
fetch("/api/orders", { headers: rum.traceHeaders() });
For manually handled errors:
try {
riskyOperation();
} catch (error) {
rum.captureException(error, { "feature.name": "checkout" });
}
Do not put secrets, tokens, or sensitive customer data in breadcrumbs or custom attributes.
React error boundaries
Rendering errors are not reliably caught by global window.onerror handlers.
Report them from an error boundary and render a recovery screen:
import React from "react";
export class AppErrorBoundary extends React.Component<
{ children: React.ReactNode },
{ error: Error | null }
> {
state = { error: null as Error | null };
static getDerivedStateFromError(error: Error) {
return { error };
}
componentDidCatch(error: Error, info: React.ErrorInfo) {
window.OtelRUM?.last?.captureException(error, {
"exception.type": "ReactErrorBoundary",
"react.component_stack": info.componentStack,
});
}
render() {
if (this.state.error) {
return <button onClick={() => this.setState({ error: null })}>Retry</button>;
}
return this.props.children;
}
}
Use the same pattern in your application, including a user-visible fallback for individual views and the application root.
Backend instrumentation
Instrument backend services with an OpenTelemetry SDK or auto-instrumentation and export OTLP to this receiver:
- OTLP/gRPC:
otel.example.com:4317 - OTLP/HTTP traces:
https://otel.example.com/v1/traces - OTLP/HTTP metrics:
https://otel.example.com/v1/metrics - OTLP/HTTP logs:
https://otel.example.com/v1/logs
Set the resource attributes on every provider/exporter:
service.name=checkout-api
service.release=2026.09.18-a1b2c3
service.environment=production
Create spans around inbound requests, database calls, external HTTP calls, queue operations, and important business operations. Record HTTP status, route, method, and exception details. Mark failed spans with an error status. The receiver derives backend issues from error spans and ERROR/FATAL logs.
If HTTP ingest authentication is enabled, provide OTEL_INGEST_KEY as the
X-Otel-Ingest-Key header or key query parameter. Keep that key server-side;
browser clients should not receive it.
For non-interactive agents that need to query protected debug endpoints, use a
separate OTEL_AGENT_TOKEN service token. Send it as
X-Otel-Agent-Token: $OTEL_AGENT_TOKEN; do not put it in browser code or
telemetry payloads.
Source maps
Build the frontend with the same immutable release used by RUM. The command
and output directory depend on your build tool; the example below assumes a
standard dist/ output:
export RELEASE="2026.09.18-a1b2c3"
VITE_APP_RELEASE="$RELEASE" npm run build
After deployment, upload the contents of each generated JavaScript map to the
public multipart endpoint. No repository checkout or upload script is needed.
source must match the generated bundle URL/path captured in the browser
stack, while file=@... reads the local source-map file contents:
curl --fail-with-body --silent --show-error \
-X POST "https://otel.example.com/api/sourcemaps" \
-F "release=$RELEASE" \
-F "source=/assets/app-HASH.js" \
-F "file=@dist/assets/app-HASH.js.map;type=application/json"
For example, if the deployed JavaScript is available at
https://app.example.com/assets/main-abc123.js, upload its matching map with
source=/assets/main-abc123.js. The source-map filename on the build machine
does not need to match the URL, but the source field must.
Maps are parsed and retained server-side. Issue responses include a resolved
stack when service.release and the generated bundle path match. Original
source files and map contents are never returned by the API.
The Docker build removes .map files from production images by default. For
development or staging, public maps can be enabled for Chrome DevTools:
PUBLIC_SOURCEMAPS=true ./build.sh
Public maps expose application source code and should not be enabled in production unless that is intentional.
Verification checklist
- Trigger a controlled frontend exception.
- Trigger a controlled backend error span or ERROR log.
- Confirm both appear under Issues as separate frontend/backend issues.
- Confirm the issue sample contains the expected release.
- Confirm a mapped frontend stack shows original file and line information.
- Confirm trace IDs connect frontend requests to backend traces where headers were propagated.