@splunk/otel-web
v2.3.0
Published
Splunk distribution of Open Telemetry for browser environment.
Readme
@splunk/otel-web
For complete instructions for how to get started with the Splunk distribution of OpenTelemetry JavaScript for Web, see Install the Browser RUM agent for Splunk RUM and Instrument browser-based web applications for Splunk RUM.
Splunk RUM for Browser collects performance metrics, web vitals, errors, and other forms of data for every user session to enable you to detect and troubleshoot problems in your application. For a complete view of your application from browser to back-end, integrate with Splunk APM.
🚀 Installation & Setup
Package Manager Installation
1. Install the Package
npm install @splunk/otel-web
# or
pnpm add @splunk/otel-web
# or
yarn add @splunk/otel-web2. Initialize RUM
import { SplunkRum } from '@splunk/otel-web'
SplunkRum.init({
realm: 'us1', // Your Splunk realm
rumAccessToken: 'YOUR_RUM_ACCESS_TOKEN', // RUM access token
applicationName: 'my-web-app', // Application identifier
deploymentEnvironment: 'production', // Environment (dev, staging, prod)
})CDN Installation
1. Load the Script
Choose a versioning strategy based on your needs:
Major Version Lock (Recommended)
<!-- Locks to v2.x.x - gets latest minor and patch updates -->
<script src="https://cdn.signalfx.com/o11y-gdi-rum/v2/splunk-otel-web.js" crossorigin="anonymous"></script>Minor Version Lock
<!-- Locks to v2.3.x - gets latest patch updates only -->
<script src="https://cdn.signalfx.com/o11y-gdi-rum/v2.3/splunk-otel-web.js" crossorigin="anonymous"></script>Exact Version Lock
<!-- Locks to exact version v2.3.0 - no automatic updates -->
<script
src="https://cdn.signalfx.com/o11y-gdi-rum/v2.3.0/splunk-otel-web.js"
crossorigin="anonymous"
integrity="sha384-<integrity>"
></script>Latest Version (Not Recommended)
<!-- Always pulls the latest released version -->
<script src="https://cdn.signalfx.com/o11y-gdi-rum/latest/splunk-otel-web.js" crossorigin="anonymous"></script>⚠️ Warning: Using
latestautomatically pulls the newest released version of the RUM agent, which may introduce breaking changes without notice. This can cause unexpected behavior in production. Use a version lock strategy instead.
📖 For version numbers and integrity hashes, see GitHub Releases.
📚 For detailed CDN setup instructions, see the official documentation.
2. Initialize RUM
<script>
SplunkRum.init({
realm: 'us1', // Your Splunk realm
rumAccessToken: 'YOUR_RUM_ACCESS_TOKEN', // RUM access token
applicationName: 'my-web-app', // Application identifier
deploymentEnvironment: 'production', // Environment (dev, staging, prod)
})
</script>Complete CDN Example
<!DOCTYPE html>
<html>
<head>
<title>My Web App</title>
<!-- Load Splunk RUM (using major version lock) -->
<script src="https://cdn.signalfx.com/o11y-gdi-rum/v1/splunk-otel-web.js" crossorigin="anonymous"></script>
<script>
SplunkRum.init({
realm: 'us1',
rumAccessToken: 'YOUR_RUM_ACCESS_TOKEN',
applicationName: 'my-web-app',
deploymentEnvironment: 'production',
debug: false, // Set to true for development
})
</script>
</head>
<body>
<!-- Your app content -->
</body>
</html>⚙️ Configuration Options
| Option | Type | Required | Default | Description |
| ------------------------------------- | ----------------------------------------------------- | -------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| realm | string | ✅ | - | Splunk realm (us0, us1, eu0, etc.) |
| rumAccessToken | string | ✅ | - | Publicly-visible RUM access token |
| applicationName | string | ❌ | 'unknown-browser-app' | Application name identifier |
| deploymentEnvironment | string | ❌ | - | Sets the environment attribute |
| version | string | ❌ | - | Sets the app.version attribute |
| beaconEndpoint | string | ❌ | - | Custom destination URL (overrides realm) |
| debug | boolean | ❌ | false | Enable internal debug logging |
| cookieDomain | string | ❌ | window.location.hostname | Domain for session cookies |
| ignoreUrls | Array<string\|RegExp> | ❌ | [] | URLs to exclude from tracing |
| globalAttributes | Attributes | ❌ | {} | Attributes added to every span |
| persistence | 'cookie'\|'localStorage' | ❌ | 'cookie' | Where to store session data |
| disableAutomationFrameworks | boolean | ❌ | false | Block automation frameworks |
| disableBots | boolean | ❌ | false | Block bots (Google bot, Bing bot, etc.) |
| user | { trackingMode: 'noTracking'\|'anonymousTracking' } | ❌ | - | user.trackingMode controls whether the agent creates and attaches an anonymous user ID to spans. In noTracking mode, no anonymous user ID is generated or stored. In anonymousTracking mode, the agent generates a persistent anonymous user ID (stored using the configured persistence method) and attaches it to spans to enable session and user correlation. |
| user.trackingMode | 'noTracking'\|'anonymousTracking' | ❌ | v1.x: 'noTracking'; v2+: 'anonymousTracking' | User tracking behavior |
| exporter.otlp | boolean | ❌ | false | Use OTLP format instead of Zipkin |
| exporter.onAttributesSerializing | function | ❌ | - | Transform attributes before export |
| privacy.maskAllText | boolean | ❌ | true | Mask all text from text nodes |
| privacy.sensitivityRules | Array<SensitivityRule> | ❌ | [] | Rules for text sensitivity by selector |
| Instrumentations | | | | |
| instrumentations.connectivity | boolean\|Config | ❌ | false | Network connectivity monitoring |
| instrumentations.document | boolean\|Config | ❌ | true | Document load instrumentation |
| instrumentations.errors | boolean\|Config | ❌ | true | Error capture |
| instrumentations.fetch | boolean\|Config | ❌ | true | Fetch API monitoring |
| instrumentations.frustrationSignals | boolean\|Config | ❌ | true | User frustration detection (rage clicks enabled by default, thrashed cursor opt-in). See Frustration Signals below |
| instrumentations.interactions | boolean\|Config | ❌ | true | User interaction tracking |
| instrumentations.longtask | boolean\|Config | ❌ | true | Long task detection (>50ms) |
| instrumentations.postload | boolean\|Config | ❌ | true | Post-load resource timing |
| instrumentations.socketio | boolean\|Config | ❌ | false | Socket.IO client monitoring |
| instrumentations.visibility | boolean\|Config | ❌ | false | Page visibility changes |
| instrumentations.webvitals | boolean\|Config | ❌ | true | Web Vitals collection |
| instrumentations.websocket | boolean\|Config | ❌ | false | WebSocket monitoring |
| instrumentations.xhr | boolean\|Config | ❌ | true | XMLHttpRequest monitoring |
Privacy Configuration
The privacy configuration allows you to control how text content is collected from user interactions:
maskAllText: Whentrue(default), all text from text nodes is masked unless an unmask rule appliessensitivityRules: Array of rules that determine text sensitivity based on CSS selectors. Rules are applied in order, with later rules overriding earlier ones
Rule Types:
mask: Mask text content in matching elementsunmask: Unmask text content in matching elementsexclude: Exclude matching elements from text collection entirely
Example:
privacy: {
maskAllText: true,
sensitivityRules: [
{ rule: 'unmask', selector: '.public-content' },
{ rule: 'exclude', selector: '.sensitive-data' },
{ rule: 'mask', selector: '.public-content .private-info' }
]
}Frustration Signals
The frustrationSignals instrumentation detects user frustration patterns and emits frustration spans. Rage click detection is enabled by default. Thrashed cursor detection is disabled by default and must be explicitly enabled.
Rage Clicks detect rapid repeated clicks on the same element, indicating the user is frustrated because the UI is unresponsive.
| Option | Type | Default | Description |
| ---------------------------- | ------------------------- | ------- | ---------------------------------------------- |
| rageClick | false \| object \| true | true | Set to false to disable rage click detection |
| rageClick.count | number | 4 | Number of clicks to trigger detection |
| rageClick.timeframeSeconds | number | 1 | Time window in seconds |
| rageClick.ignoreSelectors | string[] | [] | CSS selectors to exclude from detection |
Thrashed Cursor detects erratic back-and-forth mouse movements, indicating the user is confused or annoyed.
| Option | Type | Default | Description |
| -------------------------------------------- | ------------------------- | ------- | ------------------------------------------------------------------------------ |
| thrashedCursor | false \| object \| true | false | Set to true or an options object to enable thrashed cursor detection |
| thrashedCursor.timeWindowMs | number | 2000 | Analysis time window in milliseconds. Also used as cooldown between detections |
| thrashedCursor.throttleMs | number | 16 | Minimum interval between samples (min: 16ms) |
| thrashedCursor.minDirectionChanges | number | 4 | Minimum direction changes to consider |
| thrashedCursor.minDirectionChangeDegrees | number | 45 | Minimum angle change (degrees) to count as a direction change |
| thrashedCursor.minTotalDistance | number | 300 | Minimum total distance in pixels |
| thrashedCursor.minMovementDistance | number | 5 | Dead zone radius in pixels; movements smaller than this are ignored |
| thrashedCursor.minAverageVelocity | number | 300 | Minimum average velocity in px/s |
| thrashedCursor.maxVelocity | number | 5000 | Maximum velocity in px/s; samples above this are discarded as noise |
| thrashedCursor.maxConfinedAreaSize | number | 200 | Maximum bounding box size (px) for the confined-area score component |
| thrashedCursor.thrashingScoreThreshold | number | 0.6 | Score threshold (0–1) to trigger detection |
| thrashedCursor.scoreWeightDirectionChanges | number | 0.4 | Weight of direction-changes component in the thrashing score |
| thrashedCursor.scoreWeightVelocity | number | 0.3 | Weight of velocity component in the thrashing score |
| thrashedCursor.scoreWeightConfinedArea | number | 0.3 | Weight of confined-area component in the thrashing score |
| thrashedCursor.ignoreUrls | Array<string\|RegExp> | [] | URLs where detection is skipped |
Example:
instrumentations: {
frustrationSignals: {
rageClick: {
count: 4,
timeframeSeconds: 1,
ignoreSelectors: ['#interactive-canvas'],
},
thrashedCursor: {
thrashingScoreThreshold: 0.7,
ignoreUrls: [/\/game/, /\/drawing-tool/],
},
},
}To enable thrashed cursor detection while disabling rage clicks:
instrumentations: {
frustrationSignals: {
rageClick: false, // Disable rage click detection
thrashedCursor: true, // Enable thrashed cursor detection (disabled by default)
},
}Complete Configuration Example
import { SplunkRum } from '@splunk/otel-web'
SplunkRum.init({
// Required settings
realm: 'us1',
rumAccessToken: 'YOUR_RUM_ACCESS_TOKEN',
// Application identification
applicationName: 'my-web-app',
deploymentEnvironment: 'production',
version: '1.2.3',
cookieDomain: window.location.hostname,
persistence: 'cookie',
ignoreUrls: [/\/health-check/, '/analytics/track', 'https://third-party-ads.com'],
// Global attributes for all spans
globalAttributes: {
'team': 'frontend',
'feature.flag.checkout': 'enabled',
},
// Bot detection
disableAutomationFrameworks: true,
disableBots: true,
// User tracking
user: {
trackingMode: 'anonymousTracking',
},
// Privacy configuration
privacy: {
maskAllText: true, // Mask all text from text nodes by default
sensitivityRules: [
// Unmask text in specific elements
{ rule: 'unmask', selector: '.public-content' },
{ rule: 'unmask', selector: 'h1, h2, h3' },
// Exclude sensitive elements entirely
{ rule: 'exclude', selector: '.sensitive-data' },
// Override previous rules for specific cases
{ rule: 'mask', selector: '.public-content .private-info' },
],
},
// Export options
exporter: {
otlp: true, // Use OTLP instead of Zipkin
onAttributesSerializing: (attributes, span) => {
// Remove or hash sensitive data
if (attributes['http.url']) {
attributes['http.url'] = sanitizeUrl(attributes['http.url'])
}
return attributes
},
},
// Instrumentation control
instrumentations: {
// Core instrumentations (enabled by default)
document: true,
errors: true,
fetch: true,
frustrationSignals: {
rageClick: { count: 4 },
thrashedCursor: true, // Opt-in: disabled by default
},
interactions: true,
longtask: true,
postload: true,
webvitals: true,
xhr: true,
// Optional instrumentations (disabled by default)
connectivity: false,
socketio: false,
visibility: false,
websocket: false,
},
// Development
debug: process.env.NODE_ENV !== 'production',
})📚 API Reference
SplunkRum Class
Static Methods
| Method | Parameters | Returns | Description |
| ---------------------------- | ----------------- | -------- | -------------------------- |
| init(config) | SplunkRumConfig | void | Initialize the RUM SDK |
| setGlobalAttributes(attrs) | Attributes | void | Add global span attributes |
| getSessionId() | - | string | Get current session ID |
Properties
| Property | Type | Description |
| ---------- | ---------------- | ----------------------------- |
| provider | TracerProvider | OpenTelemetry tracer provider |
🛠️ Troubleshooting
For troubleshooting issues with the Splunk Distribution of OpenTelemetry JS for Web, see Troubleshoot browser instrumentation for Splunk Observability Cloud in the official documentation.
📜 License
Licensed under the Apache License, Version 2.0. See LICENSE for the full license text.
ℹ️ SignalFx was acquired by Splunk in October 2019. See Splunk SignalFx for more information.
