From 2827ee224eb9b44c44542847a32057b94748888e Mon Sep 17 00:00:00 2001 From: Boris Dibon Date: Thu, 13 Aug 2026 11:45:21 +0200 Subject: [PATCH 1/2] Add WebSocket vital event schemas Report WebSocket connection metrics as a stream of vital events, one per connection phase, all sharing a `vital.websocket.id`. Adds one schema per phase, discriminated by a const on `vital.name` over the shared `vital.type` const `websocket`, following the pattern the timeseries event schemas already use. Registered in `rum-events-schema.json` and `rum-events-browser-schema.json` only: the browser SDK is the only SDK reporting these for now. Co-Authored-By: Claude Opus 5 (1M context) --- lib/cjs/generated/rum.d.ts | 254 +++++++++++++++++- lib/esm/generated/rum.d.ts | 254 +++++++++++++++++- .../vital-websocket-closed-never-opened.json | 35 +++ .../vital-websocket-closed-session-end.json | 51 ++++ .../rum-events/vital-websocket-closed.json | 56 ++++ .../rum-events/vital-websocket-closing.json | 30 +++ .../vital-websocket-connecting.json | 31 +++ samples/rum-events/vital-websocket-open.json | 52 ++++ schemas/rum-events-browser-schema.json | 12 + schemas/rum-events-schema.json | 12 + .../rum/_vital-websocket-common-schema.json | 44 +++ ...al-websocket-message-direction-schema.json | 47 ++++ .../rum/_vital-websocket-snapshot-schema.json | 48 ++++ .../rum/vital-websocket-closed-schema.json | 83 ++++++ .../rum/vital-websocket-closing-schema.json | 50 ++++ .../vital-websocket-connecting-schema.json | 58 ++++ schemas/rum/vital-websocket-open-schema.json | 83 ++++++ 17 files changed, 1198 insertions(+), 2 deletions(-) create mode 100644 samples/rum-events/vital-websocket-closed-never-opened.json create mode 100644 samples/rum-events/vital-websocket-closed-session-end.json create mode 100644 samples/rum-events/vital-websocket-closed.json create mode 100644 samples/rum-events/vital-websocket-closing.json create mode 100644 samples/rum-events/vital-websocket-connecting.json create mode 100644 samples/rum-events/vital-websocket-open.json create mode 100644 schemas/rum/_vital-websocket-common-schema.json create mode 100644 schemas/rum/_vital-websocket-message-direction-schema.json create mode 100644 schemas/rum/_vital-websocket-snapshot-schema.json create mode 100644 schemas/rum/vital-websocket-closed-schema.json create mode 100644 schemas/rum/vital-websocket-closing-schema.json create mode 100644 schemas/rum/vital-websocket-connecting-schema.json create mode 100644 schemas/rum/vital-websocket-open-schema.json diff --git a/lib/cjs/generated/rum.d.ts b/lib/cjs/generated/rum.d.ts index dde1ceab..ba96fd98 100644 --- a/lib/cjs/generated/rum.d.ts +++ b/lib/cjs/generated/rum.d.ts @@ -895,7 +895,7 @@ export type RumViewUpdateEvent = ViewContainerSchema & StreamSchema & ViewProper }; [k: string]: unknown; }; -export type RumVitalEvent = RumVitalDurationEvent | RumVitalOperationStepEvent | RumVitalAppLaunchEvent; +export type RumVitalEvent = RumVitalDurationEvent | RumVitalOperationStepEvent | RumVitalAppLaunchEvent | RumVitalWebsocketConnectingEvent | RumVitalWebsocketOpenEvent | RumVitalWebsocketClosingEvent | RumVitalWebsocketClosedEvent; /** * Schema for a duration vital event. */ @@ -1021,6 +1021,200 @@ export type RumVitalAppLaunchEvent = RumVitalEventCommonProperties & { }; [k: string]: unknown; }; +/** + * Schema for the vital reported when a WebSocket connection is created. Reported exactly once per connection, when the WebSocket is constructed + */ +export type RumVitalWebsocketConnectingEvent = RumVitalWebsocketCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Name of the vital, identifying the phase of the connection it reports + */ + readonly name: 'websocket_connecting'; + /** + * WebSocket properties. The identity of the connection is reported on this vital only, and is not repeated on the vitals of the later phases + */ + readonly websocket?: { + /** + * URL the connection was opened on, without its query string + */ + readonly url: string; + /** + * Subprotocols the connection was requested with. Omitted when none was requested + */ + readonly requested_protocols?: string[]; + /** + * Date the WebSocket was constructed, in ms from epoch + */ + readonly connecting_date: number; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; +/** + * Schema of common properties for a WebSocket vital event. One connection reports a stream of vitals over its lifetime, one per phase, all sharing the same vital.websocket.id + */ +export type RumVitalWebsocketCommonProperties = RumVitalEventCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Type of the vital. + */ + readonly type: 'websocket'; + /** + * WebSocket properties + */ + readonly websocket: { + /** + * UUID of the WebSocket connection, stable across every vital reported for that connection. It is not vital.id, which identifies a single vital + */ + readonly id: string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; +/** + * Schema for the vital reported when a WebSocket connection opens, then repeatedly as a heartbeat while it stays open + */ +export type RumVitalWebsocketOpenEvent = RumVitalWebsocketCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Name of the vital, identifying the phase of the connection it reports + */ + readonly name: 'websocket_open'; + /** + * WebSocket properties + */ + readonly websocket?: { + /** + * Whether the opening handshake succeeded + */ + readonly open_handshake_succeeded: boolean; + /** + * Interval between the connecting date and the open date of the connection, in nanoseconds + */ + readonly connecting_duration: number; + /** + * Date the connection opened, in ms from epoch + */ + readonly open_date: number; + /** + * Subprotocol the server selected. Omitted when the server selected none + */ + readonly selected_protocol?: string; + /** + * Extensions the server negotiated, as the single string the connection exposes them as. Omitted when none was negotiated + */ + readonly selected_extensions?: string; + /** + * Version of the snapshot, incremented on every vital reporting one, so that heartbeats can be ordered when they are received out of sequence + */ + readonly snapshot_version: number; + /** + * Values of the connection at the time the vital was reported + */ + readonly snapshot: RumVitalWebsocketSnapshot; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; +/** + * Schema for the vital reported when a WebSocket connection starts closing. Reported at most once per connection + */ +export type RumVitalWebsocketClosingEvent = RumVitalWebsocketCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Name of the vital, identifying the phase of the connection it reports + */ + readonly name: 'websocket_closing'; + /** + * WebSocket properties + */ + readonly websocket?: { + /** + * Date the connection was asked to close, in ms from epoch + */ + readonly closing_date: number; + /** + * Side that initiated the close. Only reported on this vital, and only as client, since a close observed from the application is always initiated by it. The other values are reported by the connection-level data derived from these vitals + */ + readonly close_initiator: 'client' | 'server' | 'unknown'; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; +/** + * Schema for the vital reported when a WebSocket connection stops being tracked, whether because it closed or because tracking ended first. Reported at most once per connection + */ +export type RumVitalWebsocketClosedEvent = RumVitalWebsocketCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Name of the vital, identifying the phase of the connection it reports + */ + readonly name: 'websocket_closed'; + /** + * WebSocket properties + */ + readonly websocket?: { + /** + * Date tracking of the connection ended, in ms from epoch. It is the date the connection closed when the tracking end reason is close_event, and the date tracking stopped otherwise + */ + readonly closed_date: number; + /** + * Interval between the connecting date and the closed date of the connection, in nanoseconds. It is not vital.duration, which is reported by duration vitals + */ + readonly duration: number; + /** + * Why tracking of the connection ended + */ + readonly tracking_end_reason: 'close_event' | 'session_end' | 'page_unloaded'; + /** + * Code the connection was closed with. Reported when, and only when, the tracking end reason is close_event + */ + readonly close_code?: number; + /** + * Reason the connection was closed with, as an empty string when the other side supplied none. Reported when, and only when, the tracking end reason is close_event + */ + readonly close_reason?: string; + /** + * Whether the connection closed cleanly. Reported when, and only when, the tracking end reason is close_event + */ + readonly was_clean?: boolean; + /** + * Version of the snapshot, incremented on every vital reporting one, so that heartbeats can be ordered when they are received out of sequence + */ + readonly snapshot_version: number; + /** + * Values of the connection when tracking ended. Omitted when the connection never opened + */ + readonly snapshot?: RumVitalWebsocketSnapshot; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; /** * Schema of common properties of RUM events */ @@ -2283,3 +2477,61 @@ export interface ViewAccessibilityProperties { readonly rtl_enabled?: boolean; [k: string]: unknown; } +/** + * Schema of the WebSocket connection values that change from one vital of a connection to the next. Both directions are present whenever the snapshot is, and are zero-filled while no message has been observed + */ +export interface RumVitalWebsocketSnapshot { + /** + * Messages received from the server + */ + readonly inbound: RumVitalWebsocketMessageDirection; + /** + * Messages sent to the server + */ + readonly outbound: RumVitalWebsocketMessageDirection & { + /** + * Largest amount of bytes ever observed queued in the send buffer, sampled after each send is enqueued + */ + readonly buffered_amount_max: number; + /** + * Number of sends issued while the send buffer was already deep, that is while it held at least 65536 bytes right before the send + */ + readonly backpressured_message_count: number; + /** + * Amount of bytes still queued in the send buffer when tracking ended, in bytes. Set on the websocket_closed vital only + */ + readonly buffered_amount_at_close?: number; + [k: string]: unknown; + }; + [k: string]: unknown; +} +/** + * Schema of the messages observed in one direction of a WebSocket connection + */ +export interface RumVitalWebsocketMessageDirection { + /** + * Number of messages observed in this direction since the connection opened + */ + readonly message_count: number; + /** + * Sum of the payload sizes of the messages observed in this direction, in bytes + */ + readonly message_size_total: number; + /** + * Size of the largest single payload observed in this direction, in bytes. 0 while no message has been observed + */ + readonly message_size_max: number; + /** + * Longest interval between two consecutive messages observed in this direction, in nanoseconds, including the still-open interval since the last message. 0 until two messages have been observed. The interval before the first message is excluded from it, as it is reported by time_to_first_message + */ + readonly longest_silence: number; + /** + * Interval between the open date of the connection and the first message observed in this direction, in nanoseconds. Omitted until a first message has been observed + */ + readonly time_to_first_message?: number; + /** + * Interval between the last message observed in this direction and the closed date of the connection, in nanoseconds. Set on the websocket_closed vital only, and only when at least one message has been observed in this direction + */ + readonly silence_before_close?: number; + [k: string]: unknown; +} diff --git a/lib/esm/generated/rum.d.ts b/lib/esm/generated/rum.d.ts index dde1ceab..ba96fd98 100644 --- a/lib/esm/generated/rum.d.ts +++ b/lib/esm/generated/rum.d.ts @@ -895,7 +895,7 @@ export type RumViewUpdateEvent = ViewContainerSchema & StreamSchema & ViewProper }; [k: string]: unknown; }; -export type RumVitalEvent = RumVitalDurationEvent | RumVitalOperationStepEvent | RumVitalAppLaunchEvent; +export type RumVitalEvent = RumVitalDurationEvent | RumVitalOperationStepEvent | RumVitalAppLaunchEvent | RumVitalWebsocketConnectingEvent | RumVitalWebsocketOpenEvent | RumVitalWebsocketClosingEvent | RumVitalWebsocketClosedEvent; /** * Schema for a duration vital event. */ @@ -1021,6 +1021,200 @@ export type RumVitalAppLaunchEvent = RumVitalEventCommonProperties & { }; [k: string]: unknown; }; +/** + * Schema for the vital reported when a WebSocket connection is created. Reported exactly once per connection, when the WebSocket is constructed + */ +export type RumVitalWebsocketConnectingEvent = RumVitalWebsocketCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Name of the vital, identifying the phase of the connection it reports + */ + readonly name: 'websocket_connecting'; + /** + * WebSocket properties. The identity of the connection is reported on this vital only, and is not repeated on the vitals of the later phases + */ + readonly websocket?: { + /** + * URL the connection was opened on, without its query string + */ + readonly url: string; + /** + * Subprotocols the connection was requested with. Omitted when none was requested + */ + readonly requested_protocols?: string[]; + /** + * Date the WebSocket was constructed, in ms from epoch + */ + readonly connecting_date: number; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; +/** + * Schema of common properties for a WebSocket vital event. One connection reports a stream of vitals over its lifetime, one per phase, all sharing the same vital.websocket.id + */ +export type RumVitalWebsocketCommonProperties = RumVitalEventCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Type of the vital. + */ + readonly type: 'websocket'; + /** + * WebSocket properties + */ + readonly websocket: { + /** + * UUID of the WebSocket connection, stable across every vital reported for that connection. It is not vital.id, which identifies a single vital + */ + readonly id: string; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; +/** + * Schema for the vital reported when a WebSocket connection opens, then repeatedly as a heartbeat while it stays open + */ +export type RumVitalWebsocketOpenEvent = RumVitalWebsocketCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Name of the vital, identifying the phase of the connection it reports + */ + readonly name: 'websocket_open'; + /** + * WebSocket properties + */ + readonly websocket?: { + /** + * Whether the opening handshake succeeded + */ + readonly open_handshake_succeeded: boolean; + /** + * Interval between the connecting date and the open date of the connection, in nanoseconds + */ + readonly connecting_duration: number; + /** + * Date the connection opened, in ms from epoch + */ + readonly open_date: number; + /** + * Subprotocol the server selected. Omitted when the server selected none + */ + readonly selected_protocol?: string; + /** + * Extensions the server negotiated, as the single string the connection exposes them as. Omitted when none was negotiated + */ + readonly selected_extensions?: string; + /** + * Version of the snapshot, incremented on every vital reporting one, so that heartbeats can be ordered when they are received out of sequence + */ + readonly snapshot_version: number; + /** + * Values of the connection at the time the vital was reported + */ + readonly snapshot: RumVitalWebsocketSnapshot; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; +/** + * Schema for the vital reported when a WebSocket connection starts closing. Reported at most once per connection + */ +export type RumVitalWebsocketClosingEvent = RumVitalWebsocketCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Name of the vital, identifying the phase of the connection it reports + */ + readonly name: 'websocket_closing'; + /** + * WebSocket properties + */ + readonly websocket?: { + /** + * Date the connection was asked to close, in ms from epoch + */ + readonly closing_date: number; + /** + * Side that initiated the close. Only reported on this vital, and only as client, since a close observed from the application is always initiated by it. The other values are reported by the connection-level data derived from these vitals + */ + readonly close_initiator: 'client' | 'server' | 'unknown'; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; +/** + * Schema for the vital reported when a WebSocket connection stops being tracked, whether because it closed or because tracking ended first. Reported at most once per connection + */ +export type RumVitalWebsocketClosedEvent = RumVitalWebsocketCommonProperties & { + /** + * Vital properties + */ + readonly vital?: { + /** + * Name of the vital, identifying the phase of the connection it reports + */ + readonly name: 'websocket_closed'; + /** + * WebSocket properties + */ + readonly websocket?: { + /** + * Date tracking of the connection ended, in ms from epoch. It is the date the connection closed when the tracking end reason is close_event, and the date tracking stopped otherwise + */ + readonly closed_date: number; + /** + * Interval between the connecting date and the closed date of the connection, in nanoseconds. It is not vital.duration, which is reported by duration vitals + */ + readonly duration: number; + /** + * Why tracking of the connection ended + */ + readonly tracking_end_reason: 'close_event' | 'session_end' | 'page_unloaded'; + /** + * Code the connection was closed with. Reported when, and only when, the tracking end reason is close_event + */ + readonly close_code?: number; + /** + * Reason the connection was closed with, as an empty string when the other side supplied none. Reported when, and only when, the tracking end reason is close_event + */ + readonly close_reason?: string; + /** + * Whether the connection closed cleanly. Reported when, and only when, the tracking end reason is close_event + */ + readonly was_clean?: boolean; + /** + * Version of the snapshot, incremented on every vital reporting one, so that heartbeats can be ordered when they are received out of sequence + */ + readonly snapshot_version: number; + /** + * Values of the connection when tracking ended. Omitted when the connection never opened + */ + readonly snapshot?: RumVitalWebsocketSnapshot; + [k: string]: unknown; + }; + [k: string]: unknown; + }; + [k: string]: unknown; +}; /** * Schema of common properties of RUM events */ @@ -2283,3 +2477,61 @@ export interface ViewAccessibilityProperties { readonly rtl_enabled?: boolean; [k: string]: unknown; } +/** + * Schema of the WebSocket connection values that change from one vital of a connection to the next. Both directions are present whenever the snapshot is, and are zero-filled while no message has been observed + */ +export interface RumVitalWebsocketSnapshot { + /** + * Messages received from the server + */ + readonly inbound: RumVitalWebsocketMessageDirection; + /** + * Messages sent to the server + */ + readonly outbound: RumVitalWebsocketMessageDirection & { + /** + * Largest amount of bytes ever observed queued in the send buffer, sampled after each send is enqueued + */ + readonly buffered_amount_max: number; + /** + * Number of sends issued while the send buffer was already deep, that is while it held at least 65536 bytes right before the send + */ + readonly backpressured_message_count: number; + /** + * Amount of bytes still queued in the send buffer when tracking ended, in bytes. Set on the websocket_closed vital only + */ + readonly buffered_amount_at_close?: number; + [k: string]: unknown; + }; + [k: string]: unknown; +} +/** + * Schema of the messages observed in one direction of a WebSocket connection + */ +export interface RumVitalWebsocketMessageDirection { + /** + * Number of messages observed in this direction since the connection opened + */ + readonly message_count: number; + /** + * Sum of the payload sizes of the messages observed in this direction, in bytes + */ + readonly message_size_total: number; + /** + * Size of the largest single payload observed in this direction, in bytes. 0 while no message has been observed + */ + readonly message_size_max: number; + /** + * Longest interval between two consecutive messages observed in this direction, in nanoseconds, including the still-open interval since the last message. 0 until two messages have been observed. The interval before the first message is excluded from it, as it is reported by time_to_first_message + */ + readonly longest_silence: number; + /** + * Interval between the open date of the connection and the first message observed in this direction, in nanoseconds. Omitted until a first message has been observed + */ + readonly time_to_first_message?: number; + /** + * Interval between the last message observed in this direction and the closed date of the connection, in nanoseconds. Set on the websocket_closed vital only, and only when at least one message has been observed in this direction + */ + readonly silence_before_close?: number; + [k: string]: unknown; +} diff --git a/samples/rum-events/vital-websocket-closed-never-opened.json b/samples/rum-events/vital-websocket-closed-never-opened.json new file mode 100644 index 00000000..06dbe9e5 --- /dev/null +++ b/samples/rum-events/vital-websocket-closed-never-opened.json @@ -0,0 +1,35 @@ +{ + "type": "vital", + "date": 1706792747000, + "application": { + "id": "ac8218cf-498b-4d33-bd44-151095959547" + }, + "session": { + "id": "cacbf45c-3a05-48ce-b066-d76349460599", + "type": "user" + }, + "source": "browser", + "view": { + "id": "623d50fd-75cf-4025-97d2-e51ff94171f6", + "referrer": "", + "url": "https://app.datadoghq.com/rum/explorer?live=1h&query=&tab=view" + }, + "vital": { + "id": "f0e1d2c3-b4a5-4968-8877-665544332211", + "type": "websocket", + "name": "websocket_closed", + "websocket": { + "id": "1a2b3c4d-5e6f-4708-8192-a3b4c5d6e7f8", + "closed_date": 1706792747000, + "duration": 424000000, + "tracking_end_reason": "close_event", + "close_code": 1006, + "close_reason": "", + "was_clean": false, + "snapshot_version": 1 + } + }, + "_dd": { + "format_version": 2 + } +} diff --git a/samples/rum-events/vital-websocket-closed-session-end.json b/samples/rum-events/vital-websocket-closed-session-end.json new file mode 100644 index 00000000..5dbf4ce5 --- /dev/null +++ b/samples/rum-events/vital-websocket-closed-session-end.json @@ -0,0 +1,51 @@ +{ + "type": "vital", + "date": 1706792760000, + "application": { + "id": "ac8218cf-498b-4d33-bd44-151095959547" + }, + "session": { + "id": "cacbf45c-3a05-48ce-b066-d76349460599", + "type": "user" + }, + "source": "browser", + "view": { + "id": "623d50fd-75cf-4025-97d2-e51ff94171f6", + "referrer": "", + "url": "https://app.datadoghq.com/rum/explorer?live=1h&query=&tab=view" + }, + "vital": { + "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", + "type": "websocket", + "name": "websocket_closed", + "websocket": { + "id": "9e9f4b3a-1c2d-4e5f-8a9b-0c1d2e3f4a5b", + "closed_date": 1706792760000, + "duration": 13424000000, + "tracking_end_reason": "session_end", + "snapshot_version": 9, + "snapshot": { + "inbound": { + "message_count": 40, + "message_size_total": 81920, + "message_size_max": 4096, + "longest_silence": 2000000000, + "time_to_first_message": 120000000, + "silence_before_close": 1200000000 + }, + "outbound": { + "message_count": 0, + "message_size_total": 0, + "message_size_max": 0, + "longest_silence": 0, + "buffered_amount_max": 0, + "backpressured_message_count": 0, + "buffered_amount_at_close": 0 + } + } + } + }, + "_dd": { + "format_version": 2 + } +} diff --git a/samples/rum-events/vital-websocket-closed.json b/samples/rum-events/vital-websocket-closed.json new file mode 100644 index 00000000..184a5041 --- /dev/null +++ b/samples/rum-events/vital-websocket-closed.json @@ -0,0 +1,56 @@ +{ + "type": "vital", + "date": 1706792752100, + "application": { + "id": "ac8218cf-498b-4d33-bd44-151095959547" + }, + "session": { + "id": "cacbf45c-3a05-48ce-b066-d76349460599", + "type": "user" + }, + "source": "browser", + "view": { + "id": "623d50fd-75cf-4025-97d2-e51ff94171f6", + "referrer": "", + "url": "https://app.datadoghq.com/rum/explorer?live=1h&query=&tab=view" + }, + "vital": { + "id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f70", + "type": "websocket", + "name": "websocket_closed", + "websocket": { + "id": "9e9f4b3a-1c2d-4e5f-8a9b-0c1d2e3f4a5b", + "closed_date": 1706792752100, + "duration": 5524000000, + "tracking_end_reason": "close_event", + "close_code": 1000, + "close_reason": "", + "was_clean": true, + "snapshot_version": 4, + "snapshot": { + "inbound": { + "message_count": 18, + "message_size_total": 36864, + "message_size_max": 4096, + "longest_silence": 1500000000, + "time_to_first_message": 120000000, + "silence_before_close": 400000000 + }, + "outbound": { + "message_count": 6, + "message_size_total": 768, + "message_size_max": 256, + "longest_silence": 3000000000, + "time_to_first_message": 80000000, + "silence_before_close": 900000000, + "buffered_amount_max": 1024, + "backpressured_message_count": 0, + "buffered_amount_at_close": 0 + } + } + } + }, + "_dd": { + "format_version": 2 + } +} diff --git a/samples/rum-events/vital-websocket-closing.json b/samples/rum-events/vital-websocket-closing.json new file mode 100644 index 00000000..b357c0a9 --- /dev/null +++ b/samples/rum-events/vital-websocket-closing.json @@ -0,0 +1,30 @@ +{ + "type": "vital", + "date": 1706792752000, + "application": { + "id": "ac8218cf-498b-4d33-bd44-151095959547" + }, + "session": { + "id": "cacbf45c-3a05-48ce-b066-d76349460599", + "type": "user" + }, + "source": "browser", + "view": { + "id": "623d50fd-75cf-4025-97d2-e51ff94171f6", + "referrer": "", + "url": "https://app.datadoghq.com/rum/explorer?live=1h&query=&tab=view" + }, + "vital": { + "id": "7b5c8d91-2e3f-4a5b-8c7d-9e0f1a2b3c4d", + "type": "websocket", + "name": "websocket_closing", + "websocket": { + "id": "9e9f4b3a-1c2d-4e5f-8a9b-0c1d2e3f4a5b", + "closing_date": 1706792752000, + "close_initiator": "client" + } + }, + "_dd": { + "format_version": 2 + } +} diff --git a/samples/rum-events/vital-websocket-connecting.json b/samples/rum-events/vital-websocket-connecting.json new file mode 100644 index 00000000..0766c82b --- /dev/null +++ b/samples/rum-events/vital-websocket-connecting.json @@ -0,0 +1,31 @@ +{ + "type": "vital", + "date": 1706792746576, + "application": { + "id": "ac8218cf-498b-4d33-bd44-151095959547" + }, + "session": { + "id": "cacbf45c-3a05-48ce-b066-d76349460599", + "type": "user" + }, + "source": "browser", + "view": { + "id": "623d50fd-75cf-4025-97d2-e51ff94171f6", + "referrer": "", + "url": "https://app.datadoghq.com/rum/explorer?live=1h&query=&tab=view" + }, + "vital": { + "id": "47af0abb-3de1-4170-a491-4473716df979", + "type": "websocket", + "name": "websocket_connecting", + "websocket": { + "id": "9e9f4b3a-1c2d-4e5f-8a9b-0c1d2e3f4a5b", + "url": "wss://example.datadoghq.com/socket", + "requested_protocols": ["graphql-ws"], + "connecting_date": 1706792746576 + } + }, + "_dd": { + "format_version": 2 + } +} diff --git a/samples/rum-events/vital-websocket-open.json b/samples/rum-events/vital-websocket-open.json new file mode 100644 index 00000000..03aae2dc --- /dev/null +++ b/samples/rum-events/vital-websocket-open.json @@ -0,0 +1,52 @@ +{ + "type": "vital", + "date": 1706792746776, + "application": { + "id": "ac8218cf-498b-4d33-bd44-151095959547" + }, + "session": { + "id": "cacbf45c-3a05-48ce-b066-d76349460599", + "type": "user" + }, + "source": "browser", + "view": { + "id": "623d50fd-75cf-4025-97d2-e51ff94171f6", + "referrer": "", + "url": "https://app.datadoghq.com/rum/explorer?live=1h&query=&tab=view" + }, + "vital": { + "id": "2c1a6de4-5b7f-4a3e-9d81-6f0b2c3d4e5f", + "type": "websocket", + "name": "websocket_open", + "websocket": { + "id": "9e9f4b3a-1c2d-4e5f-8a9b-0c1d2e3f4a5b", + "open_handshake_succeeded": true, + "connecting_duration": 200000000, + "open_date": 1706792746776, + "selected_protocol": "graphql-ws", + "selected_extensions": "permessage-deflate; client_max_window_bits=10", + "snapshot_version": 3, + "snapshot": { + "inbound": { + "message_count": 12, + "message_size_total": 24576, + "message_size_max": 4096, + "longest_silence": 1500000000, + "time_to_first_message": 120000000 + }, + "outbound": { + "message_count": 4, + "message_size_total": 512, + "message_size_max": 256, + "longest_silence": 3000000000, + "time_to_first_message": 80000000, + "buffered_amount_max": 1024, + "backpressured_message_count": 0 + } + } + } + }, + "_dd": { + "format_version": 2 + } +} diff --git a/schemas/rum-events-browser-schema.json b/schemas/rum-events-browser-schema.json index ba995614..e9657056 100644 --- a/schemas/rum-events-browser-schema.json +++ b/schemas/rum-events-browser-schema.json @@ -34,6 +34,18 @@ }, { "$ref": "rum/vital-operation-step-schema.json" + }, + { + "$ref": "rum/vital-websocket-connecting-schema.json" + }, + { + "$ref": "rum/vital-websocket-open-schema.json" + }, + { + "$ref": "rum/vital-websocket-closing-schema.json" + }, + { + "$ref": "rum/vital-websocket-closed-schema.json" } ] } diff --git a/schemas/rum-events-schema.json b/schemas/rum-events-schema.json index fdd4757f..7dc9071b 100644 --- a/schemas/rum-events-schema.json +++ b/schemas/rum-events-schema.json @@ -37,6 +37,18 @@ }, { "$ref": "rum/vital-app-launch-schema.json" + }, + { + "$ref": "rum/vital-websocket-connecting-schema.json" + }, + { + "$ref": "rum/vital-websocket-open-schema.json" + }, + { + "$ref": "rum/vital-websocket-closing-schema.json" + }, + { + "$ref": "rum/vital-websocket-closed-schema.json" } ] } diff --git a/schemas/rum/_vital-websocket-common-schema.json b/schemas/rum/_vital-websocket-common-schema.json new file mode 100644 index 00000000..86fe972f --- /dev/null +++ b/schemas/rum/_vital-websocket-common-schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "rum/_vital-websocket-common-schema.json", + "title": "RumVitalWebsocketCommonProperties", + "type": "object", + "description": "Schema of common properties for a WebSocket vital event. One connection reports a stream of vitals over its lifetime, one per phase, all sharing the same vital.websocket.id", + "allOf": [ + { + "$ref": "_vital-common-schema.json" + }, + { + "properties": { + "vital": { + "type": "object", + "description": "Vital properties", + "required": ["type", "websocket"], + "properties": { + "type": { + "type": "string", + "description": "Type of the vital.", + "const": "websocket", + "readOnly": true + }, + "websocket": { + "type": "object", + "description": "WebSocket properties", + "required": ["id"], + "properties": { + "id": { + "type": "string", + "description": "UUID of the WebSocket connection, stable across every vital reported for that connection. It is not vital.id, which identifies a single vital", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$", + "readOnly": true + } + }, + "readOnly": true + } + }, + "readOnly": true + } + } + } + ] +} diff --git a/schemas/rum/_vital-websocket-message-direction-schema.json b/schemas/rum/_vital-websocket-message-direction-schema.json new file mode 100644 index 00000000..b6f02149 --- /dev/null +++ b/schemas/rum/_vital-websocket-message-direction-schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "rum/_vital-websocket-message-direction-schema.json", + "title": "RumVitalWebsocketMessageDirection", + "type": "object", + "description": "Schema of the messages observed in one direction of a WebSocket connection", + "required": ["message_count", "message_size_total", "message_size_max", "longest_silence"], + "properties": { + "message_count": { + "type": "integer", + "description": "Number of messages observed in this direction since the connection opened", + "minimum": 0, + "readOnly": true + }, + "message_size_total": { + "type": "integer", + "description": "Sum of the payload sizes of the messages observed in this direction, in bytes", + "minimum": 0, + "readOnly": true + }, + "message_size_max": { + "type": "integer", + "description": "Size of the largest single payload observed in this direction, in bytes. 0 while no message has been observed", + "minimum": 0, + "readOnly": true + }, + "longest_silence": { + "type": "integer", + "description": "Longest interval between two consecutive messages observed in this direction, in nanoseconds, including the still-open interval since the last message. 0 until two messages have been observed. The interval before the first message is excluded from it, as it is reported by time_to_first_message", + "minimum": 0, + "readOnly": true + }, + "time_to_first_message": { + "type": "integer", + "description": "Interval between the open date of the connection and the first message observed in this direction, in nanoseconds. Omitted until a first message has been observed", + "minimum": 0, + "readOnly": true + }, + "silence_before_close": { + "type": "integer", + "description": "Interval between the last message observed in this direction and the closed date of the connection, in nanoseconds. Set on the websocket_closed vital only, and only when at least one message has been observed in this direction", + "minimum": 0, + "readOnly": true + } + }, + "readOnly": true +} diff --git a/schemas/rum/_vital-websocket-snapshot-schema.json b/schemas/rum/_vital-websocket-snapshot-schema.json new file mode 100644 index 00000000..ecf8eb2f --- /dev/null +++ b/schemas/rum/_vital-websocket-snapshot-schema.json @@ -0,0 +1,48 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "rum/_vital-websocket-snapshot-schema.json", + "title": "RumVitalWebsocketSnapshot", + "type": "object", + "description": "Schema of the WebSocket connection values that change from one vital of a connection to the next. Both directions are present whenever the snapshot is, and are zero-filled while no message has been observed", + "required": ["inbound", "outbound"], + "properties": { + "inbound": { + "type": "object", + "description": "Messages received from the server", + "allOf": [{ "$ref": "_vital-websocket-message-direction-schema.json" }], + "readOnly": true + }, + "outbound": { + "type": "object", + "description": "Messages sent to the server", + "allOf": [ + { "$ref": "_vital-websocket-message-direction-schema.json" }, + { + "required": ["buffered_amount_max", "backpressured_message_count"], + "properties": { + "buffered_amount_max": { + "type": "integer", + "description": "Largest amount of bytes ever observed queued in the send buffer, sampled after each send is enqueued", + "minimum": 0, + "readOnly": true + }, + "backpressured_message_count": { + "type": "integer", + "description": "Number of sends issued while the send buffer was already deep, that is while it held at least 65536 bytes right before the send", + "minimum": 0, + "readOnly": true + }, + "buffered_amount_at_close": { + "type": "integer", + "description": "Amount of bytes still queued in the send buffer when tracking ended, in bytes. Set on the websocket_closed vital only", + "minimum": 0, + "readOnly": true + } + } + } + ], + "readOnly": true + } + }, + "readOnly": true +} diff --git a/schemas/rum/vital-websocket-closed-schema.json b/schemas/rum/vital-websocket-closed-schema.json new file mode 100644 index 00000000..f34a82a7 --- /dev/null +++ b/schemas/rum/vital-websocket-closed-schema.json @@ -0,0 +1,83 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "rum/vital-websocket-closed-schema.json", + "title": "RumVitalWebsocketClosedEvent", + "type": "object", + "description": "Schema for the vital reported when a WebSocket connection stops being tracked, whether because it closed or because tracking ended first. Reported at most once per connection", + "allOf": [ + { + "$ref": "_vital-websocket-common-schema.json" + }, + { + "properties": { + "vital": { + "type": "object", + "description": "Vital properties", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Name of the vital, identifying the phase of the connection it reports", + "const": "websocket_closed", + "readOnly": true + }, + "websocket": { + "type": "object", + "description": "WebSocket properties", + "required": ["closed_date", "duration", "tracking_end_reason", "snapshot_version"], + "properties": { + "closed_date": { + "type": "integer", + "description": "Date tracking of the connection ended, in ms from epoch. It is the date the connection closed when the tracking end reason is close_event, and the date tracking stopped otherwise", + "minimum": 0, + "readOnly": true + }, + "duration": { + "type": "integer", + "description": "Interval between the connecting date and the closed date of the connection, in nanoseconds. It is not vital.duration, which is reported by duration vitals", + "minimum": 0, + "readOnly": true + }, + "tracking_end_reason": { + "type": "string", + "description": "Why tracking of the connection ended", + "enum": ["close_event", "session_end", "page_unloaded"], + "readOnly": true + }, + "close_code": { + "type": "number", + "description": "Code the connection was closed with. Reported when, and only when, the tracking end reason is close_event", + "readOnly": true + }, + "close_reason": { + "type": "string", + "description": "Reason the connection was closed with, as an empty string when the other side supplied none. Reported when, and only when, the tracking end reason is close_event", + "readOnly": true + }, + "was_clean": { + "type": "boolean", + "description": "Whether the connection closed cleanly. Reported when, and only when, the tracking end reason is close_event", + "readOnly": true + }, + "snapshot_version": { + "type": "integer", + "description": "Version of the snapshot, incremented on every vital reporting one, so that heartbeats can be ordered when they are received out of sequence", + "minimum": 0, + "readOnly": true + }, + "snapshot": { + "type": "object", + "description": "Values of the connection when tracking ended. Omitted when the connection never opened", + "allOf": [{ "$ref": "_vital-websocket-snapshot-schema.json" }], + "readOnly": true + } + }, + "readOnly": true + } + }, + "readOnly": true + } + } + } + ] +} diff --git a/schemas/rum/vital-websocket-closing-schema.json b/schemas/rum/vital-websocket-closing-schema.json new file mode 100644 index 00000000..50558cc7 --- /dev/null +++ b/schemas/rum/vital-websocket-closing-schema.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "rum/vital-websocket-closing-schema.json", + "title": "RumVitalWebsocketClosingEvent", + "type": "object", + "description": "Schema for the vital reported when a WebSocket connection starts closing. Reported at most once per connection", + "allOf": [ + { + "$ref": "_vital-websocket-common-schema.json" + }, + { + "properties": { + "vital": { + "type": "object", + "description": "Vital properties", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Name of the vital, identifying the phase of the connection it reports", + "const": "websocket_closing", + "readOnly": true + }, + "websocket": { + "type": "object", + "description": "WebSocket properties", + "required": ["closing_date", "close_initiator"], + "properties": { + "closing_date": { + "type": "integer", + "description": "Date the connection was asked to close, in ms from epoch", + "minimum": 0, + "readOnly": true + }, + "close_initiator": { + "type": "string", + "description": "Side that initiated the close. Only reported on this vital, and only as client, since a close observed from the application is always initiated by it. The other values are reported by the connection-level data derived from these vitals", + "enum": ["client", "server", "unknown"], + "readOnly": true + } + }, + "readOnly": true + } + }, + "readOnly": true + } + } + } + ] +} diff --git a/schemas/rum/vital-websocket-connecting-schema.json b/schemas/rum/vital-websocket-connecting-schema.json new file mode 100644 index 00000000..c0831629 --- /dev/null +++ b/schemas/rum/vital-websocket-connecting-schema.json @@ -0,0 +1,58 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "rum/vital-websocket-connecting-schema.json", + "title": "RumVitalWebsocketConnectingEvent", + "type": "object", + "description": "Schema for the vital reported when a WebSocket connection is created. Reported exactly once per connection, when the WebSocket is constructed", + "allOf": [ + { + "$ref": "_vital-websocket-common-schema.json" + }, + { + "properties": { + "vital": { + "type": "object", + "description": "Vital properties", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Name of the vital, identifying the phase of the connection it reports", + "const": "websocket_connecting", + "readOnly": true + }, + "websocket": { + "type": "object", + "description": "WebSocket properties. The identity of the connection is reported on this vital only, and is not repeated on the vitals of the later phases", + "required": ["url", "connecting_date"], + "properties": { + "url": { + "type": "string", + "description": "URL the connection was opened on, without its query string", + "readOnly": true + }, + "requested_protocols": { + "type": "array", + "description": "Subprotocols the connection was requested with. Omitted when none was requested", + "items": { + "type": "string", + "readOnly": true + }, + "readOnly": true + }, + "connecting_date": { + "type": "integer", + "description": "Date the WebSocket was constructed, in ms from epoch", + "minimum": 0, + "readOnly": true + } + }, + "readOnly": true + } + }, + "readOnly": true + } + } + } + ] +} diff --git a/schemas/rum/vital-websocket-open-schema.json b/schemas/rum/vital-websocket-open-schema.json new file mode 100644 index 00000000..afb0f3f8 --- /dev/null +++ b/schemas/rum/vital-websocket-open-schema.json @@ -0,0 +1,83 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "rum/vital-websocket-open-schema.json", + "title": "RumVitalWebsocketOpenEvent", + "type": "object", + "description": "Schema for the vital reported when a WebSocket connection opens, then repeatedly as a heartbeat while it stays open", + "allOf": [ + { + "$ref": "_vital-websocket-common-schema.json" + }, + { + "properties": { + "vital": { + "type": "object", + "description": "Vital properties", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Name of the vital, identifying the phase of the connection it reports", + "const": "websocket_open", + "readOnly": true + }, + "websocket": { + "type": "object", + "description": "WebSocket properties", + "required": [ + "open_handshake_succeeded", + "connecting_duration", + "open_date", + "snapshot_version", + "snapshot" + ], + "properties": { + "open_handshake_succeeded": { + "type": "boolean", + "description": "Whether the opening handshake succeeded", + "readOnly": true + }, + "connecting_duration": { + "type": "integer", + "description": "Interval between the connecting date and the open date of the connection, in nanoseconds", + "minimum": 0, + "readOnly": true + }, + "open_date": { + "type": "integer", + "description": "Date the connection opened, in ms from epoch", + "minimum": 0, + "readOnly": true + }, + "selected_protocol": { + "type": "string", + "description": "Subprotocol the server selected. Omitted when the server selected none", + "readOnly": true + }, + "selected_extensions": { + "type": "string", + "description": "Extensions the server negotiated, as the single string the connection exposes them as. Omitted when none was negotiated", + "readOnly": true + }, + "snapshot_version": { + "type": "integer", + "description": "Version of the snapshot, incremented on every vital reporting one, so that heartbeats can be ordered when they are received out of sequence", + "minimum": 0, + "readOnly": true + }, + "snapshot": { + "type": "object", + "description": "Values of the connection at the time the vital was reported", + "allOf": [{ "$ref": "_vital-websocket-snapshot-schema.json" }], + "readOnly": true + } + }, + "readOnly": true + } + }, + "readOnly": true + } + } + } + ] +} From 9491921963f7e3b552e335f97525a40444a963f0 Mon Sep 17 00:00:00 2001 From: Boris Dibon Date: Thu, 13 Aug 2026 12:17:40 +0200 Subject: [PATCH 2/2] Discriminate WebSocket vitals by required fields, not vital.name `dd-sdk-android`'s model generator cannot narrow a property that a shared schema already declares: `Primitive.mergedWith` accepts only an identical `Primitive`, while `Constant.mergedWith` and `Enum.mergedWith` error unconditionally (`buildSrc/.../jsonschema/TypeDefinition.kt`). Since `_vital-common-schema.json` declares `vital.name` as a free-form string, a `const` on it in each phase schema fails `generateRumModelsFromJson` with "Can't merge Primitive with type Constant". Drop the consts. The four phase schemas stay mutually exclusive through their disjoint `required` sets, so `oneOf` still resolves to exactly one arm per event. `vital.name` remains required, declared as the plain string the shared envelope already uses, and each phase schema records the value it is reported with in its description. Co-Authored-By: Claude Opus 5 (1M context) --- lib/cjs/generated/rum.d.ts | 28 ++++++------------- lib/esm/generated/rum.d.ts | 28 ++++++------------- .../rum/_vital-websocket-common-schema.json | 7 ++++- .../rum/vital-websocket-closed-schema.json | 15 ++++------ .../rum/vital-websocket-closing-schema.json | 9 +----- .../vital-websocket-connecting-schema.json | 9 +----- schemas/rum/vital-websocket-open-schema.json | 15 ++++------ 7 files changed, 36 insertions(+), 75 deletions(-) diff --git a/lib/cjs/generated/rum.d.ts b/lib/cjs/generated/rum.d.ts index ba96fd98..1eb60b91 100644 --- a/lib/cjs/generated/rum.d.ts +++ b/lib/cjs/generated/rum.d.ts @@ -1022,17 +1022,13 @@ export type RumVitalAppLaunchEvent = RumVitalEventCommonProperties & { [k: string]: unknown; }; /** - * Schema for the vital reported when a WebSocket connection is created. Reported exactly once per connection, when the WebSocket is constructed + * Schema for the vital reported when a WebSocket connection is created. Reported exactly once per connection, when the WebSocket is constructed. Reported with vital.name websocket_connecting */ export type RumVitalWebsocketConnectingEvent = RumVitalWebsocketCommonProperties & { /** * Vital properties */ readonly vital?: { - /** - * Name of the vital, identifying the phase of the connection it reports - */ - readonly name: 'websocket_connecting'; /** * WebSocket properties. The identity of the connection is reported on this vital only, and is not repeated on the vitals of the later phases */ @@ -1067,6 +1063,10 @@ export type RumVitalWebsocketCommonProperties = RumVitalEventCommonProperties & * Type of the vital. */ readonly type: 'websocket'; + /** + * Name of the vital, identifying the phase of the connection it reports: websocket_connecting, websocket_open, websocket_closing or websocket_closed + */ + readonly name: string; /** * WebSocket properties */ @@ -1082,17 +1082,13 @@ export type RumVitalWebsocketCommonProperties = RumVitalEventCommonProperties & [k: string]: unknown; }; /** - * Schema for the vital reported when a WebSocket connection opens, then repeatedly as a heartbeat while it stays open + * Schema for the vital reported when a WebSocket connection opens, then repeatedly as a heartbeat while it stays open. Reported with vital.name websocket_open */ export type RumVitalWebsocketOpenEvent = RumVitalWebsocketCommonProperties & { /** * Vital properties */ readonly vital?: { - /** - * Name of the vital, identifying the phase of the connection it reports - */ - readonly name: 'websocket_open'; /** * WebSocket properties */ @@ -1132,17 +1128,13 @@ export type RumVitalWebsocketOpenEvent = RumVitalWebsocketCommonProperties & { [k: string]: unknown; }; /** - * Schema for the vital reported when a WebSocket connection starts closing. Reported at most once per connection + * Schema for the vital reported when a WebSocket connection starts closing. Reported at most once per connection. Reported with vital.name websocket_closing */ export type RumVitalWebsocketClosingEvent = RumVitalWebsocketCommonProperties & { /** * Vital properties */ readonly vital?: { - /** - * Name of the vital, identifying the phase of the connection it reports - */ - readonly name: 'websocket_closing'; /** * WebSocket properties */ @@ -1162,17 +1154,13 @@ export type RumVitalWebsocketClosingEvent = RumVitalWebsocketCommonProperties & [k: string]: unknown; }; /** - * Schema for the vital reported when a WebSocket connection stops being tracked, whether because it closed or because tracking ended first. Reported at most once per connection + * Schema for the vital reported when a WebSocket connection stops being tracked, whether because it closed or because tracking ended first. Reported at most once per connection. Reported with vital.name websocket_closed */ export type RumVitalWebsocketClosedEvent = RumVitalWebsocketCommonProperties & { /** * Vital properties */ readonly vital?: { - /** - * Name of the vital, identifying the phase of the connection it reports - */ - readonly name: 'websocket_closed'; /** * WebSocket properties */ diff --git a/lib/esm/generated/rum.d.ts b/lib/esm/generated/rum.d.ts index ba96fd98..1eb60b91 100644 --- a/lib/esm/generated/rum.d.ts +++ b/lib/esm/generated/rum.d.ts @@ -1022,17 +1022,13 @@ export type RumVitalAppLaunchEvent = RumVitalEventCommonProperties & { [k: string]: unknown; }; /** - * Schema for the vital reported when a WebSocket connection is created. Reported exactly once per connection, when the WebSocket is constructed + * Schema for the vital reported when a WebSocket connection is created. Reported exactly once per connection, when the WebSocket is constructed. Reported with vital.name websocket_connecting */ export type RumVitalWebsocketConnectingEvent = RumVitalWebsocketCommonProperties & { /** * Vital properties */ readonly vital?: { - /** - * Name of the vital, identifying the phase of the connection it reports - */ - readonly name: 'websocket_connecting'; /** * WebSocket properties. The identity of the connection is reported on this vital only, and is not repeated on the vitals of the later phases */ @@ -1067,6 +1063,10 @@ export type RumVitalWebsocketCommonProperties = RumVitalEventCommonProperties & * Type of the vital. */ readonly type: 'websocket'; + /** + * Name of the vital, identifying the phase of the connection it reports: websocket_connecting, websocket_open, websocket_closing or websocket_closed + */ + readonly name: string; /** * WebSocket properties */ @@ -1082,17 +1082,13 @@ export type RumVitalWebsocketCommonProperties = RumVitalEventCommonProperties & [k: string]: unknown; }; /** - * Schema for the vital reported when a WebSocket connection opens, then repeatedly as a heartbeat while it stays open + * Schema for the vital reported when a WebSocket connection opens, then repeatedly as a heartbeat while it stays open. Reported with vital.name websocket_open */ export type RumVitalWebsocketOpenEvent = RumVitalWebsocketCommonProperties & { /** * Vital properties */ readonly vital?: { - /** - * Name of the vital, identifying the phase of the connection it reports - */ - readonly name: 'websocket_open'; /** * WebSocket properties */ @@ -1132,17 +1128,13 @@ export type RumVitalWebsocketOpenEvent = RumVitalWebsocketCommonProperties & { [k: string]: unknown; }; /** - * Schema for the vital reported when a WebSocket connection starts closing. Reported at most once per connection + * Schema for the vital reported when a WebSocket connection starts closing. Reported at most once per connection. Reported with vital.name websocket_closing */ export type RumVitalWebsocketClosingEvent = RumVitalWebsocketCommonProperties & { /** * Vital properties */ readonly vital?: { - /** - * Name of the vital, identifying the phase of the connection it reports - */ - readonly name: 'websocket_closing'; /** * WebSocket properties */ @@ -1162,17 +1154,13 @@ export type RumVitalWebsocketClosingEvent = RumVitalWebsocketCommonProperties & [k: string]: unknown; }; /** - * Schema for the vital reported when a WebSocket connection stops being tracked, whether because it closed or because tracking ended first. Reported at most once per connection + * Schema for the vital reported when a WebSocket connection stops being tracked, whether because it closed or because tracking ended first. Reported at most once per connection. Reported with vital.name websocket_closed */ export type RumVitalWebsocketClosedEvent = RumVitalWebsocketCommonProperties & { /** * Vital properties */ readonly vital?: { - /** - * Name of the vital, identifying the phase of the connection it reports - */ - readonly name: 'websocket_closed'; /** * WebSocket properties */ diff --git a/schemas/rum/_vital-websocket-common-schema.json b/schemas/rum/_vital-websocket-common-schema.json index 86fe972f..b024edb6 100644 --- a/schemas/rum/_vital-websocket-common-schema.json +++ b/schemas/rum/_vital-websocket-common-schema.json @@ -13,7 +13,7 @@ "vital": { "type": "object", "description": "Vital properties", - "required": ["type", "websocket"], + "required": ["type", "name", "websocket"], "properties": { "type": { "type": "string", @@ -21,6 +21,11 @@ "const": "websocket", "readOnly": true }, + "name": { + "type": "string", + "description": "Name of the vital, identifying the phase of the connection it reports: websocket_connecting, websocket_open, websocket_closing or websocket_closed", + "readOnly": true + }, "websocket": { "type": "object", "description": "WebSocket properties", diff --git a/schemas/rum/vital-websocket-closed-schema.json b/schemas/rum/vital-websocket-closed-schema.json index f34a82a7..ff587198 100644 --- a/schemas/rum/vital-websocket-closed-schema.json +++ b/schemas/rum/vital-websocket-closed-schema.json @@ -3,7 +3,7 @@ "$id": "rum/vital-websocket-closed-schema.json", "title": "RumVitalWebsocketClosedEvent", "type": "object", - "description": "Schema for the vital reported when a WebSocket connection stops being tracked, whether because it closed or because tracking ended first. Reported at most once per connection", + "description": "Schema for the vital reported when a WebSocket connection stops being tracked, whether because it closed or because tracking ended first. Reported at most once per connection. Reported with vital.name websocket_closed", "allOf": [ { "$ref": "_vital-websocket-common-schema.json" @@ -13,14 +13,7 @@ "vital": { "type": "object", "description": "Vital properties", - "required": ["name"], "properties": { - "name": { - "type": "string", - "description": "Name of the vital, identifying the phase of the connection it reports", - "const": "websocket_closed", - "readOnly": true - }, "websocket": { "type": "object", "description": "WebSocket properties", @@ -68,7 +61,11 @@ "snapshot": { "type": "object", "description": "Values of the connection when tracking ended. Omitted when the connection never opened", - "allOf": [{ "$ref": "_vital-websocket-snapshot-schema.json" }], + "allOf": [ + { + "$ref": "_vital-websocket-snapshot-schema.json" + } + ], "readOnly": true } }, diff --git a/schemas/rum/vital-websocket-closing-schema.json b/schemas/rum/vital-websocket-closing-schema.json index 50558cc7..80c8e754 100644 --- a/schemas/rum/vital-websocket-closing-schema.json +++ b/schemas/rum/vital-websocket-closing-schema.json @@ -3,7 +3,7 @@ "$id": "rum/vital-websocket-closing-schema.json", "title": "RumVitalWebsocketClosingEvent", "type": "object", - "description": "Schema for the vital reported when a WebSocket connection starts closing. Reported at most once per connection", + "description": "Schema for the vital reported when a WebSocket connection starts closing. Reported at most once per connection. Reported with vital.name websocket_closing", "allOf": [ { "$ref": "_vital-websocket-common-schema.json" @@ -13,14 +13,7 @@ "vital": { "type": "object", "description": "Vital properties", - "required": ["name"], "properties": { - "name": { - "type": "string", - "description": "Name of the vital, identifying the phase of the connection it reports", - "const": "websocket_closing", - "readOnly": true - }, "websocket": { "type": "object", "description": "WebSocket properties", diff --git a/schemas/rum/vital-websocket-connecting-schema.json b/schemas/rum/vital-websocket-connecting-schema.json index c0831629..b05e6447 100644 --- a/schemas/rum/vital-websocket-connecting-schema.json +++ b/schemas/rum/vital-websocket-connecting-schema.json @@ -3,7 +3,7 @@ "$id": "rum/vital-websocket-connecting-schema.json", "title": "RumVitalWebsocketConnectingEvent", "type": "object", - "description": "Schema for the vital reported when a WebSocket connection is created. Reported exactly once per connection, when the WebSocket is constructed", + "description": "Schema for the vital reported when a WebSocket connection is created. Reported exactly once per connection, when the WebSocket is constructed. Reported with vital.name websocket_connecting", "allOf": [ { "$ref": "_vital-websocket-common-schema.json" @@ -13,14 +13,7 @@ "vital": { "type": "object", "description": "Vital properties", - "required": ["name"], "properties": { - "name": { - "type": "string", - "description": "Name of the vital, identifying the phase of the connection it reports", - "const": "websocket_connecting", - "readOnly": true - }, "websocket": { "type": "object", "description": "WebSocket properties. The identity of the connection is reported on this vital only, and is not repeated on the vitals of the later phases", diff --git a/schemas/rum/vital-websocket-open-schema.json b/schemas/rum/vital-websocket-open-schema.json index afb0f3f8..1a862e2b 100644 --- a/schemas/rum/vital-websocket-open-schema.json +++ b/schemas/rum/vital-websocket-open-schema.json @@ -3,7 +3,7 @@ "$id": "rum/vital-websocket-open-schema.json", "title": "RumVitalWebsocketOpenEvent", "type": "object", - "description": "Schema for the vital reported when a WebSocket connection opens, then repeatedly as a heartbeat while it stays open", + "description": "Schema for the vital reported when a WebSocket connection opens, then repeatedly as a heartbeat while it stays open. Reported with vital.name websocket_open", "allOf": [ { "$ref": "_vital-websocket-common-schema.json" @@ -13,14 +13,7 @@ "vital": { "type": "object", "description": "Vital properties", - "required": ["name"], "properties": { - "name": { - "type": "string", - "description": "Name of the vital, identifying the phase of the connection it reports", - "const": "websocket_open", - "readOnly": true - }, "websocket": { "type": "object", "description": "WebSocket properties", @@ -68,7 +61,11 @@ "snapshot": { "type": "object", "description": "Values of the connection at the time the vital was reported", - "allOf": [{ "$ref": "_vital-websocket-snapshot-schema.json" }], + "allOf": [ + { + "$ref": "_vital-websocket-snapshot-schema.json" + } + ], "readOnly": true } },