WebSocket 장기 연결 수집¶
RUM SDK 3.3.6부터 브라우저의 네이티브 WebSocket 연결을 RUM Resource로 집계하여 핸드셰이크, 메시지 트래픽, 인바운드 유휴 상태, 전송 백로그 및 종료 상태를 분석할 수 있습니다.
이 기능은 현재 실험적 기능이며 기본적으로 비활성화되어 있습니다. SDK는 WebSocket 메시지 본문을 읽거나 업로드하지 않습니다.
수집 활성화¶
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"],
})
축소된 RUM 패키지도 동일한 구성을 사용합니다:
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>
위 예시는 공용 네트워크 OpenWay를 사용합니다. DataKit 직접 연결을 사용하는 경우 site 및 clientToken을 datakitOrigin으로 대체하고, 두 가지 보고 주소를 동시에 구성하지 마십시오.
enableExperimentalFeatures는 배열이어야 합니다. 문자열 "track_websockets"로 직접 작성하면 수집이 활성화되지 않습니다.
초기화 시점¶
RUM은 비즈니스에서 WebSocket 연결을 생성하기 전에 초기화되어야 합니다:
datafluxRum.init({
// 기타 구성
enableExperimentalFeatures: ["track_websockets"],
})
const socket = new WebSocket("wss://example.com/socket")
다음 연결은 수집되지 않습니다:
- RUM 초기화 전에 이미 생성된 연결
- 초기화 전에 캐시된 원본
WebSocket생성자를 사용하여 생성된 연결 - Web Worker 또는 Service Worker 내에서 생성된 연결
window.WebSocket를 거치지 않는 기타 전송 구현WebSocketStream으로 생성된 연결
수집을 활성화해도 네이티브 WebSocket의 생성 방식, 정적 상수, instanceof, 비즈니스 이벤트 리스닝 및 send() 반환 동작은 변경되지 않습니다.
수집 모델¶
각 WebSocket Session 세그먼트는 하나의 RUM Resource를 생성합니다:
Resource는 다음 시점에 생성됩니다:
- 브라우저가 WebSocket
close이벤트를 수신할 때 - 현재 RUM Session이 만료될 때
- 페이지가
beforeunload를 트리거할 때 - SDK가 현재 수집 인스턴스를 중지할 때
WebSocket은 장기 연결입니다. 연결이 계속 열려 있는 동안 Network에 최종 RUM Resource가 일시적으로 없는 것은 정상입니다. SDK는 현재 주기적으로 연결 스냅샷을 보고하지 않습니다.
페이지 새로고침, 종료 또는 이동 시 SDK는 beforeunload 단계에서 가능한 한 정산하여 전송합니다. 페이지 프로세스가 강제 종료되거나 브라우저가 충돌하거나 장치 전원이 꺼지는 경우 JavaScript가 실행되지 않아 마지막 연결 Resource가 손실될 수 있습니다.
정상 종료¶
브라우저가 close를 수신한 후:
이벤트에는 종료 코드, 종료 이유 및 was_clean이 포함됩니다.
Session 만료¶
Session이 만료되면 아직 열려 있는 연결은 현재 데이터를 기준으로 정산됩니다:
이때 비즈니스 WebSocket이 종료되지는 않으므로 종료 코드, 종료 이유 및 was_clean이 존재하지 않을 수 있습니다. 새 Session이 설정되면 SDK는 동일한 물리적 연결에 대해 새로운 통계 세그먼트를 시작하고, 동일한 connection_id를 유지하며 메시지 카운트 및 세그먼트 시간을 재설정합니다.
Session이 만료되었으나 아직 갱신되지 않은 기간의 메시지는 어느 Session에도 포함되지 않습니다. 이 기간 동안 새로 생성되어 갱신 시점에 계속 열려 있는 연결은 새 Session의 갱신 시점부터 통계가 시작됩니다.
페이지 언로드¶
페이지가 beforeunload를 트리거할 때:
연결이 반드시 브라우저 close 이벤트를 트리거하는 것은 아니므로 종료 관련 필드가 존재하지 않을 수 있습니다.
핸드셰이크 실패¶
연결이 open을 한 번도 트리거하지 않고 close 상태가 된 경우:
이때 setup_duration은 연결 생성부터 종료 또는 Session 정산까지의 시간을 나타내며, 성공적인 핸드셰이크 소요 시간을 의미하지 않습니다. 브라우저는 일반적으로 종료 코드 1006을 사용하여 비정상 종료를 나타내며, 구체적인 값은 브라우저 이벤트를 기준으로 합니다.
메시지 통계¶
SDK는 메시지 수와 바이트 수만 통계하며, 본문은 수집하지 않습니다:
| 메시지 유형 | 바이트 계산 방식 |
|---|---|
| string | UTF-8 바이트 수 |
ArrayBuffer |
byteLength |
TypedArray, DataView |
현재 view의 byteLength |
Blob |
size |
| 인식할 수 없는 유형 | 0 |
예를 들어 문자열 你好는 UTF-8 기준으로 6바이트로 계산되며, JavaScript 문자열 길이 2가 아닙니다.
View 소속¶
하나의 WebSocket Session 세그먼트는 여러 RUM View에 걸쳐 있을 수 있습니다. Resource는 다음을 추가로 기록합니다:
start_view_id: 현재 세그먼트 시작 시점의 Viewend_view_id: 종료 또는 정산 시점의 View
세그먼트가 페이지를 넘나드는 경우 이 두 ID는 서로 다를 수 있습니다. Resource는 여전히 세그먼트 시작 시간을 기준으로 RUM 이벤트 파이프라인에 진입합니다.
보고 필드¶
beforeSend에서 event.resource.websocket을 읽을 수 있습니다. 최종 intake는 필드를 resource_websocket_*로 변환합니다.
기본 Resource 필드¶
beforeSend 경로 |
intake 필드 | 설명 | 단위 |
|---|---|---|---|
resource.type |
resource_type |
고정값 websocket |
- |
resource.url |
resource_url |
브라우저가 해석한 ws:// 또는 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 매개변수 | - |
resource.duration |
duration |
현재 Session 세그먼트 길이 | ns |
WebSocket Resource에는 HTTP 응답이 없으므로 resource_status, resource_method, TTFB, 다운로드 크기 또는 HTTP timing이 없습니다.
연결 필드¶
resource.websocket.* |
intake 필드 | 설명 | 단위 |
|---|---|---|---|
connection_id |
resource_websocket_connection_id |
물리적 연결 고유 ID, Session 세그먼트를 넘어 유지됨 | - |
handshake_succeeded |
resource_websocket_handshake_succeeded |
open 수신 여부 |
boolean |
start_time |
resource_websocket_start_time |
현재 세그먼트 시작 시간 | Unix ms |
end_time |
resource_websocket_end_time |
종료 또는 정산 시간 | Unix ms |
start_view_id |
resource_websocket_start_view_id |
세그먼트 시작 시점의 View ID | - |
end_view_id |
resource_websocket_end_view_id |
연결 종료 시점의 View ID | - |
tracking_end_reason |
resource_websocket_tracking_end_reason |
close_event, session_end 또는 page_exit |
- |
protocol |
resource_websocket_protocol |
서버가 협상한 서브 프로토콜 | - |
setup_duration |
resource_websocket_setup_duration |
첫 번째 세그먼트는 생성부터 open까지의 시간; 갱신 세그먼트는 0 |
ns |
메시지 필드¶
resource.websocket.* |
intake 필드 | 설명 | 단위 |
|---|---|---|---|
messages_in.count |
resource_websocket_messages_in_count |
인바운드 메시지 수 | count |
messages_in.size |
resource_websocket_messages_in_size |
인바운드 메시지 총 바이트 수 | byte |
messages_out.count |
resource_websocket_messages_out_count |
성공적인 send() 호출 횟수 |
count |
messages_out.size |
resource_websocket_messages_out_size |
아웃바운드 메시지 총 바이트 수 | byte |
time_to_first_message_in |
resource_websocket_time_to_first_message_in |
open부터 첫 번째 인바운드 메시지까지 |
ns |
time_to_first_message_out |
resource_websocket_time_to_first_message_out |
open부터 첫 번째 아웃바운드 메시지까지 |
ns |
last_message_in_at |
resource_websocket_last_message_in_at |
마지막 인바운드 메시지 시간 | Unix ms |
longest_inbound_silence |
resource_websocket_longest_inbound_silence |
인접한 인바운드 메시지 간 최대 간격 | ns |
inbound_idle_duration_before_close |
resource_websocket_inbound_idle_duration_before_close |
마지막 인바운드 메시지부터 종료 또는 정산까지의 시간 | ns |
buffered_amount_max |
resource_websocket_buffered_amount_max |
각 send() 호출 전에 관찰된 bufferedAmount 최대값 |
byte |
buffered_amount_max는 send() 호출 전의 샘플링된 최대값이며, 브라우저 전송 큐의 연속 모니터링 값이 아닙니다.
종료 필드¶
resource.websocket.* |
intake 필드 | 설명 |
|---|---|---|
close_code |
resource_websocket_close_code |
브라우저 CloseEvent의 종료 코드 |
close_reason |
resource_websocket_close_reason |
종료 이유 |
was_clean |
resource_websocket_was_clean |
브라우저가 연결을 정상 종료로 간주하는지 여부 |
Session 만료 또는 페이지 언로드 정산 시 이러한 필드는 존재하지 않을 수 있습니다.
beforeSend 사용¶
WebSocket Resource를 확인, 보강 또는 필터링할 수 있습니다:
datafluxRum.init({
// 기타 구성
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
},
})
WebSocket Resource의 domainContext에는 다음이 포함됩니다:
domainContext는 beforeSend 콜백 내에서만 사용되며 업로드되지 않습니다.
false를 반환하여 지정된 연결을 폐기할 수 있습니다:
beforeSend(event) {
if (
event.type === "resource" &&
event.resource?.type === "websocket" &&
event.resource.url.includes("/health-stream")
) {
return false
}
return true
}
개인정보 보호 및 보안¶
SDK는 전체 WebSocket URL을 수집하고 URL query를 해석합니다. URL에 비밀번호, 장기 Token, 주민등록번호 등 민감한 값을 배치하지 마십시오.
수집을 활성화해도 페이지 CSP connect-src, 서버 Origin 검증 또는 기타 브라우저 보안 정책을 우회하지 않으며, 핸드셰이크 요청에 사용자 정의 trace header를 주입하지 않습니다.
타사 라이브러리가 RUM 초기화 후에 window.WebSocket을 호출하는 한, 기본 연결이 수집됩니다:
- 자동 재연결은 새 연결이 생성될 때마다 새
connection_id및 Resource를 생성합니다. - 하나의 연결이 여러 비즈니스 topic을 재사용하는 경우 SDK는 연결 수준의 요약만 제공합니다.
- HTTP long polling 단계는 여전히 XHR 또는 fetch Resource로 수집됩니다.
연동 확인¶
- 브라우저 개발자 도구를 열고 비즈니스 WebSocket이 연결되어 메시지를 생성하는지 확인합니다.
socket.close(1000, "done")을 수동으로 실행합니다.- Network에서
/v1/write/rum을 필터링합니다. - 요청 데이터에서
resource_type=websocket을 찾습니다. handshake_succeeded, 메시지 수, 바이트 수 및 종료 필드를 확인합니다.
연결이 계속 종료되지 않는 경우 Session 만료를 기다린 후 tracking_end_reason=session_end를 확인할 수 있습니다. 페이지를 새로고침하면 tracking_end_reason=page_exit이 표시되어야 합니다. 이 전송은 페이지 종료 단계의 최선 노력 보고에 해당합니다.
자주 묻는 질문¶
설정 후 WebSocket Resource가 보이지 않음¶
다음 순서로 확인하십시오:
enableExperimentalFeatures가"track_websockets"를 포함한 배열인지 확인- RUM이 연결 생성 전에 초기화되었는지 확인
- 현재 Session이
sessionSampleRate에 해당하는지 확인 - 연결이 이미 종료되었거나 Session이 만료되었는지 확인
beforeSend가false를 반환하지 않는지 확인- 연결이 Worker에 의해 생성되었는지 확인
WebSocket error는 수신했지만 Resource가 아직 없음¶
SDK는 close 또는 Session 정산 시 최종 이벤트를 생성하며, error 이벤트에서 별도로 정산하지 않습니다.
handshake_succeeded=false¶
브라우저가 open을 트리거하지 않았습니다. WebSocket URL, TLS 인증서, CSP connect-src, 리버스 프록시 Upgrade 구성, 서버 Origin 검증 및 인증을 확인하십시오.
메시지 size가 0¶
데이터 유형이 string, ArrayBuffer, TypedArray, DataView 또는 Blob인지 확인하십시오. SDK는 임의의 객체를 직렬화하여 크기를 추정하지 않습니다.
HTTP 상태 코드가 보이지 않음¶
브라우저 WebSocket API는 페이지에 핸드셰이크 HTTP 상태 코드를 노출하지 않으므로 WebSocket Resource에는 resource_status가 없습니다.