Skip to content

WebSocket Long-Lived Connection Collection

Starting from RUM SDK 3.3.6, native WebSocket connections in the browser can be aggregated as RUM Resources. This allows you to analyze handshake, message traffic, inbound idle, send backlog, and close state.

This feature is currently experimental and disabled by default. The SDK does not read or upload WebSocket message bodies.

Enabling Collection

NPM

import { datafluxRum } from "@cloudcare/browser-rum"

datafluxRum.init({
  applicationId: "<APPLICATION_ID>",
  site: "<PUBLIC_OPENWAY_URL>",
  clientToken: "<CLIENT_TOKEN>",
  service: "websocket-client",
  env: "production",
  version: "1.0.0",
  sessionSampleRate: 100,
  enableExperimentalFeatures: ["track_websockets"],
})

The slim RUM bundle uses the same configuration:

import { datafluxRum } from "@cloudcare/browser-rum-slim"

CDN

<script
  src="https://static.guance.com/browser-sdk/v3/dataflux-rum.js"
  type="text/javascript"
></script>
<script>
  window.DATAFLUX_RUM &&
    window.DATAFLUX_RUM.init({
      applicationId: "<APPLICATION_ID>",
      site: "<PUBLIC_OPENWAY_URL>",
      clientToken: "<CLIENT_TOKEN>",
      service: "websocket-client",
      env: "production",
      version: "1.0.0",
      sessionSampleRate: 100,
      enableExperimentalFeatures: ["track_websockets"],
    })
</script>

The example above uses the public OpenWay. When connecting directly via DataKit, replace site and clientToken with datakitOrigin. Do not configure both report endpoints at the same time.

enableExperimentalFeatures must be an array. Using a string "track_websockets" directly will not enable collection.

Initialization Timing

RUM must be initialized before the business creates any WebSocket connection:

datafluxRum.init({
  // other configuration
  enableExperimentalFeatures: ["track_websockets"],
})

const socket = new WebSocket("wss://example.com/socket")

The following connections will not be collected:

  • Connections created before RUM initialization;
  • Connections created using a cached original WebSocket constructor from before initialization;
  • Connections created inside a Web Worker or Service Worker;
  • Other transport implementations that do not go through window.WebSocket;
  • Connections created via WebSocketStream.

Enabling collection does not change the construction behavior of the native WebSocket, its static constants, instanceof checks, business event listeners, or the return behavior of send().

Collection Model

Each WebSocket session segment generates one RUM Resource:

type = resource
resource.type = websocket

A Resource is generated in the following situations:

  1. The browser receives a WebSocket close event;
  2. The current RUM Session expires;
  3. The page triggers beforeunload;
  4. The SDK stops the current collection instance.

WebSocket is a long-lived connection. While the connection remains open, it is normal that no final RUM Resource appears in the Network tab; the SDK does not periodically report connection snapshots.

When the page is refreshed, closed, or navigated away, the SDK attempts to finalize and send the data during the beforeunload phase. If the page process is forcefully terminated, the browser crashes, or the device loses power, JavaScript may not have a chance to execute, and the last connection Resource may still be lost.

Normal Close

After the browser receives a close event:

resource.websocket.tracking_end_reason = close_event

The event includes the close code, close reason, and was_clean.

Session Expiration

When a Session expires, connections that are still open are finalized with the current data:

resource.websocket.tracking_end_reason = session_end

The business WebSocket is not closed at this point, so the close code, close reason, and was_clean may be absent. After a new Session is established, the SDK starts a new statistics segment for the same physical connection, retains the same connection_id, and resets the message counts and segment duration.

Messages sent during the period between Session expiration and the start of the new Session are not counted in either Session. Connections created during this period and still open when the new Session begins are tracked from the renewal moment of the new Session.

Page Unload

When the page triggers beforeunload:

resource.websocket.tracking_end_reason = page_exit

The connection may not trigger a browser close event, so the close fields may be absent.

Handshake Failure

When a connection enters close without ever having triggered open:

resource.websocket.handshake_succeeded = false

In this case, setup_duration represents the time from connection construction to close or session finalization, not the time for a successful handshake. The browser typically uses close code 1006 for abnormal closures; the actual value depends on the browser event.

Message Statistics

The SDK only counts the number of messages and bytes; it does not capture message bodies:

Message Type Bytes Calculation
string UTF-8 byte count
ArrayBuffer byteLength
TypedArray, DataView byteLength of the current view
Blob size
Unrecognized types 0

For example, the string 你好 is counted as 6 bytes in UTF-8, not as the JavaScript string length of 2.

View Attribution

A WebSocket session segment may span multiple RUM Views. The Resource additionally records:

  • start_view_id: the View in which the current segment started;
  • end_view_id: the View in which the connection closed or was finalized;
  • end_view_path: the page path at the time of close or finalization (added in RUM SDK 3.3.8).

The page path at the start of the segment reuses the standard View context of the Resource: view.path in beforeSend, and the final intake field is view_path. When a segment spans pages, the start and end View IDs and paths can differ. The path contains only the URL pathname, not the query or hash. The Resource still enters the RUM event pipeline with the start time of the segment.

Reported Fields

In beforeSend, you can read event.resource.websocket; the final intake converts these fields to resource_websocket_*.

Basic Resource Fields

beforeSend path Intake field Description Unit
resource.type resource_type Fixed to websocket -
resource.url resource_url The browser-resolved ws:// or wss:// URL -
resource.url_host resource_url_host URL host -
resource.url_path resource_url_path URL path -
resource.url_query resource_url_query URL query parameters -
resource.duration duration Duration of the current session segment ns
view.path view_path Page path of the View in which the segment started -

