@multiplayer-app/session-recorder-react-native 1.0.1-beta.4 → 1.0.1-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeConfig.kt +1 -1
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeModule.kt +9 -9
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativePackage.kt +2 -2
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/model/TargetInfo.kt +1 -1
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/util/ViewUtils.kt +2 -2
- package/lib/module/SessionRecorderNativeSpec.js +5 -0
- package/lib/module/SessionRecorderNativeSpec.js.map +1 -0
- package/lib/module/components/SessionRecorderWidget/ErrorBanner.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/ModalHeader.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/icons.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/styles.js.map +1 -1
- package/lib/module/config/constants.js.map +1 -1
- package/lib/module/config/defaults.js.map +1 -1
- package/lib/module/config/masking.js.map +1 -1
- package/lib/module/config/session-recorder.js.map +1 -1
- package/lib/module/config/validators.js.map +1 -1
- package/lib/module/config/widget.js.map +1 -1
- package/lib/module/context/SessionRecorderStore.js.map +1 -1
- package/lib/module/context/useSessionRecorderStore.js.map +1 -1
- package/lib/module/context/useStoreSelector.js.map +1 -1
- package/lib/module/native/SessionRecorderNative.js +16 -11
- package/lib/module/native/SessionRecorderNative.js.map +1 -1
- package/lib/module/native/index.js.map +1 -1
- package/lib/module/otel/helpers.js +1 -1
- package/lib/module/otel/helpers.js.map +1 -1
- package/lib/module/otel/index.js.map +1 -1
- package/lib/module/otel/instrumentations/index.js.map +1 -1
- package/lib/module/patch/xhr.js.map +1 -1
- package/lib/module/recorder/eventExporter.js.map +1 -1
- package/lib/module/recorder/gestureRecorder.js.map +1 -1
- package/lib/module/recorder/index.js.map +1 -1
- package/lib/module/recorder/navigationTracker.js.map +1 -1
- package/lib/module/recorder/screenRecorder.js.map +1 -1
- package/lib/module/services/api.service.js.map +1 -1
- package/lib/module/services/network.service.js.map +1 -1
- package/lib/module/services/screenMaskingService.js +1 -1
- package/lib/module/services/screenMaskingService.js.map +1 -1
- package/lib/module/services/storage.service.js.map +1 -1
- package/lib/module/session-recorder.js.map +1 -1
- package/lib/module/types/index.js.map +1 -1
- package/lib/module/types/session-recorder.js.map +1 -1
- package/lib/module/utils/app-metadata.js +2 -2
- package/lib/module/utils/constants.optional.js.map +1 -1
- package/lib/module/utils/createStore.js.map +1 -1
- package/lib/module/utils/logger.js +0 -8
- package/lib/module/utils/logger.js.map +1 -1
- package/lib/module/utils/platform.js +1 -1
- package/lib/module/utils/platform.js.map +1 -1
- package/lib/module/utils/rrweb-events.js.map +1 -1
- package/lib/module/utils/session.js.map +1 -1
- package/lib/module/utils/shallowEqual.js.map +1 -1
- package/lib/module/utils/time.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/SessionRecorderNativeSpec.d.ts +42 -0
- package/lib/typescript/src/SessionRecorderNativeSpec.d.ts.map +1 -0
- package/lib/typescript/src/components/ScreenRecorderView/index.d.ts +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/ErrorBanner.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/SessionRecorderWidget.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/icons.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/index.d.ts +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/config/constants.d.ts.map +1 -1
- package/lib/typescript/src/config/defaults.d.ts.map +1 -1
- package/lib/typescript/src/config/index.d.ts.map +1 -1
- package/lib/typescript/src/config/masking.d.ts.map +1 -1
- package/lib/typescript/src/config/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/config/validators.d.ts.map +1 -1
- package/lib/typescript/src/config/widget.d.ts +1 -1
- package/lib/typescript/src/config/widget.d.ts.map +1 -1
- package/lib/typescript/src/context/SessionRecorderStore.d.ts.map +1 -1
- package/lib/typescript/src/context/useSessionRecorderStore.d.ts +3 -3
- package/lib/typescript/src/context/useSessionRecorderStore.d.ts.map +1 -1
- package/lib/typescript/src/context/useStoreSelector.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/SessionRecorderNative.d.ts +3 -54
- package/lib/typescript/src/native/SessionRecorderNative.d.ts.map +1 -1
- package/lib/typescript/src/native/index.d.ts +1 -1
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/lib/typescript/src/otel/helpers.d.ts.map +1 -1
- package/lib/typescript/src/otel/index.d.ts.map +1 -1
- package/lib/typescript/src/otel/instrumentations/index.d.ts.map +1 -1
- package/lib/typescript/src/patch/index.d.ts.map +1 -1
- package/lib/typescript/src/patch/xhr.d.ts.map +1 -1
- package/lib/typescript/src/recorder/eventExporter.d.ts.map +1 -1
- package/lib/typescript/src/recorder/gestureRecorder.d.ts.map +1 -1
- package/lib/typescript/src/recorder/index.d.ts.map +1 -1
- package/lib/typescript/src/recorder/navigationTracker.d.ts.map +1 -1
- package/lib/typescript/src/recorder/screenRecorder.d.ts.map +1 -1
- package/lib/typescript/src/services/api.service.d.ts.map +1 -1
- package/lib/typescript/src/services/network.service.d.ts.map +1 -1
- package/lib/typescript/src/services/screenMaskingService.d.ts +1 -1
- package/lib/typescript/src/services/screenMaskingService.d.ts.map +1 -1
- package/lib/typescript/src/services/storage.service.d.ts.map +1 -1
- package/lib/typescript/src/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/configs.d.ts.map +1 -1
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/lib/typescript/src/types/session-recorder.d.ts +4 -4
- package/lib/typescript/src/types/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/session.d.ts.map +1 -1
- package/lib/typescript/src/utils/app-metadata.d.ts.map +1 -1
- package/lib/typescript/src/utils/constants.optional.d.ts.map +1 -1
- package/lib/typescript/src/utils/constants.optional.expo.d.ts.map +1 -1
- package/lib/typescript/src/utils/createStore.d.ts.map +1 -1
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/logger.d.ts +1 -1
- package/lib/typescript/src/utils/logger.d.ts.map +1 -1
- package/lib/typescript/src/utils/platform.d.ts.map +1 -1
- package/lib/typescript/src/utils/request-utils.d.ts.map +1 -1
- package/lib/typescript/src/utils/rrweb-events.d.ts.map +1 -1
- package/lib/typescript/src/utils/session.d.ts.map +1 -1
- package/lib/typescript/src/utils/shallowEqual.d.ts.map +1 -1
- package/lib/typescript/src/utils/time.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-utils.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/SessionRecorderNativeSpec.ts +53 -0
- package/src/components/ScreenRecorderView/index.ts +1 -1
- package/src/components/SessionRecorderWidget/ErrorBanner.tsx +14 -14
- package/src/components/SessionRecorderWidget/ModalHeader.tsx +11 -9
- package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +70 -56
- package/src/components/SessionRecorderWidget/icons.tsx +58 -30
- package/src/components/SessionRecorderWidget/index.ts +1 -1
- package/src/components/SessionRecorderWidget/styles.ts +17 -18
- package/src/components/index.ts +2 -2
- package/src/config/constants.ts +19 -20
- package/src/config/defaults.ts +35 -31
- package/src/config/index.ts +5 -5
- package/src/config/masking.ts +44 -18
- package/src/config/session-recorder.ts +54 -26
- package/src/config/validators.ts +43 -20
- package/src/config/widget.ts +24 -15
- package/src/context/SessionRecorderStore.ts +19 -18
- package/src/context/useSessionRecorderStore.ts +17 -10
- package/src/context/useStoreSelector.ts +20 -18
- package/src/index.ts +7 -7
- package/src/native/SessionRecorderNative.ts +68 -112
- package/src/native/index.ts +5 -1
- package/src/otel/helpers.ts +109 -93
- package/src/otel/index.ts +46 -49
- package/src/otel/instrumentations/index.ts +44 -41
- package/src/patch/index.ts +1 -1
- package/src/patch/xhr.ts +77 -78
- package/src/recorder/eventExporter.ts +63 -68
- package/src/recorder/gestureRecorder.ts +359 -212
- package/src/recorder/index.ts +75 -62
- package/src/recorder/navigationTracker.ts +120 -97
- package/src/recorder/screenRecorder.ts +214 -163
- package/src/services/api.service.ts +49 -48
- package/src/services/network.service.ts +67 -58
- package/src/services/screenMaskingService.ts +81 -50
- package/src/services/storage.service.ts +99 -70
- package/src/session-recorder.ts +270 -214
- package/src/types/configs.ts +53 -31
- package/src/types/expo-constants.d.ts +2 -2
- package/src/types/index.ts +16 -18
- package/src/types/session-recorder.ts +106 -111
- package/src/types/session.ts +45 -45
- package/src/utils/app-metadata.ts +9 -9
- package/src/utils/constants.optional.expo.ts +3 -3
- package/src/utils/constants.optional.ts +14 -12
- package/src/utils/createStore.ts +23 -20
- package/src/utils/index.ts +7 -7
- package/src/utils/logger.ts +87 -58
- package/src/utils/platform.ts +149 -118
- package/src/utils/request-utils.ts +15 -15
- package/src/utils/rrweb-events.ts +47 -34
- package/src/utils/session.ts +15 -12
- package/src/utils/shallowEqual.ts +16 -10
- package/src/utils/time.ts +7 -4
- package/src/utils/type-utils.ts +36 -36
- package/src/version.ts +1 -1
- package/android/src/main/java/com/multiplayer/sessionrecordernative/SessionRecorderNativeModuleSpec.kt +0 -51
- package/android/src/main/java/com/xxx/XxxModule.kt +0 -23
- package/ios/Xxx.h +0 -5
- package/ios/Xxx.mm +0 -21
package/src/otel/helpers.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Span } from '@opentelemetry/api'
|
|
1
|
+
import { type Span } from '@opentelemetry/api';
|
|
2
2
|
import {
|
|
3
3
|
MULTIPLAYER_TRACE_DEBUG_PREFIX,
|
|
4
4
|
MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,
|
|
@@ -6,24 +6,22 @@ import {
|
|
|
6
6
|
ATTR_MULTIPLAYER_HTTP_REQUEST_HEADERS,
|
|
7
7
|
ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY,
|
|
8
8
|
ATTR_MULTIPLAYER_HTTP_RESPONSE_HEADERS,
|
|
9
|
-
} from '@multiplayer-app/session-recorder-common'
|
|
10
|
-
import { logger } from '../utils'
|
|
11
|
-
import { type TracerReactNativeConfig } from '../types'
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
} from '@multiplayer-app/session-recorder-common';
|
|
10
|
+
import { logger } from '../utils';
|
|
11
|
+
import { type TracerReactNativeConfig } from '../types';
|
|
14
12
|
|
|
15
13
|
export interface HttpPayloadData {
|
|
16
|
-
requestBody?: any
|
|
17
|
-
responseBody?: any
|
|
18
|
-
requestHeaders?: Record<string, string
|
|
19
|
-
responseHeaders?: Record<string, string
|
|
14
|
+
requestBody?: any;
|
|
15
|
+
responseBody?: any;
|
|
16
|
+
requestHeaders?: Record<string, string>;
|
|
17
|
+
responseHeaders?: Record<string, string>;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
export interface ProcessedHttpPayload {
|
|
23
|
-
requestBody?: string
|
|
24
|
-
responseBody?: string
|
|
25
|
-
requestHeaders?: string
|
|
26
|
-
responseHeaders?: string
|
|
21
|
+
requestBody?: string;
|
|
22
|
+
responseBody?: string;
|
|
23
|
+
requestHeaders?: string;
|
|
24
|
+
responseHeaders?: string;
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
/**
|
|
@@ -33,7 +31,7 @@ export function shouldProcessTrace(traceId: string): boolean {
|
|
|
33
31
|
return (
|
|
34
32
|
traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||
|
|
35
33
|
traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
|
|
36
|
-
)
|
|
34
|
+
);
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
/**
|
|
@@ -42,22 +40,22 @@ export function shouldProcessTrace(traceId: string): boolean {
|
|
|
42
40
|
export function processBody(
|
|
43
41
|
payload: HttpPayloadData,
|
|
44
42
|
config: TracerReactNativeConfig,
|
|
45
|
-
span: Span
|
|
43
|
+
span: Span
|
|
46
44
|
): { requestBody?: string; responseBody?: string } {
|
|
47
|
-
const { captureBody, masking } = config
|
|
48
|
-
const traceId = span.spanContext().traceId
|
|
45
|
+
const { captureBody, masking } = config;
|
|
46
|
+
const traceId = span.spanContext().traceId;
|
|
49
47
|
|
|
50
48
|
if (!captureBody) {
|
|
51
|
-
return {}
|
|
49
|
+
return {};
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
let { requestBody, responseBody } = payload
|
|
52
|
+
let { requestBody, responseBody } = payload;
|
|
55
53
|
|
|
56
54
|
if (requestBody !== undefined && requestBody !== null) {
|
|
57
|
-
requestBody = JSON.parse(JSON.stringify(requestBody))
|
|
55
|
+
requestBody = JSON.parse(JSON.stringify(requestBody));
|
|
58
56
|
}
|
|
59
57
|
if (responseBody !== undefined && responseBody !== null) {
|
|
60
|
-
responseBody = JSON.parse(JSON.stringify(responseBody))
|
|
58
|
+
responseBody = JSON.parse(JSON.stringify(responseBody));
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
// Apply masking for debug traces
|
|
@@ -66,24 +64,24 @@ export function processBody(
|
|
|
66
64
|
traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
|
|
67
65
|
) {
|
|
68
66
|
if (masking.isContentMaskingEnabled) {
|
|
69
|
-
requestBody = requestBody && masking.maskBody?.(requestBody, span)
|
|
70
|
-
responseBody = responseBody && masking.maskBody?.(responseBody, span)
|
|
67
|
+
requestBody = requestBody && masking.maskBody?.(requestBody, span);
|
|
68
|
+
responseBody = responseBody && masking.maskBody?.(responseBody, span);
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
// Convert to string if needed
|
|
75
73
|
if (typeof requestBody !== 'string') {
|
|
76
|
-
requestBody = JSON.stringify(requestBody)
|
|
74
|
+
requestBody = JSON.stringify(requestBody);
|
|
77
75
|
}
|
|
78
76
|
|
|
79
77
|
if (typeof responseBody !== 'string') {
|
|
80
|
-
responseBody = JSON.stringify(responseBody)
|
|
78
|
+
responseBody = JSON.stringify(responseBody);
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
return {
|
|
84
82
|
requestBody: requestBody?.length ? requestBody : undefined,
|
|
85
83
|
responseBody: responseBody?.length ? responseBody : undefined,
|
|
86
|
-
}
|
|
84
|
+
};
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
/**
|
|
@@ -92,71 +90,74 @@ export function processBody(
|
|
|
92
90
|
export function processHeaders(
|
|
93
91
|
payload: HttpPayloadData,
|
|
94
92
|
config: TracerReactNativeConfig,
|
|
95
|
-
span: Span
|
|
93
|
+
span: Span
|
|
96
94
|
): { requestHeaders?: string; responseHeaders?: string } {
|
|
97
|
-
const { captureHeaders, masking } = config
|
|
95
|
+
const { captureHeaders, masking } = config;
|
|
98
96
|
|
|
99
97
|
if (!captureHeaders) {
|
|
100
|
-
return {}
|
|
98
|
+
return {};
|
|
101
99
|
}
|
|
102
100
|
|
|
103
|
-
let { requestHeaders = {}, responseHeaders = {} } = payload
|
|
101
|
+
let { requestHeaders = {}, responseHeaders = {} } = payload;
|
|
104
102
|
|
|
105
103
|
// Handle header filtering
|
|
106
|
-
if (
|
|
107
|
-
!masking.headersToInclude?.length &&
|
|
108
|
-
!masking.headersToExclude?.length
|
|
109
|
-
) {
|
|
104
|
+
if (!masking.headersToInclude?.length && !masking.headersToExclude?.length) {
|
|
110
105
|
// Add null checks to prevent JSON.parse error when headers is undefined
|
|
111
106
|
if (requestHeaders !== undefined && requestHeaders !== null) {
|
|
112
|
-
requestHeaders = JSON.parse(JSON.stringify(requestHeaders))
|
|
107
|
+
requestHeaders = JSON.parse(JSON.stringify(requestHeaders));
|
|
113
108
|
}
|
|
114
109
|
if (responseHeaders !== undefined && responseHeaders !== null) {
|
|
115
|
-
responseHeaders = JSON.parse(JSON.stringify(responseHeaders))
|
|
110
|
+
responseHeaders = JSON.parse(JSON.stringify(responseHeaders));
|
|
116
111
|
}
|
|
117
112
|
} else {
|
|
118
113
|
if (masking.headersToInclude) {
|
|
119
|
-
const _requestHeaders: Record<string, string> = {}
|
|
120
|
-
const _responseHeaders: Record<string, string> = {}
|
|
114
|
+
const _requestHeaders: Record<string, string> = {};
|
|
115
|
+
const _responseHeaders: Record<string, string> = {};
|
|
121
116
|
|
|
122
117
|
for (const headerName of masking.headersToInclude) {
|
|
123
118
|
if (requestHeaders[headerName]) {
|
|
124
|
-
_requestHeaders[headerName] = requestHeaders[headerName]
|
|
119
|
+
_requestHeaders[headerName] = requestHeaders[headerName];
|
|
125
120
|
}
|
|
126
121
|
if (responseHeaders[headerName]) {
|
|
127
|
-
_responseHeaders[headerName] = responseHeaders[headerName]
|
|
122
|
+
_responseHeaders[headerName] = responseHeaders[headerName];
|
|
128
123
|
}
|
|
129
124
|
}
|
|
130
125
|
|
|
131
|
-
requestHeaders = _requestHeaders
|
|
132
|
-
responseHeaders = _responseHeaders
|
|
126
|
+
requestHeaders = _requestHeaders;
|
|
127
|
+
responseHeaders = _responseHeaders;
|
|
133
128
|
}
|
|
134
129
|
|
|
135
130
|
if (masking.headersToExclude?.length) {
|
|
136
131
|
for (const headerName of masking.headersToExclude) {
|
|
137
|
-
delete requestHeaders[headerName]
|
|
138
|
-
delete responseHeaders[headerName]
|
|
132
|
+
delete requestHeaders[headerName];
|
|
133
|
+
delete responseHeaders[headerName];
|
|
139
134
|
}
|
|
140
135
|
}
|
|
141
136
|
}
|
|
142
137
|
|
|
143
138
|
// Apply masking
|
|
144
|
-
const maskedRequestHeaders =
|
|
145
|
-
|
|
139
|
+
const maskedRequestHeaders =
|
|
140
|
+
masking.maskHeaders?.(requestHeaders, span) || requestHeaders;
|
|
141
|
+
const maskedResponseHeaders =
|
|
142
|
+
masking.maskHeaders?.(responseHeaders, span) || responseHeaders;
|
|
146
143
|
|
|
147
144
|
// Convert to string
|
|
148
|
-
const requestHeadersStr =
|
|
149
|
-
|
|
150
|
-
|
|
145
|
+
const requestHeadersStr =
|
|
146
|
+
typeof maskedRequestHeaders === 'string'
|
|
147
|
+
? maskedRequestHeaders
|
|
148
|
+
: JSON.stringify(maskedRequestHeaders);
|
|
151
149
|
|
|
152
|
-
const responseHeadersStr =
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
const responseHeadersStr =
|
|
151
|
+
typeof maskedResponseHeaders === 'string'
|
|
152
|
+
? maskedResponseHeaders
|
|
153
|
+
: JSON.stringify(maskedResponseHeaders);
|
|
155
154
|
|
|
156
155
|
return {
|
|
157
156
|
requestHeaders: requestHeadersStr?.length ? requestHeadersStr : undefined,
|
|
158
|
-
responseHeaders: responseHeadersStr?.length
|
|
159
|
-
|
|
157
|
+
responseHeaders: responseHeadersStr?.length
|
|
158
|
+
? responseHeadersStr
|
|
159
|
+
: undefined,
|
|
160
|
+
};
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
/**
|
|
@@ -165,110 +166,125 @@ export function processHeaders(
|
|
|
165
166
|
export function processHttpPayload(
|
|
166
167
|
payload: HttpPayloadData,
|
|
167
168
|
config: TracerReactNativeConfig,
|
|
168
|
-
span: Span
|
|
169
|
+
span: Span
|
|
169
170
|
): void {
|
|
170
|
-
const traceId = span.spanContext().traceId
|
|
171
|
+
const traceId = span.spanContext().traceId;
|
|
171
172
|
|
|
172
173
|
if (!shouldProcessTrace(traceId)) {
|
|
173
|
-
return
|
|
174
|
+
return;
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
const { requestBody, responseBody } = processBody(payload, config, span)
|
|
177
|
-
const { requestHeaders, responseHeaders } = processHeaders(
|
|
177
|
+
const { requestBody, responseBody } = processBody(payload, config, span);
|
|
178
|
+
const { requestHeaders, responseHeaders } = processHeaders(
|
|
179
|
+
payload,
|
|
180
|
+
config,
|
|
181
|
+
span
|
|
182
|
+
);
|
|
178
183
|
|
|
179
184
|
// Set span attributes
|
|
180
185
|
if (requestBody) {
|
|
181
|
-
span.setAttribute(ATTR_MULTIPLAYER_HTTP_REQUEST_BODY, requestBody)
|
|
186
|
+
span.setAttribute(ATTR_MULTIPLAYER_HTTP_REQUEST_BODY, requestBody);
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
if (responseBody) {
|
|
185
|
-
span.setAttribute(ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY, responseBody)
|
|
190
|
+
span.setAttribute(ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY, responseBody);
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
if (requestHeaders) {
|
|
189
|
-
span.setAttribute(ATTR_MULTIPLAYER_HTTP_REQUEST_HEADERS, requestHeaders)
|
|
194
|
+
span.setAttribute(ATTR_MULTIPLAYER_HTTP_REQUEST_HEADERS, requestHeaders);
|
|
190
195
|
}
|
|
191
196
|
|
|
192
197
|
if (responseHeaders) {
|
|
193
|
-
span.setAttribute(ATTR_MULTIPLAYER_HTTP_RESPONSE_HEADERS, responseHeaders)
|
|
198
|
+
span.setAttribute(ATTR_MULTIPLAYER_HTTP_RESPONSE_HEADERS, responseHeaders);
|
|
194
199
|
}
|
|
195
200
|
}
|
|
196
201
|
|
|
197
202
|
/**
|
|
198
203
|
* Converts Headers object to plain object
|
|
199
204
|
*/
|
|
200
|
-
export function headersToObject(
|
|
201
|
-
|
|
205
|
+
export function headersToObject(
|
|
206
|
+
headers:
|
|
207
|
+
| Headers
|
|
208
|
+
| Record<string, string>
|
|
209
|
+
| Record<string, string | string[]>
|
|
210
|
+
| string[][]
|
|
211
|
+
| undefined
|
|
212
|
+
): Record<string, string> {
|
|
213
|
+
const result: Record<string, string> = {};
|
|
202
214
|
|
|
203
215
|
if (!headers) {
|
|
204
|
-
return result
|
|
216
|
+
return result;
|
|
205
217
|
}
|
|
206
218
|
|
|
207
219
|
if (headers instanceof Headers) {
|
|
208
220
|
headers.forEach((value: string, key: string) => {
|
|
209
|
-
result[key] = value
|
|
210
|
-
})
|
|
221
|
+
result[key] = value;
|
|
222
|
+
});
|
|
211
223
|
} else if (Array.isArray(headers)) {
|
|
212
224
|
// Handle array of [key, value] pairs
|
|
213
225
|
for (const [key, value] of headers) {
|
|
214
226
|
if (typeof key === 'string' && typeof value === 'string') {
|
|
215
|
-
result[key] = value
|
|
227
|
+
result[key] = value;
|
|
216
228
|
}
|
|
217
229
|
}
|
|
218
230
|
} else if (typeof headers === 'object' && !Array.isArray(headers)) {
|
|
219
231
|
for (const [key, value] of Object.entries(headers)) {
|
|
220
232
|
if (typeof key === 'string' && typeof value === 'string') {
|
|
221
|
-
result[key] = value
|
|
233
|
+
result[key] = value;
|
|
222
234
|
}
|
|
223
235
|
}
|
|
224
236
|
}
|
|
225
237
|
|
|
226
|
-
return result
|
|
238
|
+
return result;
|
|
227
239
|
}
|
|
228
240
|
|
|
229
241
|
/**
|
|
230
242
|
* Extracts response body as string from Response object
|
|
231
243
|
*/
|
|
232
|
-
export async function extractResponseBody(
|
|
244
|
+
export async function extractResponseBody(
|
|
245
|
+
response: Response
|
|
246
|
+
): Promise<string | null> {
|
|
233
247
|
if (!response.body) {
|
|
234
|
-
return null
|
|
248
|
+
return null;
|
|
235
249
|
}
|
|
236
250
|
|
|
237
251
|
try {
|
|
238
252
|
if (response.body instanceof ReadableStream) {
|
|
239
253
|
// Check if response body is already consumed
|
|
240
254
|
if (response.bodyUsed) {
|
|
241
|
-
return null
|
|
255
|
+
return null;
|
|
242
256
|
}
|
|
243
257
|
|
|
244
|
-
const responseClone = response.clone()
|
|
245
|
-
return responseClone.text()
|
|
258
|
+
const responseClone = response.clone();
|
|
259
|
+
return responseClone.text();
|
|
246
260
|
} else {
|
|
247
|
-
return JSON.stringify(response.body)
|
|
261
|
+
return JSON.stringify(response.body);
|
|
248
262
|
}
|
|
249
263
|
} catch (error) {
|
|
250
264
|
// If cloning fails (body already consumed), return null
|
|
251
|
-
|
|
252
|
-
logger.warn('DEBUGGER_LIB', 'Failed to extract response body', error)
|
|
253
|
-
return null
|
|
265
|
+
|
|
266
|
+
logger.warn('DEBUGGER_LIB', 'Failed to extract response body', error);
|
|
267
|
+
return null;
|
|
254
268
|
}
|
|
255
269
|
}
|
|
256
270
|
|
|
257
271
|
export const getExporterEndpoint = (exporterEndpoint: string): string => {
|
|
258
|
-
const hasPath =
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
272
|
+
const hasPath =
|
|
273
|
+
exporterEndpoint &&
|
|
274
|
+
(() => {
|
|
275
|
+
try {
|
|
276
|
+
const url = new URL(exporterEndpoint);
|
|
277
|
+
return url.pathname !== '/' && url.pathname !== '';
|
|
278
|
+
} catch {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
})();
|
|
266
282
|
|
|
267
283
|
if (hasPath) {
|
|
268
|
-
return exporterEndpoint
|
|
284
|
+
return exporterEndpoint;
|
|
269
285
|
}
|
|
270
286
|
|
|
271
|
-
const trimmedExporterEndpoint = new URL(exporterEndpoint).origin
|
|
287
|
+
const trimmedExporterEndpoint = new URL(exporterEndpoint).origin;
|
|
272
288
|
|
|
273
|
-
return `${trimmedExporterEndpoint}/v1/traces
|
|
274
|
-
}
|
|
289
|
+
return `${trimmedExporterEndpoint}/v1/traces`;
|
|
290
|
+
};
|
package/src/otel/index.ts
CHANGED
|
@@ -1,55 +1,51 @@
|
|
|
1
|
-
import { resourceFromAttributes } from '@opentelemetry/resources'
|
|
2
|
-
import { W3CTraceContextPropagator } from '@opentelemetry/core'
|
|
3
|
-
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'
|
|
4
|
-
import * as SemanticAttributes from '@opentelemetry/semantic-conventions'
|
|
5
|
-
import { registerInstrumentations } from '@opentelemetry/instrumentation'
|
|
1
|
+
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
2
|
+
import { W3CTraceContextPropagator } from '@opentelemetry/core';
|
|
3
|
+
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
4
|
+
import * as SemanticAttributes from '@opentelemetry/semantic-conventions';
|
|
5
|
+
import { registerInstrumentations } from '@opentelemetry/instrumentation';
|
|
6
6
|
import {
|
|
7
7
|
SessionType,
|
|
8
8
|
ATTR_MULTIPLAYER_SESSION_ID,
|
|
9
9
|
SessionRecorderIdGenerator,
|
|
10
10
|
SessionRecorderTraceIdRatioBasedSampler,
|
|
11
11
|
SessionRecorderBrowserTraceExporter,
|
|
12
|
-
} from '@multiplayer-app/session-recorder-common'
|
|
13
|
-
import { type TracerReactNativeConfig } from '../types'
|
|
14
|
-
import { getInstrumentations } from './instrumentations'
|
|
15
|
-
import { getExporterEndpoint } from './helpers'
|
|
16
|
-
|
|
17
|
-
import { getPlatformAttributes } from '../utils/platform'
|
|
18
|
-
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
} from '@multiplayer-app/session-recorder-common';
|
|
13
|
+
import { type TracerReactNativeConfig } from '../types';
|
|
14
|
+
import { getInstrumentations } from './instrumentations';
|
|
15
|
+
import { getExporterEndpoint } from './helpers';
|
|
21
16
|
|
|
17
|
+
import { getPlatformAttributes } from '../utils/platform';
|
|
18
|
+
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
|
|
22
19
|
|
|
23
20
|
export class TracerReactNativeSDK {
|
|
24
|
-
private tracerProvider?: WebTracerProvider
|
|
25
|
-
private config?: TracerReactNativeConfig
|
|
21
|
+
private tracerProvider?: WebTracerProvider;
|
|
22
|
+
private config?: TracerReactNativeConfig;
|
|
26
23
|
|
|
27
|
-
private sessionId = ''
|
|
28
|
-
private idGenerator?: SessionRecorderIdGenerator
|
|
29
|
-
private exporter?: any
|
|
24
|
+
private sessionId = '';
|
|
25
|
+
private idGenerator?: SessionRecorderIdGenerator;
|
|
26
|
+
private exporter?: any;
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
constructor() { }
|
|
28
|
+
constructor() {}
|
|
33
29
|
|
|
34
30
|
private _setSessionId(
|
|
35
31
|
sessionId: string,
|
|
36
|
-
sessionType: SessionType = SessionType.PLAIN
|
|
32
|
+
sessionType: SessionType = SessionType.PLAIN
|
|
37
33
|
) {
|
|
38
|
-
this.sessionId = sessionId
|
|
39
|
-
this.idGenerator?.setSessionId(sessionId, sessionType)
|
|
34
|
+
this.sessionId = sessionId;
|
|
35
|
+
this.idGenerator?.setSessionId(sessionId, sessionType);
|
|
40
36
|
}
|
|
41
37
|
|
|
42
38
|
init(options: TracerReactNativeConfig): void {
|
|
43
|
-
this.config = options
|
|
39
|
+
this.config = options;
|
|
44
40
|
|
|
45
|
-
const { application, version, environment } = this.config
|
|
41
|
+
const { application, version, environment } = this.config;
|
|
46
42
|
|
|
47
|
-
this.idGenerator = new SessionRecorderIdGenerator()
|
|
43
|
+
this.idGenerator = new SessionRecorderIdGenerator();
|
|
48
44
|
|
|
49
45
|
this.exporter = new SessionRecorderBrowserTraceExporter({
|
|
50
46
|
apiKey: options.apiKey,
|
|
51
47
|
url: getExporterEndpoint(options.exporterEndpoint),
|
|
52
|
-
})
|
|
48
|
+
});
|
|
53
49
|
|
|
54
50
|
this.tracerProvider = new WebTracerProvider({
|
|
55
51
|
resource: resourceFromAttributes({
|
|
@@ -59,77 +55,78 @@ export class TracerReactNativeSDK {
|
|
|
59
55
|
...getPlatformAttributes(),
|
|
60
56
|
}),
|
|
61
57
|
idGenerator: this.idGenerator,
|
|
62
|
-
sampler: new SessionRecorderTraceIdRatioBasedSampler(
|
|
58
|
+
sampler: new SessionRecorderTraceIdRatioBasedSampler(
|
|
59
|
+
this.config.sampleTraceRatio || 0.15
|
|
60
|
+
),
|
|
63
61
|
spanProcessors: [
|
|
64
62
|
this._getSpanSessionIdProcessor(),
|
|
65
63
|
new BatchSpanProcessor(this.exporter),
|
|
66
64
|
],
|
|
67
|
-
})
|
|
65
|
+
});
|
|
68
66
|
|
|
69
67
|
this.tracerProvider.register({
|
|
70
68
|
propagator: new W3CTraceContextPropagator(),
|
|
71
|
-
})
|
|
69
|
+
});
|
|
72
70
|
|
|
73
71
|
// Register instrumentations
|
|
74
72
|
registerInstrumentations({
|
|
75
73
|
tracerProvider: this.tracerProvider,
|
|
76
74
|
instrumentations: getInstrumentations(this.config),
|
|
77
|
-
})
|
|
78
|
-
|
|
75
|
+
});
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
private _getSpanSessionIdProcessor() {
|
|
82
79
|
return {
|
|
83
80
|
onStart: (span: any) => {
|
|
84
81
|
if (this.sessionId) {
|
|
85
|
-
span.setAttribute(ATTR_MULTIPLAYER_SESSION_ID, this.sessionId)
|
|
82
|
+
span.setAttribute(ATTR_MULTIPLAYER_SESSION_ID, this.sessionId);
|
|
86
83
|
}
|
|
87
84
|
// Add React Native specific attributes
|
|
88
|
-
span.setAttribute('platform', 'react-native')
|
|
89
|
-
span.setAttribute('timestamp', Date.now())
|
|
85
|
+
span.setAttribute('platform', 'react-native');
|
|
86
|
+
span.setAttribute('timestamp', Date.now());
|
|
90
87
|
},
|
|
91
|
-
onEnd: () => {
|
|
88
|
+
onEnd: () => {},
|
|
92
89
|
shutdown: () => Promise.resolve(),
|
|
93
90
|
forceFlush: () => Promise.resolve(),
|
|
94
|
-
}
|
|
91
|
+
};
|
|
95
92
|
}
|
|
96
93
|
|
|
97
94
|
start(sessionId: string, sessionType: SessionType): void {
|
|
98
95
|
if (!this.tracerProvider) {
|
|
99
96
|
throw new Error(
|
|
100
|
-
'Configuration not initialized. Call init() before start().'
|
|
101
|
-
)
|
|
97
|
+
'Configuration not initialized. Call init() before start().'
|
|
98
|
+
);
|
|
102
99
|
}
|
|
103
100
|
|
|
104
|
-
this._setSessionId(sessionId, sessionType)
|
|
101
|
+
this._setSessionId(sessionId, sessionType);
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
stop(): void {
|
|
108
105
|
if (!this.tracerProvider) {
|
|
109
106
|
throw new Error(
|
|
110
|
-
'Configuration not initialized. Call init() before start().'
|
|
111
|
-
)
|
|
107
|
+
'Configuration not initialized. Call init() before start().'
|
|
108
|
+
);
|
|
112
109
|
}
|
|
113
110
|
|
|
114
|
-
this._setSessionId('')
|
|
111
|
+
this._setSessionId('');
|
|
115
112
|
}
|
|
116
113
|
|
|
117
114
|
setApiKey(apiKey: string): void {
|
|
118
115
|
if (!this.exporter) {
|
|
119
116
|
throw new Error(
|
|
120
|
-
'Configuration not initialized. Call init() before setApiKey().'
|
|
121
|
-
)
|
|
117
|
+
'Configuration not initialized. Call init() before setApiKey().'
|
|
118
|
+
);
|
|
122
119
|
}
|
|
123
120
|
|
|
124
|
-
this.exporter.setApiKey?.(apiKey)
|
|
121
|
+
this.exporter.setApiKey?.(apiKey);
|
|
125
122
|
}
|
|
126
123
|
|
|
127
124
|
setSessionId(sessionId: string, sessionType: SessionType): void {
|
|
128
|
-
this._setSessionId(sessionId, sessionType)
|
|
125
|
+
this._setSessionId(sessionId, sessionType);
|
|
129
126
|
}
|
|
130
127
|
|
|
131
128
|
// Shutdown (React Native specific)
|
|
132
129
|
shutdown(): Promise<void> {
|
|
133
|
-
return Promise.resolve()
|
|
130
|
+
return Promise.resolve();
|
|
134
131
|
}
|
|
135
132
|
}
|