@multiplayer-app/session-recorder-react-native 0.0.1-alpha.1 → 0.0.1-alpha.3
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/dist/config/constants.d.ts +19 -0
- package/dist/config/constants.js +1 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/config/defaults.d.ts +4 -0
- package/dist/config/defaults.js +1 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +1 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/masking.d.ts +2 -30
- package/dist/config/masking.js +1 -1
- package/dist/config/masking.js.map +1 -1
- package/dist/config/session-recorder.d.ts +2 -0
- package/dist/config/session-recorder.js +1 -0
- package/dist/config/session-recorder.js.map +1 -0
- package/dist/config/validators.d.ts +10 -0
- package/dist/config/validators.js +1 -0
- package/dist/config/validators.js.map +1 -0
- package/dist/expo.d.ts +2 -2
- package/dist/expo.js +1 -1
- package/dist/expo.js.map +1 -1
- package/dist/exporters.d.ts +3 -0
- package/dist/exporters.js +1 -0
- package/dist/exporters.js.map +1 -0
- package/dist/index.d.ts +3 -9
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/otel/helpers.d.ts +45 -3
- package/dist/otel/helpers.js +1 -1
- package/dist/otel/helpers.js.map +1 -1
- package/dist/otel/index.d.ts +9 -5
- package/dist/otel/index.js +1 -1
- package/dist/otel/index.js.map +1 -1
- package/dist/otel/instrumentations/index.js +1 -1
- package/dist/otel/instrumentations/index.js.map +1 -1
- package/dist/patch/index.d.ts +1 -0
- package/dist/patch/index.js +1 -0
- package/dist/patch/index.js.map +1 -0
- package/dist/patch/xhr.d.ts +2 -0
- package/dist/patch/xhr.js +1 -0
- package/dist/patch/xhr.js.map +1 -0
- package/dist/recorder/gestureRecorder.js +1 -1
- package/dist/recorder/gestureRecorder.js.map +1 -1
- package/dist/recorder/navigationTracker.js +1 -1
- package/dist/recorder/navigationTracker.js.map +1 -1
- package/dist/recorder/screenRecorder.d.ts +1 -0
- package/dist/recorder/screenRecorder.js +1 -1
- package/dist/recorder/screenRecorder.js.map +1 -1
- package/dist/services/api.service.d.ts +62 -10
- package/dist/services/api.service.js +1 -1
- package/dist/services/api.service.js.map +1 -1
- package/dist/services/storage.service.d.ts +23 -16
- package/dist/services/storage.service.js +1 -1
- package/dist/services/storage.service.js.map +1 -1
- package/dist/session-recorder.d.ts +131 -0
- package/dist/session-recorder.js +1 -0
- package/dist/session-recorder.js.map +1 -0
- package/dist/sessionRecorder.d.ts +113 -34
- package/dist/sessionRecorder.js +1 -1
- package/dist/sessionRecorder.js.map +1 -1
- package/dist/types/index.d.ts +2 -81
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/session-recorder.d.ts +366 -0
- package/dist/types/session-recorder.js +1 -0
- package/dist/types/session-recorder.js.map +1 -0
- package/dist/types/session.d.ts +59 -0
- package/dist/types/session.js +1 -0
- package/dist/types/session.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/platform.d.ts +5 -0
- package/dist/utils/platform.js +1 -1
- package/dist/utils/platform.js.map +1 -1
- package/dist/utils/request-utils.d.ts +21 -0
- package/dist/utils/request-utils.js +1 -0
- package/dist/utils/request-utils.js.map +1 -0
- package/dist/utils/session.d.ts +5 -0
- package/dist/utils/session.js +1 -0
- package/dist/utils/session.js.map +1 -0
- package/dist/utils/time.d.ts +4 -0
- package/dist/utils/time.js +1 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/type-utils.d.ts +16 -0
- package/dist/utils/type-utils.js +1 -0
- package/dist/utils/type-utils.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/config/constants.ts +60 -0
- package/src/config/defaults.ts +82 -0
- package/src/config/index.ts +6 -0
- package/src/config/masking.ts +10 -61
- package/src/config/session-recorder.ts +55 -0
- package/src/config/validators.ts +31 -0
- package/src/expo.ts +2 -22
- package/src/index.ts +3 -15
- package/src/otel/helpers.ts +247 -11
- package/src/otel/index.ts +109 -76
- package/src/otel/instrumentations/index.ts +8 -8
- package/src/patch/index.ts +1 -0
- package/src/patch/xhr.ts +142 -0
- package/src/recorder/gestureRecorder.ts +12 -12
- package/src/recorder/navigationTracker.ts +10 -10
- package/src/recorder/screenRecorder.ts +26 -26
- package/src/services/api.service.ts +169 -37
- package/src/services/storage.service.ts +101 -74
- package/src/session-recorder.ts +570 -0
- package/src/types/index.ts +2 -88
- package/src/types/session-recorder.ts +423 -0
- package/src/types/session.ts +65 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/platform.ts +13 -0
- package/src/utils/request-utils.ts +61 -0
- package/src/utils/session.ts +18 -0
- package/src/utils/time.ts +17 -0
- package/src/utils/type-utils.ts +75 -0
- package/src/version.ts +1 -1
- package/examples/sample-expo-app/README.md +0 -142
- package/examples/sample-expo-app/app/(tabs)/_layout.tsx +0 -60
- package/examples/sample-expo-app/app/(tabs)/explore.tsx +0 -110
- package/examples/sample-expo-app/app/(tabs)/index.tsx +0 -125
- package/examples/sample-expo-app/app/(tabs)/posts.tsx +0 -96
- package/examples/sample-expo-app/app/(tabs)/users.tsx +0 -131
- package/examples/sample-expo-app/app/+not-found.tsx +0 -32
- package/examples/sample-expo-app/app/_layout.tsx +0 -53
- package/examples/sample-expo-app/app/post/[id].tsx +0 -199
- package/examples/sample-expo-app/app/user/[id].tsx +0 -270
- package/examples/sample-expo-app/app.json +0 -42
- package/examples/sample-expo-app/assets/fonts/SpaceMono-Regular.ttf +0 -0
- package/examples/sample-expo-app/assets/images/adaptive-icon.png +0 -0
- package/examples/sample-expo-app/assets/images/favicon.png +0 -0
- package/examples/sample-expo-app/assets/images/icon.png +0 -0
- package/examples/sample-expo-app/assets/images/partial-react-logo.png +0 -0
- package/examples/sample-expo-app/assets/images/react-logo.png +0 -0
- package/examples/sample-expo-app/assets/images/react-logo@2x.png +0 -0
- package/examples/sample-expo-app/assets/images/react-logo@3x.png +0 -0
- package/examples/sample-expo-app/assets/images/splash-icon.png +0 -0
- package/examples/sample-expo-app/components/Collapsible.tsx +0 -45
- package/examples/sample-expo-app/components/ErrorView.tsx +0 -52
- package/examples/sample-expo-app/components/ExternalLink.tsx +0 -24
- package/examples/sample-expo-app/components/HapticTab.tsx +0 -18
- package/examples/sample-expo-app/components/HelloWave.tsx +0 -40
- package/examples/sample-expo-app/components/LoadingSpinner.tsx +0 -34
- package/examples/sample-expo-app/components/ParallaxScrollView.tsx +0 -82
- package/examples/sample-expo-app/components/ThemedText.tsx +0 -60
- package/examples/sample-expo-app/components/ThemedView.tsx +0 -14
- package/examples/sample-expo-app/components/ui/IconSymbol.ios.tsx +0 -32
- package/examples/sample-expo-app/components/ui/IconSymbol.tsx +0 -41
- package/examples/sample-expo-app/components/ui/TabBarBackground.ios.tsx +0 -19
- package/examples/sample-expo-app/components/ui/TabBarBackground.tsx +0 -6
- package/examples/sample-expo-app/constants/Colors.ts +0 -26
- package/examples/sample-expo-app/eslint.config.js +0 -10
- package/examples/sample-expo-app/hooks/useApi.ts +0 -41
- package/examples/sample-expo-app/hooks/useColorScheme.ts +0 -1
- package/examples/sample-expo-app/hooks/useColorScheme.web.ts +0 -21
- package/examples/sample-expo-app/hooks/useThemeColor.ts +0 -21
- package/examples/sample-expo-app/metro.config.js +0 -26
- package/examples/sample-expo-app/package-lock.json +0 -26296
- package/examples/sample-expo-app/package.json +0 -59
- package/examples/sample-expo-app/scripts/reset-project.js +0 -112
- package/examples/sample-expo-app/services/api.ts +0 -98
- package/examples/sample-expo-app/tsconfig.json +0 -17
- package/examples/sample-expo-app/utils/navigation.ts +0 -19
- package/src/sessionRecorder.ts +0 -367
package/src/otel/index.ts
CHANGED
|
@@ -8,95 +8,54 @@ import {
|
|
|
8
8
|
ATTR_MULTIPLAYER_SESSION_ID,
|
|
9
9
|
SessionRecorderIdGenerator,
|
|
10
10
|
SessionRecorderTraceIdRatioBasedSampler,
|
|
11
|
+
SessionRecorderBrowserTraceExporter,
|
|
11
12
|
} from '@multiplayer-app/session-recorder-common'
|
|
12
13
|
import { TracerReactNativeConfig } from '../types'
|
|
13
14
|
import { getInstrumentations } from './instrumentations'
|
|
14
|
-
import { getExporterEndpoint } from './helpers'
|
|
15
|
+
import { getExporterEndpoint, processHttpPayload, headersToObject } from './helpers'
|
|
15
16
|
import { ReactNavigationInstrumentation } from './instrumentations/reactNavigationInstrumentation'
|
|
16
17
|
import { GestureInstrumentation } from './instrumentations/gestureInstrumentation'
|
|
17
|
-
import { getHttpMaskingConfig } from '../config/masking'
|
|
18
18
|
import { getPlatformAttributes } from '../utils/platform'
|
|
19
|
+
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
// Create a custom TracerProvider for React Native
|
|
22
|
-
class ReactNativeTracerProvider {
|
|
23
|
-
private resource: any
|
|
24
|
-
private idGenerator: any
|
|
25
|
-
private sampler: any
|
|
26
|
-
private spanProcessors: any[]
|
|
27
|
-
private propagator: any
|
|
28
|
-
|
|
29
|
-
constructor(options: any) {
|
|
30
|
-
this.resource = options.resource
|
|
31
|
-
this.idGenerator = options.idGenerator
|
|
32
|
-
this.sampler = options.sampler
|
|
33
|
-
this.spanProcessors = options.spanProcessors
|
|
34
|
-
this.propagator = options.propagator
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
register(options: any) {
|
|
38
|
-
// Register the provider with OpenTelemetry
|
|
39
|
-
console.log('ReactNativeTracerProvider registered')
|
|
40
|
-
}
|
|
41
22
|
|
|
42
|
-
getTracer(name: string, version?: string, options?: any): any {
|
|
43
|
-
// Return a mock tracer for now
|
|
44
|
-
return {
|
|
45
|
-
startSpan: (name: string, options?: any) => ({
|
|
46
|
-
setAttribute: (key: string, value: any) => { },
|
|
47
|
-
setStatus: (status: any) => { },
|
|
48
|
-
end: () => { },
|
|
49
|
-
recordException: (error: Error) => { },
|
|
50
|
-
}),
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
23
|
|
|
55
24
|
export class TracerReactNativeSDK {
|
|
56
|
-
private tracerProvider?:
|
|
25
|
+
private tracerProvider?: WebTracerProvider
|
|
57
26
|
private config?: TracerReactNativeConfig
|
|
58
|
-
|
|
27
|
+
|
|
59
28
|
private sessionId = ''
|
|
60
|
-
private idGenerator
|
|
29
|
+
private idGenerator?: SessionRecorderIdGenerator
|
|
61
30
|
private exporter?: any
|
|
62
|
-
private navigationInstrumentation
|
|
63
|
-
private gestureInstrumentation
|
|
31
|
+
private navigationInstrumentation?: ReactNavigationInstrumentation
|
|
32
|
+
private gestureInstrumentation?: GestureInstrumentation
|
|
64
33
|
private isInitialized = false
|
|
65
34
|
|
|
66
|
-
constructor() {
|
|
67
|
-
this.idGenerator = new SessionRecorderIdGenerator()
|
|
68
|
-
this.navigationInstrumentation = new ReactNavigationInstrumentation()
|
|
69
|
-
this.gestureInstrumentation = new GestureInstrumentation()
|
|
70
|
-
}
|
|
35
|
+
constructor() { }
|
|
71
36
|
|
|
72
37
|
private _setSessionId(
|
|
73
38
|
sessionId: string,
|
|
74
39
|
sessionType: SessionType = SessionType.PLAIN,
|
|
75
40
|
) {
|
|
76
41
|
this.sessionId = sessionId
|
|
77
|
-
this.idGenerator
|
|
42
|
+
this.idGenerator?.setSessionId(sessionId, sessionType)
|
|
78
43
|
}
|
|
79
44
|
|
|
80
45
|
init(options: TracerReactNativeConfig): void {
|
|
81
|
-
if (this.isInitialized) {
|
|
82
|
-
console.warn('TracerReactNativeSDK already initialized')
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
|
|
86
46
|
this.config = options
|
|
87
|
-
|
|
47
|
+
|
|
88
48
|
const { application, version, environment } = this.config
|
|
89
49
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.exporter = new
|
|
50
|
+
this.idGenerator = new SessionRecorderIdGenerator()
|
|
51
|
+
|
|
52
|
+
this.exporter = new SessionRecorderBrowserTraceExporter({
|
|
53
|
+
apiKey: options.apiKey,
|
|
93
54
|
url: getExporterEndpoint(options.exporterEndpoint),
|
|
94
|
-
|
|
95
|
-
'Authorization': `Bearer ${options.apiKey}`,
|
|
96
|
-
},
|
|
55
|
+
usePostMessageFallback: options.usePostMessageFallback,
|
|
97
56
|
})
|
|
98
57
|
|
|
99
|
-
this.tracerProvider = new
|
|
58
|
+
this.tracerProvider = new WebTracerProvider({
|
|
100
59
|
resource: resourceFromAttributes({
|
|
101
60
|
[SemanticAttributes.SEMRESATTRS_SERVICE_NAME]: application,
|
|
102
61
|
[SemanticAttributes.SEMRESATTRS_SERVICE_VERSION]: version,
|
|
@@ -121,8 +80,11 @@ export class TracerReactNativeSDK {
|
|
|
121
80
|
instrumentations: getInstrumentations(this.config),
|
|
122
81
|
})
|
|
123
82
|
|
|
83
|
+
// Initialize React Native specific instrumentations
|
|
84
|
+
this.navigationInstrumentation = new ReactNavigationInstrumentation()
|
|
85
|
+
this.gestureInstrumentation = new GestureInstrumentation()
|
|
86
|
+
|
|
124
87
|
this.isInitialized = true
|
|
125
|
-
console.log('TracerReactNativeSDK initialized successfully')
|
|
126
88
|
}
|
|
127
89
|
|
|
128
90
|
private _getSpanSessionIdProcessor() {
|
|
@@ -141,64 +103,94 @@ export class TracerReactNativeSDK {
|
|
|
141
103
|
}
|
|
142
104
|
}
|
|
143
105
|
|
|
106
|
+
start(sessionId: string, sessionType: SessionType): void {
|
|
107
|
+
if (!this.tracerProvider) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
'Configuration not initialized. Call init() before start().',
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this._setSessionId(sessionId, sessionType)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
stop(): void {
|
|
117
|
+
if (!this.tracerProvider) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
'Configuration not initialized. Call init() before start().',
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this._setSessionId('')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
setApiKey(apiKey: string): void {
|
|
127
|
+
if (!this.exporter) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
'Configuration not initialized. Call init() before setApiKey().',
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.exporter.setApiKey?.(apiKey)
|
|
134
|
+
}
|
|
135
|
+
|
|
144
136
|
setSessionId(sessionId: string, sessionType: SessionType): void {
|
|
145
137
|
this._setSessionId(sessionId, sessionType)
|
|
146
138
|
}
|
|
147
139
|
|
|
148
140
|
// Navigation instrumentation methods
|
|
149
141
|
setNavigationRef(ref: any): void {
|
|
150
|
-
this.navigationInstrumentation
|
|
142
|
+
this.navigationInstrumentation?.setNavigationRef(ref)
|
|
151
143
|
}
|
|
152
144
|
|
|
153
145
|
recordNavigate(routeName: string, params?: Record<string, any>): void {
|
|
154
|
-
this.navigationInstrumentation
|
|
146
|
+
this.navigationInstrumentation?.recordNavigate(routeName, params)
|
|
155
147
|
}
|
|
156
148
|
|
|
157
149
|
recordGoBack(): void {
|
|
158
|
-
this.navigationInstrumentation
|
|
150
|
+
this.navigationInstrumentation?.recordGoBack()
|
|
159
151
|
}
|
|
160
152
|
|
|
161
153
|
recordReset(routes: any[]): void {
|
|
162
|
-
this.navigationInstrumentation
|
|
154
|
+
this.navigationInstrumentation?.recordReset(routes)
|
|
163
155
|
}
|
|
164
156
|
|
|
165
157
|
// Gesture instrumentation methods
|
|
166
158
|
enableGestureTracking(): void {
|
|
167
|
-
this.gestureInstrumentation
|
|
159
|
+
this.gestureInstrumentation?.enable()
|
|
168
160
|
}
|
|
169
161
|
|
|
170
162
|
disableGestureTracking(): void {
|
|
171
|
-
this.gestureInstrumentation
|
|
163
|
+
this.gestureInstrumentation?.disable()
|
|
172
164
|
}
|
|
173
165
|
|
|
174
166
|
recordTap(x: number, y: number, target?: string): void {
|
|
175
|
-
this.gestureInstrumentation
|
|
167
|
+
this.gestureInstrumentation?.recordTap(x, y, target)
|
|
176
168
|
}
|
|
177
169
|
|
|
178
170
|
recordSwipe(direction: string, target?: string): void {
|
|
179
|
-
this.gestureInstrumentation
|
|
171
|
+
this.gestureInstrumentation?.recordSwipe(direction, target)
|
|
180
172
|
}
|
|
181
173
|
|
|
182
174
|
recordPinch(scale: number, target?: string): void {
|
|
183
|
-
this.gestureInstrumentation
|
|
175
|
+
this.gestureInstrumentation?.recordPinch(scale, target)
|
|
184
176
|
}
|
|
185
177
|
|
|
186
178
|
recordPan(deltaX: number, deltaY: number, target?: string): void {
|
|
187
|
-
this.gestureInstrumentation
|
|
179
|
+
this.gestureInstrumentation?.recordPan(deltaX, deltaY, target)
|
|
188
180
|
}
|
|
189
181
|
|
|
190
182
|
recordLongPress(duration: number, target?: string): void {
|
|
191
|
-
this.gestureInstrumentation
|
|
183
|
+
this.gestureInstrumentation?.recordLongPress(duration, target)
|
|
192
184
|
}
|
|
193
185
|
|
|
194
186
|
recordGestureError(error: Error, gestureType: string): void {
|
|
195
|
-
this.gestureInstrumentation
|
|
187
|
+
this.gestureInstrumentation?.recordGestureError(error, gestureType)
|
|
196
188
|
}
|
|
197
189
|
|
|
198
190
|
// Performance monitoring methods
|
|
199
191
|
startTrace(name: string, attributes?: Record<string, any>): any {
|
|
200
192
|
if (!this.isInitialized) {
|
|
201
|
-
|
|
193
|
+
// Tracer not initialized - silently return
|
|
202
194
|
return null
|
|
203
195
|
}
|
|
204
196
|
|
|
@@ -242,8 +234,8 @@ export class TracerReactNativeSDK {
|
|
|
242
234
|
span.end()
|
|
243
235
|
}
|
|
244
236
|
|
|
245
|
-
// Network monitoring
|
|
246
|
-
recordNetworkRequest(url: string, method: string, statusCode: number, duration: number): void {
|
|
237
|
+
// Network monitoring with enhanced HTTP payload processing
|
|
238
|
+
recordNetworkRequest(url: string, method: string, statusCode: number, duration: number, payload?: any): void {
|
|
247
239
|
if (!this.isInitialized) return
|
|
248
240
|
|
|
249
241
|
const { trace } = require('@opentelemetry/api')
|
|
@@ -257,6 +249,48 @@ export class TracerReactNativeSDK {
|
|
|
257
249
|
},
|
|
258
250
|
})
|
|
259
251
|
|
|
252
|
+
// Process HTTP payload if provided
|
|
253
|
+
if (payload && this.config) {
|
|
254
|
+
processHttpPayload(payload, this.config, span)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
span.setStatus({ code: statusCode >= 400 ? 2 : 1 })
|
|
258
|
+
span.end()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Enhanced HTTP request recording with headers and body
|
|
262
|
+
recordHttpRequest(url: string, method: string, requestHeaders?: Record<string, string>, requestBody?: any): any {
|
|
263
|
+
if (!this.isInitialized) return null
|
|
264
|
+
|
|
265
|
+
const { trace } = require('@opentelemetry/api')
|
|
266
|
+
const span = trace.startSpan('http.request', {
|
|
267
|
+
attributes: {
|
|
268
|
+
'http.url': url,
|
|
269
|
+
'http.method': method,
|
|
270
|
+
'http.request_headers': headersToObject(requestHeaders),
|
|
271
|
+
},
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
// Process request payload
|
|
275
|
+
if (requestBody && this.config) {
|
|
276
|
+
processHttpPayload({ requestBody, requestHeaders }, this.config, span)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return span
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Enhanced HTTP response recording
|
|
283
|
+
recordHttpResponse(span: any, statusCode: number, responseHeaders?: Record<string, string>, responseBody?: any): void {
|
|
284
|
+
if (!span || !this.isInitialized) return
|
|
285
|
+
|
|
286
|
+
span.setAttribute('http.status_code', statusCode)
|
|
287
|
+
span.setAttribute('http.response_headers', headersToObject(responseHeaders))
|
|
288
|
+
|
|
289
|
+
// Process response payload
|
|
290
|
+
if (responseBody && this.config) {
|
|
291
|
+
processHttpPayload({ responseBody, responseHeaders }, this.config, span)
|
|
292
|
+
}
|
|
293
|
+
|
|
260
294
|
span.setStatus({ code: statusCode >= 400 ? 2 : 1 })
|
|
261
295
|
span.end()
|
|
262
296
|
}
|
|
@@ -339,10 +373,9 @@ export class TracerReactNativeSDK {
|
|
|
339
373
|
span.end()
|
|
340
374
|
}
|
|
341
375
|
|
|
342
|
-
// Shutdown
|
|
376
|
+
// Shutdown (React Native specific)
|
|
343
377
|
shutdown(): Promise<void> {
|
|
344
378
|
this.isInitialized = false
|
|
345
|
-
console.log('TracerReactNativeSDK shutdown')
|
|
346
379
|
return Promise.resolve()
|
|
347
380
|
}
|
|
348
381
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { TracerReactNativeConfig } from '../../types'
|
|
2
2
|
import { ReactNativeInstrumentation } from './reactNativeInstrumentation'
|
|
3
|
-
import {
|
|
3
|
+
import { getMaskingConfig } from '../../config/masking'
|
|
4
4
|
|
|
5
5
|
export function getInstrumentations(config: TracerReactNativeConfig) {
|
|
6
|
-
const
|
|
6
|
+
const masking = getMaskingConfig(config.masking)
|
|
7
7
|
const instrumentations = []
|
|
8
8
|
|
|
9
9
|
// Fetch instrumentation
|
|
@@ -16,8 +16,8 @@ export function getInstrumentations(config: TracerReactNativeConfig) {
|
|
|
16
16
|
applyCustomAttributesOnSpan: (span: any, request: any) => {
|
|
17
17
|
if (config.captureHeaders) {
|
|
18
18
|
const headers = request.headers
|
|
19
|
-
if (headers &&
|
|
20
|
-
const maskedHeaders =
|
|
19
|
+
if (headers && masking.maskHeaders) {
|
|
20
|
+
const maskedHeaders = masking.maskHeaders(headers, span)
|
|
21
21
|
Object.keys(maskedHeaders).forEach(key => {
|
|
22
22
|
span.setAttribute(`http.request.header.${key}`, maskedHeaders[key])
|
|
23
23
|
})
|
|
@@ -29,8 +29,8 @@ export function getInstrumentations(config: TracerReactNativeConfig) {
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
applyCustomAttributesOnSpanResponse: (span: any, response: any) => {
|
|
32
|
-
if (config.captureBody &&
|
|
33
|
-
const maskedBody =
|
|
32
|
+
if (config.captureBody && masking.maskBody) {
|
|
33
|
+
const maskedBody = masking.maskBody(response.body, span)
|
|
34
34
|
span.setAttribute('http.response.body', JSON.stringify(maskedBody))
|
|
35
35
|
} else if (config.captureBody) {
|
|
36
36
|
span.setAttribute('http.response.body', JSON.stringify(response.body))
|
|
@@ -52,8 +52,8 @@ export function getInstrumentations(config: TracerReactNativeConfig) {
|
|
|
52
52
|
applyCustomAttributesOnSpan: (span: any, xhr: any) => {
|
|
53
53
|
if (config.captureHeaders) {
|
|
54
54
|
const headers = xhr.getAllResponseHeaders()
|
|
55
|
-
if (headers &&
|
|
56
|
-
const maskedHeaders =
|
|
55
|
+
if (headers && masking.maskHeaders) {
|
|
56
|
+
const maskedHeaders = masking.maskHeaders(headers, span)
|
|
57
57
|
Object.keys(maskedHeaders).forEach(key => {
|
|
58
58
|
span.setAttribute(`http.response.header.${key}`, maskedHeaders[key])
|
|
59
59
|
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './xhr'
|
package/src/patch/xhr.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
|
|
3
|
+
isFormData,
|
|
4
|
+
isNullish,
|
|
5
|
+
isObject,
|
|
6
|
+
isString,
|
|
7
|
+
} from '../utils/type-utils'
|
|
8
|
+
import { formDataToQuery } from '../utils/request-utils'
|
|
9
|
+
import { DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE } from '../config'
|
|
10
|
+
|
|
11
|
+
let recordRequestHeaders = true
|
|
12
|
+
let recordResponseHeaders = true
|
|
13
|
+
const shouldRecordBody = true
|
|
14
|
+
let maxCapturingHttpPayloadSize = DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE
|
|
15
|
+
|
|
16
|
+
export const setMaxCapturingHttpPayloadSize = (_maxCapturingHttpPayloadSize: number) => {
|
|
17
|
+
maxCapturingHttpPayloadSize = _maxCapturingHttpPayloadSize
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const setShouldRecordHttpData = (shouldRecordBody: boolean, shouldRecordHeaders: boolean) => {
|
|
21
|
+
recordRequestHeaders = shouldRecordHeaders
|
|
22
|
+
recordResponseHeaders = shouldRecordHeaders
|
|
23
|
+
// eslint-disable-next-line
|
|
24
|
+
shouldRecordBody = shouldRecordBody
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function _tryReadXHRBody({
|
|
28
|
+
body,
|
|
29
|
+
url,
|
|
30
|
+
}: {
|
|
31
|
+
body: any | null | undefined
|
|
32
|
+
url: string | URL | RequestInfo
|
|
33
|
+
}): string | null {
|
|
34
|
+
if (isNullish(body)) {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isString(body)) {
|
|
39
|
+
return body
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if (isFormData(body)) {
|
|
44
|
+
return formDataToQuery(body)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (isObject(body)) {
|
|
48
|
+
try {
|
|
49
|
+
return JSON.stringify(body)
|
|
50
|
+
} catch {
|
|
51
|
+
return '[XHR] Failed to stringify response object'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return `[XHR] Cannot read body of type ${Object.prototype.toString.call(body)}`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
(function (xhr) {
|
|
59
|
+
const originalOpen = XMLHttpRequest.prototype.open
|
|
60
|
+
|
|
61
|
+
xhr.open = function (
|
|
62
|
+
method: string,
|
|
63
|
+
url: string | URL,
|
|
64
|
+
async = true,
|
|
65
|
+
username?: string | null,
|
|
66
|
+
password?: string | null,
|
|
67
|
+
) {
|
|
68
|
+
const xhr = this as XMLHttpRequest
|
|
69
|
+
const networkRequest: {
|
|
70
|
+
requestHeaders?: any,
|
|
71
|
+
requestBody?: any,
|
|
72
|
+
responseHeaders?: any,
|
|
73
|
+
responseBody?: any,
|
|
74
|
+
} = {}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
const requestHeaders: Record<string, string> = {}
|
|
79
|
+
const originalSetRequestHeader = xhr.setRequestHeader.bind(xhr)
|
|
80
|
+
xhr.setRequestHeader = (header: string, value: string) => {
|
|
81
|
+
requestHeaders[header] = value
|
|
82
|
+
return originalSetRequestHeader(header, value)
|
|
83
|
+
}
|
|
84
|
+
if (recordRequestHeaders) {
|
|
85
|
+
networkRequest.requestHeaders = requestHeaders
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const originalSend = xhr.send.bind(xhr)
|
|
89
|
+
xhr.send = (body) => {
|
|
90
|
+
if (shouldRecordBody) {
|
|
91
|
+
const requestBody = _tryReadXHRBody({ body, url })
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
requestBody?.length
|
|
95
|
+
&& new Blob([requestBody]).size <= maxCapturingHttpPayloadSize
|
|
96
|
+
) {
|
|
97
|
+
networkRequest.requestBody = requestBody
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return originalSend(body)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
xhr.addEventListener('readystatechange', () => {
|
|
104
|
+
if (xhr.readyState !== xhr.DONE) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
const responseHeaders: Record<string, string> = {}
|
|
111
|
+
const rawHeaders = xhr.getAllResponseHeaders()
|
|
112
|
+
const headers = rawHeaders.trim().split(/[\r\n]+/)
|
|
113
|
+
headers.forEach((line) => {
|
|
114
|
+
const parts = line.split(': ')
|
|
115
|
+
const header = parts.shift()
|
|
116
|
+
const value = parts.join(': ')
|
|
117
|
+
if (header) {
|
|
118
|
+
responseHeaders[header] = value
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
if (recordResponseHeaders) {
|
|
122
|
+
networkRequest.responseHeaders = responseHeaders
|
|
123
|
+
}
|
|
124
|
+
if (shouldRecordBody) {
|
|
125
|
+
const responseBody = _tryReadXHRBody({ body: xhr.response, url })
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
responseBody?.length
|
|
129
|
+
&& new Blob([responseBody]).size <= maxCapturingHttpPayloadSize
|
|
130
|
+
) {
|
|
131
|
+
networkRequest.responseBody = responseBody
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
// @ts-ignore
|
|
138
|
+
xhr.networkRequest = networkRequest
|
|
139
|
+
|
|
140
|
+
originalOpen.call(xhr, method, url as string, async, username, password)
|
|
141
|
+
}
|
|
142
|
+
})(XMLHttpRequest.prototype)
|
|
@@ -18,13 +18,13 @@ export class GestureRecorder {
|
|
|
18
18
|
this.isRecording = true
|
|
19
19
|
this.events = []
|
|
20
20
|
this._setupGestureHandlers()
|
|
21
|
-
|
|
21
|
+
// Gesture recording started
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
stop(): void {
|
|
25
25
|
this.isRecording = false
|
|
26
26
|
this._removeGestureHandlers()
|
|
27
|
-
|
|
27
|
+
// Gesture recording stopped
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
pause(): void {
|
|
@@ -42,7 +42,7 @@ export class GestureRecorder {
|
|
|
42
42
|
const { Dimensions } = require('react-native')
|
|
43
43
|
this.screenDimensions = Dimensions.get('window')
|
|
44
44
|
} catch (error) {
|
|
45
|
-
|
|
45
|
+
// Failed to get screen dimensions - silently continue
|
|
46
46
|
this.screenDimensions = { width: 375, height: 667 } // Default fallback
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -52,16 +52,16 @@ export class GestureRecorder {
|
|
|
52
52
|
try {
|
|
53
53
|
// This would integrate with React Native's component tracking
|
|
54
54
|
// For now, we'll provide methods for manual registration
|
|
55
|
-
|
|
55
|
+
// Input tracking setup complete
|
|
56
56
|
} catch (error) {
|
|
57
|
-
|
|
57
|
+
// Failed to setup input tracking - silently continue
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
private _setupGestureHandlers(): void {
|
|
62
62
|
// This would integrate with react-native-gesture-handler
|
|
63
63
|
// For now, we'll create a comprehensive implementation that can be easily integrated
|
|
64
|
-
|
|
64
|
+
// Setting up gesture handlers
|
|
65
65
|
|
|
66
66
|
// Set up global gesture listener
|
|
67
67
|
this._setupGlobalGestureListener()
|
|
@@ -73,15 +73,15 @@ export class GestureRecorder {
|
|
|
73
73
|
const { TouchableWithoutFeedback } = require('react-native')
|
|
74
74
|
|
|
75
75
|
// This is a simplified implementation - in production you'd use react-native-gesture-handler
|
|
76
|
-
|
|
76
|
+
// Global gesture listener setup complete
|
|
77
77
|
} catch (error) {
|
|
78
|
-
|
|
78
|
+
// Failed to setup global gesture listener - silently continue
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
private _removeGestureHandlers(): void {
|
|
83
83
|
this.gestureHandlers.clear()
|
|
84
|
-
|
|
84
|
+
// Gesture handlers removed
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
private _recordEvent(event: GestureEvent): void {
|
|
@@ -103,7 +103,7 @@ export class GestureRecorder {
|
|
|
103
103
|
|
|
104
104
|
private _sendEvent(event: GestureEvent): void {
|
|
105
105
|
// Send event to backend or store locally
|
|
106
|
-
|
|
106
|
+
// Gesture event recorded
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
private _recordOpenTelemetrySpan(event: GestureEvent): void {
|
|
@@ -142,7 +142,7 @@ export class GestureRecorder {
|
|
|
142
142
|
span.setStatus({ code: SpanStatusCode.OK })
|
|
143
143
|
span.end()
|
|
144
144
|
} catch (error) {
|
|
145
|
-
|
|
145
|
+
// Failed to record OpenTelemetry span for gesture - silently continue
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -376,7 +376,7 @@ export class GestureRecorder {
|
|
|
376
376
|
span.recordException(error)
|
|
377
377
|
span.end()
|
|
378
378
|
} catch (spanError) {
|
|
379
|
-
|
|
379
|
+
// Failed to record error span - silently continue
|
|
380
380
|
}
|
|
381
381
|
}
|
|
382
382
|
|
|
@@ -28,13 +28,13 @@ export class NavigationTracker {
|
|
|
28
28
|
this.navigationStack = []
|
|
29
29
|
this.navigationStartTime = Date.now()
|
|
30
30
|
this._setupNavigationListener()
|
|
31
|
-
|
|
31
|
+
// Navigation tracking started
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
stop(): void {
|
|
35
35
|
this.isRecording = false
|
|
36
36
|
this._removeNavigationListener()
|
|
37
|
-
|
|
37
|
+
// Navigation tracking stopped
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
pause(): void {
|
|
@@ -48,7 +48,7 @@ export class NavigationTracker {
|
|
|
48
48
|
|
|
49
49
|
private _setupNavigationListener(): void {
|
|
50
50
|
if (!this.navigationRef) {
|
|
51
|
-
|
|
51
|
+
// Navigation ref not set - silently continue
|
|
52
52
|
return
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -79,9 +79,9 @@ export class NavigationTracker {
|
|
|
79
79
|
this.navigationListeners.set('blur', blurListener)
|
|
80
80
|
this.navigationListeners.set('beforeRemove', beforeRemoveListener)
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
// Navigation listeners setup complete
|
|
83
83
|
} catch (error) {
|
|
84
|
-
|
|
84
|
+
// Failed to setup navigation listeners - silently continue
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -94,9 +94,9 @@ export class NavigationTracker {
|
|
|
94
94
|
}
|
|
95
95
|
})
|
|
96
96
|
this.navigationListeners.clear()
|
|
97
|
-
|
|
97
|
+
// Navigation listeners removed
|
|
98
98
|
} catch (error) {
|
|
99
|
-
|
|
99
|
+
// Failed to remove navigation listeners - silently continue
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -148,7 +148,7 @@ export class NavigationTracker {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
private _sendEvent(event: NavigationEvent): void {
|
|
151
|
-
|
|
151
|
+
// Navigation event recorded
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
private _recordOpenTelemetrySpan(event: NavigationEvent): void {
|
|
@@ -178,7 +178,7 @@ export class NavigationTracker {
|
|
|
178
178
|
span.setStatus({ code: SpanStatusCode.OK })
|
|
179
179
|
span.end()
|
|
180
180
|
} catch (error) {
|
|
181
|
-
|
|
181
|
+
// Failed to record OpenTelemetry span for navigation - silently continue
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -399,7 +399,7 @@ export class NavigationTracker {
|
|
|
399
399
|
span.recordException(error)
|
|
400
400
|
span.end()
|
|
401
401
|
} catch (spanError) {
|
|
402
|
-
|
|
402
|
+
// Failed to record error span - silently continue
|
|
403
403
|
}
|
|
404
404
|
}
|
|
405
405
|
|