WebSocket Resources have no HTTP response, so there is no resource_status, resource_method, TTFB, download size, or HTTP timing.

Connection Fields

resource.websocket.* Intake field Description Unit
connection_id resource_websocket_connection_id Unique physical connection ID, unchanged across session segments -
handshake_succeeded resource_websocket_handshake_succeeded Whether open was received boolean
start_time resource_websocket_start_time Start time of the current segment Unix ms
end_time resource_websocket_end_time Time of close or finalization Unix ms
start_view_id resource_websocket_start_view_id View ID at the start of the segment -
end_view_id resource_websocket_end_view_id View ID at the end of the connection -
end_view_path resource_websocket_end_view_path Page path at the time of close or finalization -
tracking_end_reason resource_websocket_tracking_end_reason close_event, session_end, or page_exit -
protocol resource_websocket_protocol Sub-protocol negotiated by the server -
setup_duration resource_websocket_setup_duration For the first segment: time from construction to open; for renewal segments: 0 ns

Message Fields

resource.websocket.* Intake field Description Unit
messages_in.count resource_websocket_messages_in_count Number of inbound messages count
messages_in.size resource_websocket_messages_in_size Total bytes of inbound messages byte
messages_out.count resource_websocket_messages_out_count Number of successful send() calls count
messages_out.size resource_websocket_messages_out_size Total bytes of outbound messages byte
time_to_first_message_in resource_websocket_time_to_first_message_in Time from open to the first inbound message ns
time_to_first_message_out resource_websocket_time_to_first_message_out Time from open to the first outbound message ns
last_message_in_at resource_websocket_last_message_in_at Timestamp of the last inbound message Unix ms
longest_inbound_silence resource_websocket_longest_inbound_silence Longest interval between consecutive inbound messages ns
inbound_idle_duration_before_close resource_websocket_inbound_idle_duration_before_close Time from the last inbound message to close or finalization ns
buffered_amount_max resource_websocket_buffered_amount_max Peak bufferedAmount observed before each send() call byte

buffered_amount_max is the peak sampled before calling send(), not a continuous monitoring value of the browser's send queue.

In the Resource Explorer, you can filter connection segments that started from a specific page using view_path. With RUM SDK 3.3.8 or later, you can also filter connection segments that ended on a specific page using resource_websocket_end_view_path.

Close Fields

resource.websocket.* Intake field Description
close_code resource_websocket_close_code Close code from the browser CloseEvent
close_reason resource_websocket_close_reason Close reason
was_clean resource_websocket_was_clean Whether the browser considers the connection cleanly closed

These fields may be absent when the session expires or the page unloads and the data is finalized.

Using beforeSend

You can inspect, enrich, or filter WebSocket Resources:

datafluxRum.init({
  // other configuration
  enableExperimentalFeatures: ["track_websockets"],
  beforeSend(event, domainContext) {
    if (
      event.type === "resource" &&
      event.resource?.type === "websocket"
    ) {
      event.context = {
        ...event.context,
        socket_channel: "notifications",
      }

      console.debug(
        "WebSocket completed",
        event.resource.websocket,
        domainContext?.webSocket
      )
    }

    return true
  },
})

The domainContext for a WebSocket Resource contains:

domainContext.isWebSocket = true
domainContext.webSocket = native WebSocket instance

domainContext is only available in the beforeSend callback and is not uploaded.

Returning false can discard a specific connection:

beforeSend(event) {
  if (
    event.type === "resource" &&
    event.resource?.type === "websocket" &&
    event.resource.url.includes("/health-stream")
  ) {
    return false
  }

  return true
}

Privacy and Security

The SDK collects the full WebSocket URL and parses the URL query. Do not place sensitive values such as passwords, long-lived tokens, or ID numbers in the URL.

Enabling collection does not bypass the page's CSP connect-src, server-side Origin validation, or other browser security policies, nor does it inject custom trace headers into the handshake request.

As long as third-party libraries call window.WebSocket after RUM initialization, the underlying connections are collected:

  • Each automatic reconnection that creates a new connection generates a new connection_id and Resource;
  • When a single connection is reused for multiple business topics, the SDK only provides connection-level aggregation;
  • HTTP long polling phases are still collected as XHR or fetch Resources.

Verifying Integration

  1. Open the browser developer tools and confirm that the business WebSocket is connected and producing messages.
  2. Manually execute socket.close(1000, "done").
  3. Filter the Network tab for /v1/write/rum.
  4. Look for resource_type=websocket in the request data.
  5. Check handshake_succeeded, message count, byte count, and close fields.

If the connection never closes, wait for the session to expire and check for tracking_end_reason=session_end. Refreshing the page should show tracking_end_reason=page_exit; this send is a best-effort report during the page exit phase.

Frequently Asked Questions

No WebSocket Resource after configuration

Check in the following order:

  1. Is enableExperimentalFeatures an array containing "track_websockets"?
  2. Is RUM initialized before the connection is created?
  3. Does the current session hit the sessionSampleRate?
  4. Has the connection already closed, or has the session expired?
  5. Did beforeSend return false?
  6. Is the connection created by a Worker?

Received a WebSocket error but no Resource yet

The SDK generates the final event on close or session finalization, not on the error event alone.

handshake_succeeded=false

The browser did not trigger open. Check the WebSocket URL, TLS certificate, CSP connect-src, reverse proxy Upgrade configuration, server-side Origin validation, and authentication.

Message size is 0

Confirm that the data type is string, ArrayBuffer, TypedArray, DataView, or Blob. The SDK does not serialize arbitrary objects to estimate size.

No HTTP status code visible

The browser WebSocket API does not expose the handshake HTTP status code to the page, so WebSocket Resources do not have a resource_status.

Feedback

Is this page helpful?