Skip to content

WebSocket Long Connection Tracking

# WebSocket Persistent Connection Collection

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

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

## Enable Collection

### NPM


```javascript
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 package 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 above example uses the public OpenWay. When connecting directly via DataKit, replace site and clientToken with datakitOrigin, and do not configure both reporting addresses simultaneously.

enableExperimentalFeatures must be an array. Writing it directly as a string "track_websockets" will not enable collection.

Initialization Timing

RUM must be initialized before the business creates WebSocket connections:

datafluxRum.init({
  // other configurations
  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 the cached native WebSocket constructor before initialization;
  • Connections created within Web Workers or Service Workers;
  • Other transport implementations that do not go through window.WebSocket;
  • Connections created by WebSocketStream.

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

Collection Model

Each WebSocket Session segment generates one RUM Resource:

type = resource
resource.type = websocket

Resources are generated at the following times:

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

WebSocket is a persistent connection. While the connection is still open, it is normal that no final RUM Resource appears in the Network; the SDK does not periodically report connection snapshots.

When the page is refreshed, closed, or navigated away, the SDK will attempt to settle and send data during the beforeunload phase. If the page process is forcibly 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

When the browser receives close:

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 still open are settled based on 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 not exist. After a new session is established, the SDK will start a new statistical segment for the same physical connection, retaining the same connection_id, and resetting message counts and segment duration.

Messages between session expiration and the next renewal are not counted in any session. Connections created during this period and still open at renewal time will start being counted 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 the browser's close event, so the close fields may not exist.

Handshake Failure

When a connection enters close without ever triggering open:

resource.websocket.handshake_succeeded = false

In this case, setup_duration represents the time from connection construction to close or session settlement, not the successful handshake duration. The browser typically uses close code 1006 to indicate abnormal closure, and the specific value depends on the browser event.

Message Statistics

The SDK only counts message counts and byte sizes, and does not collect message bodies:

Message Type Byte Calculation Method
string UTF-8 byte count
ArrayBuffer byteLength
TypedArray, DataView Current view's byteLength
Blob size
Unrecognized types 0

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

View Attribution

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

  • start_view_id: The View where the current segment started;
  • end_view_id: The View where the connection closed or settled.

When a segment spans pages, these two IDs can be different. The Resource still enters the RUM event pipeline based on the segment start time.

Reported Fields

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

Basic Resource Fields

beforeSend path intake field description unit
resource.type resource_type Fixed as websocket -
resource.url resource_url 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 Current session segment duration ns

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 Current segment start time Unix ms
end_time resource_websocket_end_time Close or settlement time Unix ms
start_view_id resource_websocket_start_view_id View ID at segment start -
end_view_id resource_websocket_end_view_id View ID at connection end -
tracking_end_reason resource_websocket_tracking_end_reason close_event, session_end, or page_exit -
protocol resource_websocket_protocol Server-negotiated sub-protocol -
setup_duration resource_websocket_setup_duration First segment: time from construction to open; 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 inbound message bytes byte
messages_out.count resource_websocket_messages_out_count Number of successful send() calls count
messages_out.size resource_websocket_messages_out_size Total outbound message bytes byte
time_to_first_message_in resource_websocket_time_to_first_message_in Time from open to first inbound message ns
time_to_first_message_out resource_websocket_time_to_first_message_out Time from open to first outbound message ns
last_message_in_at resource_websocket_last_message_in_at Last inbound message time 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 last inbound message to close or settlement ns
buffered_amount_max resource_websocket_buffered_amount_max Peak bufferedAmount observed before each send() call byte

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

Close Fields

resource.websocket.* intake field description
close_code resource_websocket_close_code Close code from the browser's CloseEvent
close_reason resource_websocket_close_reason Close reason
was_clean resource_websocket_was_clean Whether the browser considers the connection closed cleanly

These fields may not exist when settling due to session expiration or page unload.

Using beforeSend

You can inspect, supplement, or filter WebSocket Resources:

datafluxRum.init({
  // other configurations
  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 used within 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 URL query parameters. Do not place passwords, long-term tokens, ID numbers, or other sensitive values in the URL.

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

As long as a third-party library calls window.WebSocket after RUM initialization, the underlying connections will be collected:

  • Each time auto-reconnect creates a new connection, a new connection_id and Resource are generated;
  • When a single connection multiplexes multiple business topics, the SDK only provides connection-level aggregation;
  • HTTP long polling phases are still collected as XHR or fetch Resources.

Verification

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

If the connection is never closed, 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.

Common Questions

No WebSocket Resource after configuration

Check the following in 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. Is the connection closed, or has the session expired?
  5. Did beforeSend return false?
  6. Was the connection created by a Worker?

Received WebSocket error but no Resource yet

The SDK generates the final event on close or session settlement, 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.

Cannot see HTTP status code

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

Feedback

Is this page helpful? ×