@rejourneyco/react-native 1.0.8 → 1.0.10
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/README.md +77 -3
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +143 -8
- package/android/src/main/java/com/rejourney/RejourneyOkHttpInitProvider.kt +68 -0
- package/android/src/main/java/com/rejourney/engine/DeviceRegistrar.kt +21 -3
- package/android/src/main/java/com/rejourney/engine/RejourneyImpl.kt +69 -17
- package/android/src/main/java/com/rejourney/recording/AnrSentinel.kt +27 -2
- package/android/src/main/java/com/rejourney/recording/InteractionRecorder.kt +3 -1
- package/android/src/main/java/com/rejourney/recording/RejourneyNetworkInterceptor.kt +93 -0
- package/android/src/main/java/com/rejourney/recording/ReplayOrchestrator.kt +226 -146
- package/android/src/main/java/com/rejourney/recording/SegmentDispatcher.kt +7 -0
- package/android/src/main/java/com/rejourney/recording/StabilityMonitor.kt +3 -0
- package/android/src/main/java/com/rejourney/recording/TelemetryPipeline.kt +39 -0
- package/android/src/main/java/com/rejourney/recording/ViewHierarchyScanner.kt +8 -0
- package/android/src/main/java/com/rejourney/recording/VisualCapture.kt +95 -21
- package/android/src/main/java/com/rejourney/utility/DataCompression.kt +14 -2
- package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +14 -0
- package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +18 -0
- package/ios/Engine/DeviceRegistrar.swift +13 -3
- package/ios/Engine/RejourneyImpl.swift +204 -115
- package/ios/Recording/AnrSentinel.swift +58 -25
- package/ios/Recording/InteractionRecorder.swift +1 -0
- package/ios/Recording/RejourneyURLProtocol.swift +216 -0
- package/ios/Recording/ReplayOrchestrator.swift +207 -144
- package/ios/Recording/SegmentDispatcher.swift +8 -0
- package/ios/Recording/StabilityMonitor.swift +40 -32
- package/ios/Recording/TelemetryPipeline.swift +45 -2
- package/ios/Recording/ViewHierarchyScanner.swift +1 -0
- package/ios/Recording/VisualCapture.swift +79 -29
- package/ios/Rejourney.mm +27 -8
- package/ios/Utility/DataCompression.swift +2 -2
- package/ios/Utility/ImageBlur.swift +0 -1
- package/lib/commonjs/expoRouterTracking.js +137 -0
- package/lib/commonjs/index.js +204 -34
- package/lib/commonjs/sdk/autoTracking.js +262 -100
- package/lib/commonjs/sdk/networkInterceptor.js +84 -4
- package/lib/module/expoRouterTracking.js +135 -0
- package/lib/module/index.js +203 -28
- package/lib/module/sdk/autoTracking.js +260 -100
- package/lib/module/sdk/networkInterceptor.js +84 -4
- package/lib/typescript/NativeRejourney.d.ts +5 -2
- package/lib/typescript/expoRouterTracking.d.ts +14 -0
- package/lib/typescript/index.d.ts +2 -2
- package/lib/typescript/sdk/autoTracking.d.ts +14 -1
- package/lib/typescript/types/index.d.ts +56 -5
- package/package.json +23 -3
- package/src/NativeRejourney.ts +8 -5
- package/src/expoRouterTracking.ts +167 -0
- package/src/index.ts +221 -35
- package/src/sdk/autoTracking.ts +286 -114
- package/src/sdk/networkInterceptor.ts +110 -1
- package/src/types/index.ts +58 -6
|
@@ -55,6 +55,11 @@ class AnrSentinel private constructor() {
|
|
|
55
55
|
|
|
56
56
|
fun activate() {
|
|
57
57
|
if (isActive.getAndSet(true)) return
|
|
58
|
+
|
|
59
|
+
// Reset watchdog state on each activation to avoid stale timings from
|
|
60
|
+
// previous app background periods.
|
|
61
|
+
lastResponseTime.set(System.currentTimeMillis())
|
|
62
|
+
pongSequence.set(pingSequence.get())
|
|
58
63
|
|
|
59
64
|
startWatchdog()
|
|
60
65
|
}
|
|
@@ -87,7 +92,7 @@ class AnrSentinel private constructor() {
|
|
|
87
92
|
val elapsed = System.currentTimeMillis() - lastResponseTime.get()
|
|
88
93
|
val missedPongs = pingSequence.get() - pongSequence.get()
|
|
89
94
|
|
|
90
|
-
if (elapsed
|
|
95
|
+
if (elapsed >= anrThresholdMs && missedPongs > 0) {
|
|
91
96
|
captureAnr(elapsed)
|
|
92
97
|
|
|
93
98
|
// Reset to avoid duplicate reports
|
|
@@ -113,12 +118,32 @@ class AnrSentinel private constructor() {
|
|
|
113
118
|
"${element.className}.${element.methodName}(${element.fileName}:${element.lineNumber})"
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
ReplayOrchestrator.shared?.
|
|
121
|
+
ReplayOrchestrator.shared?.incrementStalledTally()
|
|
117
122
|
|
|
118
123
|
// Route ANR through TelemetryPipeline so it arrives in the events
|
|
119
124
|
// batch and the backend ingest worker can insert it into the anrs table
|
|
120
125
|
val stackStr = frames.joinToString("\n")
|
|
121
126
|
TelemetryPipeline.shared?.recordAnrEvent(durationMs, stackStr)
|
|
127
|
+
|
|
128
|
+
// Persist ANR incident and send through /api/ingest/fault so ANRs survive
|
|
129
|
+
// process termination/background upload loss, similar to crash recovery.
|
|
130
|
+
val sessionId = StabilityMonitor.shared?.currentSessionId
|
|
131
|
+
?: ReplayOrchestrator.shared?.replayId
|
|
132
|
+
?: "unknown"
|
|
133
|
+
val incident = IncidentRecord(
|
|
134
|
+
sessionId = sessionId,
|
|
135
|
+
timestampMs = System.currentTimeMillis(),
|
|
136
|
+
category = "anr",
|
|
137
|
+
identifier = "MainThreadFrozen",
|
|
138
|
+
detail = "Main thread unresponsive for ${durationMs}ms",
|
|
139
|
+
frames = frames,
|
|
140
|
+
context = mapOf(
|
|
141
|
+
"durationMs" to durationMs.toString(),
|
|
142
|
+
"threadState" to "blocked"
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
StabilityMonitor.shared?.persistIncidentSync(incident)
|
|
146
|
+
StabilityMonitor.shared?.transmitStoredReport()
|
|
122
147
|
|
|
123
148
|
DiagnosticLog.fault("ANR detected: ${durationMs}ms hang")
|
|
124
149
|
|
|
@@ -133,8 +133,10 @@ class InteractionRecorder private constructor(private val context: Context) {
|
|
|
133
133
|
// Notify SpecialCases about touch phases for touch-based
|
|
134
134
|
// map idle detection (Mapbox v10+ fallback).
|
|
135
135
|
when (event.actionMasked) {
|
|
136
|
-
MotionEvent.ACTION_DOWN ->
|
|
136
|
+
MotionEvent.ACTION_DOWN -> {
|
|
137
|
+
VisualCapture.shared?.invalidateMaskCache()
|
|
137
138
|
SpecialCases.shared.notifyTouchBegan()
|
|
139
|
+
}
|
|
138
140
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL ->
|
|
139
141
|
SpecialCases.shared.notifyTouchEnded()
|
|
140
142
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
package com.rejourney.recording
|
|
2
|
+
|
|
3
|
+
import okhttp3.Interceptor
|
|
4
|
+
import okhttp3.Response
|
|
5
|
+
import java.io.IOException
|
|
6
|
+
import java.util.UUID
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Native OkHttp Interceptor for Rejourney
|
|
10
|
+
*
|
|
11
|
+
* Captures native network traffic and routes it to the Rejourney TelemetryPipeline.
|
|
12
|
+
* To use, add this interceptor to your native OkHttpClient:
|
|
13
|
+
* val client = OkHttpClient.Builder()
|
|
14
|
+
* .addInterceptor(RejourneyNetworkInterceptor())
|
|
15
|
+
* .build()
|
|
16
|
+
*/
|
|
17
|
+
class RejourneyNetworkInterceptor : Interceptor {
|
|
18
|
+
|
|
19
|
+
@Throws(IOException::class)
|
|
20
|
+
override fun intercept(chain: Interceptor.Chain): Response {
|
|
21
|
+
val request = chain.request()
|
|
22
|
+
val startMs = System.currentTimeMillis()
|
|
23
|
+
|
|
24
|
+
var response: Response? = null
|
|
25
|
+
var error: Exception? = null
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
response = chain.proceed(request)
|
|
29
|
+
} catch (e: Exception) {
|
|
30
|
+
error = e
|
|
31
|
+
throw e
|
|
32
|
+
} finally {
|
|
33
|
+
try {
|
|
34
|
+
val endMs = System.currentTimeMillis()
|
|
35
|
+
val duration = endMs - startMs
|
|
36
|
+
|
|
37
|
+
val isSuccess = response?.isSuccessful == true
|
|
38
|
+
val statusCode = response?.code ?: 0
|
|
39
|
+
|
|
40
|
+
val urlStr = request.url.toString()
|
|
41
|
+
val pathStr = request.url.encodedPath
|
|
42
|
+
|
|
43
|
+
var maxUrlStr = urlStr
|
|
44
|
+
if (maxUrlStr.length > 300) {
|
|
45
|
+
maxUrlStr = maxUrlStr.substring(0, 300)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
val reqSize = request.body?.contentLength()?.takeIf { it >= 0 }?.toInt() ?: 0
|
|
49
|
+
val resSize = response?.body?.contentLength()?.takeIf { it >= 0 }?.toInt() ?: 0
|
|
50
|
+
|
|
51
|
+
val event = mutableMapOf<String, Any>(
|
|
52
|
+
"requestId" to "n_${UUID.randomUUID()}",
|
|
53
|
+
"method" to request.method,
|
|
54
|
+
"url" to maxUrlStr,
|
|
55
|
+
"urlPath" to pathStr,
|
|
56
|
+
"urlHost" to request.url.host,
|
|
57
|
+
"statusCode" to statusCode,
|
|
58
|
+
"duration" to duration,
|
|
59
|
+
"startTimestamp" to startMs,
|
|
60
|
+
"endTimestamp" to endMs,
|
|
61
|
+
"success" to isSuccess
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if (reqSize > 0) {
|
|
65
|
+
event["requestBodySize"] = reqSize
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
request.body?.contentType()?.let {
|
|
69
|
+
event["requestContentType"] = it.toString()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (resSize > 0) {
|
|
73
|
+
event["responseBodySize"] = resSize
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
response?.body?.contentType()?.let {
|
|
77
|
+
event["responseContentType"] = it.toString()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
error?.let {
|
|
81
|
+
event["errorMessage"] = it.message ?: "Network error"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
TelemetryPipeline.shared?.recordNetworkEvent(event)
|
|
85
|
+
|
|
86
|
+
} catch (ignore: Exception) {
|
|
87
|
+
// Ignore to avoid breaking the application network call
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return response!!
|
|
92
|
+
}
|
|
93
|
+
}
|