@multiplayer-app/session-recorder-react-native 1.0.1-beta.4 → 1.0.1-beta.5
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/src/main/java/com/multiplayer/sessionrecordernative/SessionRecorderNativeModule.kt +2 -2
- 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.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.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/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.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.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 +1 -1
- 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 +83 -67
- 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/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
|
}
|
|
@@ -1,42 +1,44 @@
|
|
|
1
|
-
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch'
|
|
2
|
-
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request'
|
|
3
|
-
|
|
4
|
-
import { logger } from '../../utils'
|
|
5
|
-
import { OTEL_IGNORE_URLS } from '../../config'
|
|
6
|
-
import { type TracerReactNativeConfig } from '../../types'
|
|
7
|
-
import {
|
|
1
|
+
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
|
|
2
|
+
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
|
|
3
|
+
|
|
4
|
+
import { logger } from '../../utils';
|
|
5
|
+
import { OTEL_IGNORE_URLS } from '../../config';
|
|
6
|
+
import { type TracerReactNativeConfig } from '../../types';
|
|
7
|
+
import {
|
|
8
|
+
extractResponseBody,
|
|
9
|
+
headersToObject,
|
|
10
|
+
processHttpPayload,
|
|
11
|
+
} from '../helpers';
|
|
8
12
|
|
|
9
13
|
export function getInstrumentations(config: TracerReactNativeConfig) {
|
|
10
|
-
|
|
11
|
-
const instrumentations = []
|
|
14
|
+
const instrumentations = [];
|
|
12
15
|
|
|
13
16
|
// Fetch instrumentation
|
|
14
17
|
try {
|
|
15
18
|
instrumentations.push(
|
|
16
19
|
new FetchInstrumentation({
|
|
17
20
|
clearTimingResources: false,
|
|
18
|
-
ignoreUrls: [
|
|
19
|
-
...OTEL_IGNORE_URLS,
|
|
20
|
-
...(config.ignoreUrls || []),
|
|
21
|
-
],
|
|
21
|
+
ignoreUrls: [...OTEL_IGNORE_URLS, ...(config.ignoreUrls || [])],
|
|
22
22
|
propagateTraceHeaderCorsUrls: config.propagateTraceHeaderCorsUrls,
|
|
23
23
|
applyCustomAttributesOnSpan: async (span, request, response) => {
|
|
24
|
-
if (!config) return
|
|
24
|
+
if (!config) return;
|
|
25
25
|
|
|
26
|
-
const { captureBody, captureHeaders } = config
|
|
26
|
+
const { captureBody, captureHeaders } = config;
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
29
|
if (!captureBody && !captureHeaders) {
|
|
30
|
-
return
|
|
30
|
+
return;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const requestBody = request.body
|
|
34
|
-
const requestHeaders = headersToObject(request.headers)
|
|
35
|
-
const responseHeaders = headersToObject(
|
|
33
|
+
const requestBody = request.body;
|
|
34
|
+
const requestHeaders = headersToObject(request.headers);
|
|
35
|
+
const responseHeaders = headersToObject(
|
|
36
|
+
response instanceof Response ? response.headers : undefined
|
|
37
|
+
);
|
|
36
38
|
|
|
37
|
-
let responseBody: string | null = null
|
|
39
|
+
let responseBody: string | null = null;
|
|
38
40
|
if (response instanceof Response && response.body) {
|
|
39
|
-
responseBody = await extractResponseBody(response)
|
|
41
|
+
responseBody = await extractResponseBody(response);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
const payload = {
|
|
@@ -44,17 +46,17 @@ export function getInstrumentations(config: TracerReactNativeConfig) {
|
|
|
44
46
|
responseBody,
|
|
45
47
|
requestHeaders,
|
|
46
48
|
responseHeaders,
|
|
47
|
-
}
|
|
48
|
-
processHttpPayload(payload, config, span)
|
|
49
|
+
};
|
|
50
|
+
processHttpPayload(payload, config, span);
|
|
49
51
|
} catch (error) {
|
|
50
52
|
// eslint-disable-next-line
|
|
51
53
|
logger.error('DEBUGGER_LIB', 'Failed to capture fetch payload', error)
|
|
52
54
|
}
|
|
53
55
|
},
|
|
54
56
|
})
|
|
55
|
-
)
|
|
57
|
+
);
|
|
56
58
|
} catch (error) {
|
|
57
|
-
logger.warn('DEBUGGER_LIB', 'Fetch instrumentation not available', error)
|
|
59
|
+
logger.warn('DEBUGGER_LIB', 'Fetch instrumentation not available', error);
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
// XMLHttpRequest instrumentation
|
|
@@ -62,46 +64,47 @@ export function getInstrumentations(config: TracerReactNativeConfig) {
|
|
|
62
64
|
instrumentations.push(
|
|
63
65
|
new XMLHttpRequestInstrumentation({
|
|
64
66
|
clearTimingResources: false,
|
|
65
|
-
ignoreUrls: [
|
|
66
|
-
...OTEL_IGNORE_URLS,
|
|
67
|
-
...(config.ignoreUrls || []),
|
|
68
|
-
],
|
|
67
|
+
ignoreUrls: [...OTEL_IGNORE_URLS, ...(config.ignoreUrls || [])],
|
|
69
68
|
propagateTraceHeaderCorsUrls: config.propagateTraceHeaderCorsUrls,
|
|
70
69
|
applyCustomAttributesOnSpan: (span, xhr) => {
|
|
71
|
-
if (!config) return
|
|
70
|
+
if (!config) return;
|
|
72
71
|
|
|
73
|
-
const { captureBody, captureHeaders } = config
|
|
72
|
+
const { captureBody, captureHeaders } = config;
|
|
74
73
|
|
|
75
74
|
try {
|
|
76
75
|
if (!captureBody && !captureHeaders) {
|
|
77
|
-
return
|
|
76
|
+
return;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
// @ts-ignore
|
|
81
|
-
const requestBody = xhr.networkRequest.requestBody
|
|
80
|
+
const requestBody = xhr.networkRequest.requestBody;
|
|
82
81
|
// @ts-ignore
|
|
83
|
-
const responseBody = xhr.networkRequest.responseBody
|
|
82
|
+
const responseBody = xhr.networkRequest.responseBody;
|
|
84
83
|
// @ts-ignore
|
|
85
|
-
const requestHeaders = xhr.networkRequest.requestHeaders || {}
|
|
84
|
+
const requestHeaders = xhr.networkRequest.requestHeaders || {};
|
|
86
85
|
// @ts-ignore
|
|
87
|
-
const responseHeaders = xhr.networkRequest.responseHeaders || {}
|
|
86
|
+
const responseHeaders = xhr.networkRequest.responseHeaders || {};
|
|
88
87
|
|
|
89
88
|
const payload = {
|
|
90
89
|
requestBody,
|
|
91
90
|
responseBody,
|
|
92
91
|
requestHeaders,
|
|
93
92
|
responseHeaders,
|
|
94
|
-
}
|
|
95
|
-
processHttpPayload(payload, config, span)
|
|
93
|
+
};
|
|
94
|
+
processHttpPayload(payload, config, span);
|
|
96
95
|
} catch (error) {
|
|
97
96
|
// eslint-disable-next-line
|
|
98
97
|
logger.error('DEBUGGER_LIB', 'Failed to capture xml-http payload', error)
|
|
99
98
|
}
|
|
100
99
|
},
|
|
101
100
|
})
|
|
102
|
-
)
|
|
101
|
+
);
|
|
103
102
|
} catch (error) {
|
|
104
|
-
logger.warn(
|
|
103
|
+
logger.warn(
|
|
104
|
+
'DEBUGGER_LIB',
|
|
105
|
+
'XMLHttpRequest instrumentation not available',
|
|
106
|
+
error
|
|
107
|
+
);
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
// Custom React Native instrumentations
|
|
@@ -111,5 +114,5 @@ export function getInstrumentations(config: TracerReactNativeConfig) {
|
|
|
111
114
|
// console.warn('React Native instrumentation not available:', error)
|
|
112
115
|
// }
|
|
113
116
|
|
|
114
|
-
return instrumentations
|
|
117
|
+
return instrumentations;
|
|
115
118
|
}
|
package/src/patch/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import './xhr'
|
|
1
|
+
import './xhr';
|
package/src/patch/xhr.ts
CHANGED
|
@@ -1,149 +1,148 @@
|
|
|
1
|
-
import { Platform } from 'react-native'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
isNullish,
|
|
6
|
-
isObject,
|
|
7
|
-
isString,
|
|
8
|
-
} from '../utils/type-utils'
|
|
9
|
-
import { formDataToQuery } from '../utils/request-utils'
|
|
10
|
-
import { DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE } from '../config'
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import { isFormData, isNullish, isObject, isString } from '../utils/type-utils';
|
|
3
|
+
import { formDataToQuery } from '../utils/request-utils';
|
|
4
|
+
import { DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE } from '../config';
|
|
11
5
|
|
|
12
6
|
// Check if we're on web platform
|
|
13
|
-
const isWeb = Platform.OS === 'web'
|
|
14
|
-
|
|
15
|
-
let recordRequestHeaders = true
|
|
16
|
-
let recordResponseHeaders = true
|
|
17
|
-
let shouldRecordBody = true
|
|
18
|
-
let maxCapturingHttpPayloadSize = DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE
|
|
19
|
-
|
|
20
|
-
export const setMaxCapturingHttpPayloadSize = (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
shouldRecordBody
|
|
28
|
-
|
|
7
|
+
const isWeb = Platform.OS === 'web';
|
|
8
|
+
|
|
9
|
+
let recordRequestHeaders = true;
|
|
10
|
+
let recordResponseHeaders = true;
|
|
11
|
+
let shouldRecordBody = true;
|
|
12
|
+
let maxCapturingHttpPayloadSize = DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE;
|
|
13
|
+
|
|
14
|
+
export const setMaxCapturingHttpPayloadSize = (
|
|
15
|
+
_maxCapturingHttpPayloadSize: number
|
|
16
|
+
) => {
|
|
17
|
+
maxCapturingHttpPayloadSize = _maxCapturingHttpPayloadSize;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const setShouldRecordHttpData = (
|
|
21
|
+
shouldRecordBody: boolean,
|
|
22
|
+
shouldRecordHeaders: boolean
|
|
23
|
+
) => {
|
|
24
|
+
recordRequestHeaders = shouldRecordHeaders;
|
|
25
|
+
recordResponseHeaders = shouldRecordHeaders;
|
|
26
|
+
shouldRecordBody = shouldRecordBody;
|
|
27
|
+
};
|
|
29
28
|
|
|
30
29
|
function _tryReadXHRBody({
|
|
31
30
|
body,
|
|
32
31
|
}: {
|
|
33
|
-
body: any | null | undefined
|
|
34
|
-
url: string | URL | RequestInfo
|
|
32
|
+
body: any | null | undefined;
|
|
33
|
+
url: string | URL | RequestInfo;
|
|
35
34
|
}): string | null {
|
|
36
|
-
|
|
37
35
|
if (isNullish(body)) {
|
|
38
|
-
return null
|
|
36
|
+
return null;
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
if (isString(body)) {
|
|
42
|
-
return body
|
|
40
|
+
return body;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
if (isFormData(body)) {
|
|
46
|
-
return formDataToQuery(body)
|
|
44
|
+
return formDataToQuery(body);
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
if (isObject(body)) {
|
|
50
48
|
try {
|
|
51
|
-
return JSON.stringify({ ...body })
|
|
49
|
+
return JSON.stringify({ ...body });
|
|
52
50
|
} catch {
|
|
53
|
-
return '[XHR] Failed to stringify response object'
|
|
51
|
+
return '[XHR] Failed to stringify response object';
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
return `[XHR] Cannot read body of type ${Object.prototype.toString.call(body)}
|
|
55
|
+
return `[XHR] Cannot read body of type ${Object.prototype.toString.call(body)}`;
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
// Only patch XMLHttpRequest if not on web platform or if XMLHttpRequest is available
|
|
61
59
|
if (!isWeb && typeof XMLHttpRequest !== 'undefined') {
|
|
62
60
|
(function (xhr) {
|
|
63
|
-
const originalOpen = XMLHttpRequest.prototype.open
|
|
61
|
+
const originalOpen = XMLHttpRequest.prototype.open;
|
|
64
62
|
|
|
65
63
|
xhr.open = function (
|
|
66
64
|
method: string,
|
|
67
65
|
url: string | URL,
|
|
68
66
|
async = true,
|
|
69
67
|
username?: string | null,
|
|
70
|
-
password?: string | null
|
|
68
|
+
password?: string | null
|
|
71
69
|
) {
|
|
72
|
-
const xhr = this as XMLHttpRequest
|
|
70
|
+
const xhr = this as XMLHttpRequest;
|
|
73
71
|
const networkRequest: {
|
|
74
|
-
requestHeaders?: any
|
|
75
|
-
requestBody?: any
|
|
76
|
-
responseHeaders?: any
|
|
77
|
-
responseBody?: any
|
|
78
|
-
} = {}
|
|
79
|
-
|
|
72
|
+
requestHeaders?: any;
|
|
73
|
+
requestBody?: any;
|
|
74
|
+
responseHeaders?: any;
|
|
75
|
+
responseBody?: any;
|
|
76
|
+
} = {};
|
|
80
77
|
|
|
81
78
|
// @ts-ignore
|
|
82
|
-
const requestHeaders: Record<string, string> = {}
|
|
83
|
-
const originalSetRequestHeader = xhr.setRequestHeader.bind(xhr)
|
|
79
|
+
const requestHeaders: Record<string, string> = {};
|
|
80
|
+
const originalSetRequestHeader = xhr.setRequestHeader.bind(xhr);
|
|
84
81
|
xhr.setRequestHeader = (header: string, value: string) => {
|
|
85
|
-
requestHeaders[header] = value
|
|
86
|
-
return originalSetRequestHeader(header, value)
|
|
87
|
-
}
|
|
82
|
+
requestHeaders[header] = value;
|
|
83
|
+
return originalSetRequestHeader(header, value);
|
|
84
|
+
};
|
|
88
85
|
if (recordRequestHeaders) {
|
|
89
|
-
networkRequest.requestHeaders = requestHeaders
|
|
86
|
+
networkRequest.requestHeaders = requestHeaders;
|
|
90
87
|
}
|
|
91
88
|
|
|
92
|
-
const originalSend = xhr.send.bind(xhr)
|
|
89
|
+
const originalSend = xhr.send.bind(xhr);
|
|
93
90
|
xhr.send = (body) => {
|
|
94
91
|
if (shouldRecordBody) {
|
|
95
|
-
const requestBody = _tryReadXHRBody({ body, url })
|
|
92
|
+
const requestBody = _tryReadXHRBody({ body, url });
|
|
96
93
|
|
|
97
94
|
if (
|
|
98
|
-
requestBody?.length
|
|
99
|
-
|
|
95
|
+
requestBody?.length &&
|
|
96
|
+
requestBody.length <= maxCapturingHttpPayloadSize
|
|
100
97
|
) {
|
|
101
|
-
networkRequest.requestBody = requestBody
|
|
98
|
+
networkRequest.requestBody = requestBody;
|
|
102
99
|
}
|
|
103
100
|
}
|
|
104
|
-
return originalSend(body)
|
|
105
|
-
}
|
|
101
|
+
return originalSend(body);
|
|
102
|
+
};
|
|
106
103
|
|
|
107
104
|
xhr.addEventListener('readystatechange', () => {
|
|
108
105
|
if (xhr.readyState !== xhr.DONE) {
|
|
109
|
-
return
|
|
106
|
+
return;
|
|
110
107
|
}
|
|
111
108
|
|
|
112
109
|
// @ts-ignore
|
|
113
|
-
const responseHeaders: Record<string, string> = {}
|
|
114
|
-
const rawHeaders = xhr.getAllResponseHeaders() || ''
|
|
115
|
-
const headers = rawHeaders
|
|
110
|
+
const responseHeaders: Record<string, string> = {};
|
|
111
|
+
const rawHeaders = xhr.getAllResponseHeaders() || '';
|
|
112
|
+
const headers = rawHeaders
|
|
113
|
+
.trim()
|
|
114
|
+
.split(/[\r\n]+/)
|
|
115
|
+
.filter(Boolean);
|
|
116
116
|
|
|
117
117
|
headers.forEach((line) => {
|
|
118
|
-
const parts = line.split(': ')
|
|
119
|
-
const header = parts.shift()
|
|
120
|
-
const value = parts.join(': ')
|
|
118
|
+
const parts = line.split(': ');
|
|
119
|
+
const header = parts.shift();
|
|
120
|
+
const value = parts.join(': ');
|
|
121
121
|
if (header) {
|
|
122
|
-
responseHeaders[header] = value
|
|
122
|
+
responseHeaders[header] = value;
|
|
123
123
|
}
|
|
124
|
-
})
|
|
124
|
+
});
|
|
125
125
|
if (recordResponseHeaders) {
|
|
126
|
-
networkRequest.responseHeaders = responseHeaders
|
|
126
|
+
networkRequest.responseHeaders = responseHeaders;
|
|
127
127
|
}
|
|
128
128
|
if (shouldRecordBody) {
|
|
129
|
-
const responseBody = _tryReadXHRBody({ body: xhr.response, url })
|
|
129
|
+
const responseBody = _tryReadXHRBody({ body: xhr.response, url });
|
|
130
130
|
|
|
131
131
|
if (
|
|
132
|
-
responseBody?.length
|
|
133
|
-
|
|
132
|
+
responseBody?.length &&
|
|
133
|
+
responseBody.length <= maxCapturingHttpPayloadSize
|
|
134
134
|
) {
|
|
135
|
-
networkRequest.responseBody = responseBody
|
|
135
|
+
networkRequest.responseBody = responseBody;
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
-
})
|
|
139
|
-
|
|
138
|
+
});
|
|
140
139
|
|
|
141
140
|
// @ts-ignore
|
|
142
|
-
xhr.networkRequest = networkRequest
|
|
141
|
+
xhr.networkRequest = networkRequest;
|
|
143
142
|
|
|
144
|
-
originalOpen.call(xhr, method, url as string, async, username, password)
|
|
145
|
-
}
|
|
146
|
-
})(XMLHttpRequest.prototype)
|
|
143
|
+
originalOpen.call(xhr, method, url as string, async, username, password);
|
|
144
|
+
};
|
|
145
|
+
})(XMLHttpRequest.prototype);
|
|
147
146
|
} else if (isWeb) {
|
|
148
|
-
console.info('XHR patch: Skipping XMLHttpRequest patching on web platform')
|
|
147
|
+
console.info('XHR patch: Skipping XMLHttpRequest patching on web platform');
|
|
149
148
|
}
|