expo-observe 57.0.20 → 57.0.22
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/CHANGELOG.md +10 -0
- package/android/build.gradle +0 -25
- package/android/src/main/java/expo/modules/observe/Constants.kt +0 -1
- package/android/src/main/java/expo/modules/observe/CursorRepair.kt +58 -0
- package/android/src/main/java/expo/modules/observe/DispatchUtils.kt +0 -22
- package/android/src/main/java/expo/modules/observe/Event.kt +1 -1
- package/android/src/main/java/expo/modules/observe/ObservabilityBackgroundWorker.kt +1 -12
- package/android/src/main/java/expo/modules/observe/ObservabilityManager.kt +105 -142
- package/android/src/main/java/expo/modules/observe/ObservePreferences.kt +22 -0
- package/expo-module.config.json +1 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22-sources.jar +0 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22-sources.jar.md5 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22-sources.jar.sha1 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22-sources.jar.sha256 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22-sources.jar.sha512 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.aar +0 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.aar.md5 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.aar.sha1 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.aar.sha256 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.aar.sha512 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/{57.0.20/expo.modules.observe-57.0.20.module → 57.0.22/expo.modules.observe-57.0.22.module} +25 -39
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.module.md5 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.module.sha1 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.module.sha256 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.module.sha512 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/{57.0.20/expo.modules.observe-57.0.20.pom → 57.0.22/expo.modules.observe-57.0.22.pom} +4 -16
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.pom.md5 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.pom.sha1 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.pom.sha256 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.22/expo.modules.observe-57.0.22.pom.sha512 +1 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml +4 -4
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha512 +1 -1
- package/package.json +5 -5
- package/android/src/main/java/expo/modules/observe/storage/ObserveDatabase.kt +0 -94
- package/android/src/main/java/expo/modules/observe/storage/PendingLogsManager.kt +0 -42
- package/android/src/main/java/expo/modules/observe/storage/PendingMetricsManager.kt +0 -42
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20-sources.jar +0 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20-sources.jar.md5 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20-sources.jar.sha1 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20-sources.jar.sha256 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20-sources.jar.sha512 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.aar +0 -0
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.aar.md5 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.aar.sha1 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.aar.sha256 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.aar.sha512 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.module.md5 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.module.sha1 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.module.sha256 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.module.sha512 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.pom.md5 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.pom.sha1 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.pom.sha256 +0 -1
- package/local-maven-repo/expo/modules/observe/expo.modules.observe/57.0.20/expo.modules.observe-57.0.20.pom.sha512 +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 57.0.22 — 2026-09-15
|
|
14
|
+
|
|
15
|
+
_This version does not introduce any user-facing changes._
|
|
16
|
+
|
|
17
|
+
## 57.0.21 — 2026-09-11
|
|
18
|
+
|
|
19
|
+
### 💡 Others
|
|
20
|
+
|
|
21
|
+
- [Android] Replace pending telemetry queues with persisted row-id cursors. ([#49547](https://github.com/expo/expo/pull/49547) by [@Ubax](https://github.com/Ubax))
|
|
22
|
+
|
|
13
23
|
## 57.0.20 — 2026-09-08
|
|
14
24
|
|
|
15
25
|
_This version does not introduce any user-facing changes._
|
package/android/build.gradle
CHANGED
|
@@ -1,22 +1,4 @@
|
|
|
1
1
|
buildscript {
|
|
2
|
-
ext {
|
|
3
|
-
getKspVersion = {
|
|
4
|
-
if (rootProject.hasProperty("kspVersion")) {
|
|
5
|
-
return rootProject["kspVersion"]
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
def kotlinVersion = rootProject.hasProperty("kotlinVersion") ? rootProject["kotlinVersion"] : "2.0.21"
|
|
9
|
-
if (kotlinVersion == "2.1.20") {
|
|
10
|
-
return "2.1.20-2.0.1"
|
|
11
|
-
} else if (kotlinVersion == "2.0.21") {
|
|
12
|
-
return "2.0.21-1.0.28"
|
|
13
|
-
} else if (kotlinVersion == "1.9.25") {
|
|
14
|
-
return "1.9.25-1.0.20"
|
|
15
|
-
}
|
|
16
|
-
return "1.9.24-1.0.20"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
2
|
repositories
|
|
21
3
|
{
|
|
22
4
|
google()
|
|
@@ -26,13 +8,11 @@ buildscript {
|
|
|
26
8
|
|
|
27
9
|
dependencies {
|
|
28
10
|
classpath("org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin:${kotlinVersion}")
|
|
29
|
-
classpath "com.google.devtools.ksp:symbol-processing-gradle-plugin:${getKspVersion()}"
|
|
30
11
|
}
|
|
31
12
|
}
|
|
32
13
|
apply plugin: 'com.android.library'
|
|
33
14
|
apply plugin: 'expo-module-gradle-plugin'
|
|
34
15
|
apply plugin: 'org.jetbrains.kotlin.plugin.serialization'
|
|
35
|
-
apply plugin: 'com.google.devtools.ksp'
|
|
36
16
|
|
|
37
17
|
group = 'expo.modules.observe'
|
|
38
18
|
|
|
@@ -65,11 +45,6 @@ dependencies {
|
|
|
65
45
|
// This is a workaround for IDE warning - https://youtrack.jetbrains.com/issue/KTIJ-31549
|
|
66
46
|
implementation "androidx.annotation:annotation-experimental:1.5.1"
|
|
67
47
|
|
|
68
|
-
def room_version = "2.8.3"
|
|
69
|
-
implementation "androidx.room:room-runtime:$room_version"
|
|
70
|
-
implementation "androidx.room:room-ktx:$room_version"
|
|
71
|
-
ksp "androidx.room:room-compiler:$room_version"
|
|
72
|
-
|
|
73
48
|
implementation 'androidx.work:work-runtime-ktx:2.9.1'
|
|
74
49
|
|
|
75
50
|
testImplementation 'junit:junit:4.13.2'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright 2025-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
package expo.modules.observe
|
|
4
|
+
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.util.Log
|
|
7
|
+
import expo.modules.appmetrics.storage.SessionManager
|
|
8
|
+
import kotlinx.coroutines.CancellationException
|
|
9
|
+
|
|
10
|
+
internal suspend fun repairCursorIfStale(
|
|
11
|
+
signalName: String,
|
|
12
|
+
readCursor: () -> Long,
|
|
13
|
+
writeCursor: (Long) -> Unit,
|
|
14
|
+
readMaxId: suspend () -> Long?
|
|
15
|
+
) {
|
|
16
|
+
val cursor = readCursor()
|
|
17
|
+
if (cursor < 0) return
|
|
18
|
+
|
|
19
|
+
val maxId = try {
|
|
20
|
+
readMaxId()
|
|
21
|
+
} catch (error: CancellationException) {
|
|
22
|
+
throw error
|
|
23
|
+
} catch (error: Exception) {
|
|
24
|
+
Log.w(OBSERVE_TAG, "Failed to read max $signalName id while repairing cursor: ${error.message}")
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
if (cursor > (maxId ?: -1)) {
|
|
28
|
+
Log.i(
|
|
29
|
+
OBSERVE_TAG,
|
|
30
|
+
"Resetting stale $signalName dispatch cursor (was $cursor, max id is ${maxId ?: "<empty>"})"
|
|
31
|
+
)
|
|
32
|
+
writeCursor(-1)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
internal suspend fun repairMetricCursorIfStale(
|
|
37
|
+
context: Context,
|
|
38
|
+
sessionManager: SessionManager
|
|
39
|
+
) {
|
|
40
|
+
repairCursorIfStale(
|
|
41
|
+
signalName = "metric",
|
|
42
|
+
readCursor = { ObservePreferences.getLastDispatchedMetricId(context) },
|
|
43
|
+
writeCursor = { ObservePreferences.setLastDispatchedMetricId(context, it) },
|
|
44
|
+
readMaxId = sessionManager::getMaxMetricId
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
internal suspend fun repairLogCursorIfStale(
|
|
49
|
+
context: Context,
|
|
50
|
+
sessionManager: SessionManager
|
|
51
|
+
) {
|
|
52
|
+
repairCursorIfStale(
|
|
53
|
+
signalName = "log",
|
|
54
|
+
readCursor = { ObservePreferences.getLastDispatchedLogId(context) },
|
|
55
|
+
writeCursor = { ObservePreferences.setLastDispatchedLogId(context, it) },
|
|
56
|
+
readMaxId = sessionManager::getMaxLogId
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -88,28 +88,6 @@ object DispatchUtils {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/**
|
|
92
|
-
* Whether the dispatch caller should remove the just-sent pending IDs from the queue.
|
|
93
|
-
*
|
|
94
|
-
* - `Success` and `PartialSuccess` remove them — the rows have been accepted by the server
|
|
95
|
-
* (partial success rejects a subset server-side, but the bytes still landed; re-sending
|
|
96
|
-
* them would just re-trip the same rejection).
|
|
97
|
-
* - `NonRetryableFailure` ALSO removes them — the server has refused these rows
|
|
98
|
-
* permanently, so retrying would produce the same answer; removing them drops the batch
|
|
99
|
-
* so it can't wedge subsequent rounds. This is the acceptance-criterion behavior: a
|
|
100
|
-
* 400/403 must not be re-sent on the next cycle.
|
|
101
|
-
* - `PayloadTooLarge` removes the pending ID because multi-record batches are retried with
|
|
102
|
-
* smaller chunks before this check, so reaching it means a single record exceeded the limit.
|
|
103
|
-
* - `RetryableFailure` keeps them so they can be retried.
|
|
104
|
-
*/
|
|
105
|
-
fun shouldRemovePending(result: DispatchResult): Boolean = when (result) {
|
|
106
|
-
is DispatchResult.Success,
|
|
107
|
-
is DispatchResult.PartialSuccess,
|
|
108
|
-
is DispatchResult.NonRetryableFailure,
|
|
109
|
-
is DispatchResult.PayloadTooLarge -> true
|
|
110
|
-
is DispatchResult.RetryableFailure -> false
|
|
111
|
-
}
|
|
112
|
-
|
|
113
91
|
/**
|
|
114
92
|
* Parses an HTTP `Retry-After` header into a delay in milliseconds from now. Accepts both
|
|
115
93
|
* formats permitted by RFC 7231: an integer delta-seconds, or an HTTP-date.
|
|
@@ -109,7 +109,7 @@ data class EASMetric(
|
|
|
109
109
|
* Wire shape of a log event ready for dispatch. Distinct from the storage-side
|
|
110
110
|
* `LogRecord`: this form has the JSON `attributes` blob already parsed back
|
|
111
111
|
* into a structured object (so the OTel encoder can map values to typed
|
|
112
|
-
* `OTAnyValue`s) and drops storage-only columns like
|
|
112
|
+
* `OTAnyValue`s) and drops storage-only columns like the row id.
|
|
113
113
|
*/
|
|
114
114
|
@Serializable
|
|
115
115
|
data class LogEvent(
|
|
@@ -15,16 +15,10 @@ import androidx.work.OneTimeWorkRequestBuilder
|
|
|
15
15
|
import androidx.work.WorkManager
|
|
16
16
|
import androidx.work.WorkerParameters
|
|
17
17
|
import androidx.work.workDataOf
|
|
18
|
-
import expo.modules.observe.storage.PendingLogsManager
|
|
19
|
-
import expo.modules.observe.storage.PendingMetricsManager
|
|
20
18
|
import expo.modules.appmetrics.storage.SessionManager
|
|
21
19
|
|
|
22
20
|
/**
|
|
23
|
-
* Background worker that dispatches
|
|
24
|
-
*
|
|
25
|
-
* This worker intentionally does NOT register a [SessionManager.MetricsInsertListener].
|
|
26
|
-
* It only dispatches metrics that were already queued in the pending table by the foreground
|
|
27
|
-
* [ObservabilityManager].
|
|
21
|
+
* Background worker that dispatches stored metrics and logs to EAS Observe.
|
|
28
22
|
*/
|
|
29
23
|
class ObservabilityBackgroundWorker(
|
|
30
24
|
context: Context,
|
|
@@ -42,15 +36,10 @@ class ObservabilityBackgroundWorker(
|
|
|
42
36
|
context = context
|
|
43
37
|
)
|
|
44
38
|
|
|
45
|
-
val pendingMetricsManager = PendingMetricsManager(context)
|
|
46
|
-
val pendingLogsManager = PendingLogsManager(context)
|
|
47
|
-
|
|
48
39
|
BaseObservabilityManager(
|
|
49
40
|
context = context,
|
|
50
41
|
projectId = projectId,
|
|
51
42
|
sessionManager = sessionManager,
|
|
52
|
-
pendingMetricsManager = pendingMetricsManager,
|
|
53
|
-
pendingLogsManager = pendingLogsManager,
|
|
54
43
|
baseUrl = baseUrl,
|
|
55
44
|
isDebugBuild = BuildConfig.DEBUG
|
|
56
45
|
)
|
|
@@ -3,8 +3,6 @@ package expo.modules.observe
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.util.Log
|
|
5
5
|
import expo.modules.easclient.EASClientID
|
|
6
|
-
import expo.modules.observe.storage.PendingLogsManager
|
|
7
|
-
import expo.modules.observe.storage.PendingMetricsManager
|
|
8
6
|
import expo.modules.appmetrics.storage.SessionManager
|
|
9
7
|
import expo.modules.appmetrics.utils.TimeUtils
|
|
10
8
|
import expo.modules.interfaces.constants.ConstantsInterface
|
|
@@ -34,25 +32,13 @@ class ObservabilityManager(
|
|
|
34
32
|
}
|
|
35
33
|
val baseUrl = manifest.baseUrl ?: OBSERVE_DEFAULT_BASE_URL
|
|
36
34
|
|
|
37
|
-
val pendingMetricsManager = PendingMetricsManager(context)
|
|
38
|
-
val pendingLogsManager = PendingLogsManager(context)
|
|
39
|
-
|
|
40
35
|
baseManager = BaseObservabilityManager(
|
|
41
36
|
context = context,
|
|
42
37
|
sessionManager = sessionManager,
|
|
43
|
-
pendingMetricsManager = pendingMetricsManager,
|
|
44
|
-
pendingLogsManager = pendingLogsManager,
|
|
45
38
|
projectId = projectId,
|
|
46
39
|
baseUrl = baseUrl,
|
|
47
40
|
isDebugBuild = BuildConfig.DEBUG
|
|
48
41
|
)
|
|
49
|
-
|
|
50
|
-
sessionManager.addMetricsInsertListener { metricIds ->
|
|
51
|
-
pendingMetricsManager.addPendingMetrics(metricIds)
|
|
52
|
-
}
|
|
53
|
-
sessionManager.addLogsInsertListener { logIds ->
|
|
54
|
-
pendingLogsManager.addPendingLogs(logIds)
|
|
55
|
-
}
|
|
56
42
|
}
|
|
57
43
|
|
|
58
44
|
suspend fun dispatchUnsentMetrics() {
|
|
@@ -75,8 +61,6 @@ class ObservabilityManager(
|
|
|
75
61
|
class BaseObservabilityManager(
|
|
76
62
|
private val context: Context,
|
|
77
63
|
private val sessionManager: SessionManager,
|
|
78
|
-
private val pendingMetricsManager: PendingMetricsManager,
|
|
79
|
-
private val pendingLogsManager: PendingLogsManager,
|
|
80
64
|
val projectId: String,
|
|
81
65
|
val baseUrl: String,
|
|
82
66
|
private val isDebugBuild: Boolean = false,
|
|
@@ -108,20 +92,6 @@ class BaseObservabilityManager(
|
|
|
108
92
|
private var metricsRetryGate: DispatchUtils.RetryGateState = DispatchUtils.RetryGateState.initial
|
|
109
93
|
private var logsRetryGate: DispatchUtils.RetryGateState = DispatchUtils.RetryGateState.initial
|
|
110
94
|
|
|
111
|
-
/**
|
|
112
|
-
* Per-signal mutexes that serialize same-signal dispatch calls. Two `dispatchEvents`
|
|
113
|
-
* invocations from JS can otherwise land on the same `BaseObservabilityManager` instance
|
|
114
|
-
* concurrently and race on the gate's read-modify-write (and double-POST the same pending
|
|
115
|
-
* rows). The metrics and logs paths take separate mutexes so they can still run in
|
|
116
|
-
* parallel — only same-signal calls serialize.
|
|
117
|
-
*
|
|
118
|
-
* Worst case without these would be benign (a dropped gate update or a duplicate dispatch),
|
|
119
|
-
* since the pending-ID stores are already DB-backed and telemetry is best-effort — but the
|
|
120
|
-
* gate is in-memory state with no other synchronization, so close the race explicitly.
|
|
121
|
-
*/
|
|
122
|
-
private val metricsDispatchMutex = Mutex()
|
|
123
|
-
private val logsDispatchMutex = Mutex()
|
|
124
|
-
|
|
125
95
|
/**
|
|
126
96
|
* Returns true and logs when an active retry gate suppresses this dispatch round. Called
|
|
127
97
|
* inside each per-signal dispatch method rather than at a shared entry point, so a backoff
|
|
@@ -152,169 +122,156 @@ class BaseObservabilityManager(
|
|
|
152
122
|
)
|
|
153
123
|
|
|
154
124
|
suspend fun dispatchUnsentMetrics(): Unit = metricsDispatchMutex.withLock {
|
|
155
|
-
if (!pendingMetricsManager.hasPendingMetrics()) {
|
|
156
|
-
return
|
|
157
|
-
}
|
|
158
|
-
|
|
159
125
|
if (retryGateBlocks(metricsRetryGate, "metrics")) {
|
|
160
126
|
return
|
|
161
127
|
}
|
|
162
128
|
|
|
129
|
+
repairMetricCursorIfStale(context, sessionManager)
|
|
163
130
|
if (!shouldDispatch()) {
|
|
164
|
-
|
|
131
|
+
val maxId = sessionManager.getMaxMetricId() ?: -1
|
|
132
|
+
if (ObservePreferences.getLastDispatchedMetricId(context) != maxId) {
|
|
133
|
+
ObservePreferences.setLastDispatchedMetricId(context, maxId)
|
|
134
|
+
}
|
|
165
135
|
return
|
|
166
136
|
}
|
|
167
137
|
|
|
138
|
+
var cursor = ObservePreferences.getLastDispatchedMetricId(context)
|
|
168
139
|
var chunkSize = dispatchChunkSize
|
|
169
140
|
while (currentCoroutineContext().isActive) {
|
|
170
|
-
val
|
|
171
|
-
// Use the default for the next batch unless a 413 below overrides it. Re-discovering
|
|
172
|
-
// the limit each batch is fine: the server accepts payloads over 1 MB, so the default
|
|
173
|
-
// chunk stays far below the limit and a 413 is exceptional.
|
|
141
|
+
val metrics = sessionManager.getMetrics(cursor, chunkSize)
|
|
174
142
|
chunkSize = dispatchChunkSize
|
|
175
|
-
if (
|
|
143
|
+
if (metrics.isEmpty()) {
|
|
176
144
|
break
|
|
177
145
|
}
|
|
178
146
|
|
|
179
|
-
val
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
val
|
|
183
|
-
|
|
184
|
-
if (orphanedIds.isNotEmpty()) {
|
|
185
|
-
pendingMetricsManager.removePendingMetrics(orphanedIds)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (sessionsWithPendingMetrics.isNotEmpty()) {
|
|
189
|
-
val events = sessionsWithPendingMetrics.map { sessionWithMetrics ->
|
|
147
|
+
val highestId = metrics.last().id
|
|
148
|
+
val metricsBySessionId = metrics.groupBy { it.sessionId }
|
|
149
|
+
val sessions = sessionManager.getSessions(metricsBySessionId.keys).associateBy { it.id }
|
|
150
|
+
val events = metricsBySessionId.mapNotNull { (sessionId, sessionMetrics) ->
|
|
151
|
+
sessions[sessionId]?.let { session ->
|
|
190
152
|
Event(
|
|
191
|
-
metadata = Metadata.fromSessionMetadata(
|
|
192
|
-
metrics =
|
|
153
|
+
metadata = Metadata.fromSessionMetadata(session),
|
|
154
|
+
metrics = sessionMetrics.map(EASMetric::fromMetric)
|
|
193
155
|
)
|
|
194
156
|
}
|
|
157
|
+
}
|
|
158
|
+
if (events.isEmpty()) {
|
|
159
|
+
cursor = highestId
|
|
160
|
+
ObservePreferences.setLastDispatchedMetricId(context, cursor)
|
|
161
|
+
continue
|
|
162
|
+
}
|
|
195
163
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
"Dropping batch of ${dispatchedMetricIds.size} metric event(s): ${result.reason}"
|
|
211
|
-
)
|
|
212
|
-
is DispatchResult.PayloadTooLarge ->
|
|
213
|
-
if (dispatchedMetricIds.size == 1) {
|
|
214
|
-
Log.w(OBSERVE_TAG, "Dropping metric event that exceeds the server's payload limit")
|
|
215
|
-
}
|
|
216
|
-
is DispatchResult.Success, is DispatchResult.RetryableFailure -> Unit
|
|
217
|
-
}
|
|
218
|
-
if (result is DispatchResult.PayloadTooLarge && dispatchedMetricIds.size > 1) {
|
|
219
|
-
chunkSize = dispatchedMetricIds.size / 2
|
|
220
|
-
// Keep the pending metrics, but retry immediately with a smaller chunk.
|
|
221
|
-
continue
|
|
164
|
+
val result = eventDispatcher.dispatch(events)
|
|
165
|
+
metricsRetryGate = nextGate(metricsRetryGate, result)
|
|
166
|
+
when (result) {
|
|
167
|
+
is DispatchResult.PartialSuccess ->
|
|
168
|
+
Log.w(
|
|
169
|
+
OBSERVE_TAG,
|
|
170
|
+
"Partial success on batch of ${metrics.size} metric event(s): " +
|
|
171
|
+
"server rejected ${result.partial.rejectedCount} " +
|
|
172
|
+
"(${result.partial.errorMessage ?: "no error message"})"
|
|
173
|
+
)
|
|
174
|
+
is DispatchResult.NonRetryableFailure ->
|
|
175
|
+
Log.w(OBSERVE_TAG, "Dropping batch of ${metrics.size} metric event(s): ${result.reason}")
|
|
176
|
+
DispatchResult.PayloadTooLarge -> if (metrics.size == 1) {
|
|
177
|
+
Log.w(OBSERVE_TAG, "Dropping metric event that exceeds the server's payload limit")
|
|
222
178
|
}
|
|
223
|
-
|
|
224
|
-
|
|
179
|
+
DispatchResult.Success, is DispatchResult.RetryableFailure -> Unit
|
|
180
|
+
}
|
|
181
|
+
if (result is DispatchResult.PayloadTooLarge && metrics.size > 1) {
|
|
182
|
+
chunkSize = metrics.size / 2
|
|
183
|
+
continue
|
|
184
|
+
}
|
|
185
|
+
when (result) {
|
|
186
|
+
DispatchResult.Success, is DispatchResult.PartialSuccess -> {
|
|
187
|
+
cursor = highestId
|
|
188
|
+
ObservePreferences.setLastDispatchedMetricId(context, cursor)
|
|
225
189
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (result is DispatchResult.NonRetryableFailure || result is DispatchResult.PayloadTooLarge) {
|
|
190
|
+
is DispatchResult.NonRetryableFailure, DispatchResult.PayloadTooLarge -> {
|
|
191
|
+
ObservePreferences.setLastDispatchedMetricId(context, highestId)
|
|
229
192
|
break
|
|
230
193
|
}
|
|
194
|
+
is DispatchResult.RetryableFailure -> break
|
|
231
195
|
}
|
|
232
196
|
}
|
|
233
197
|
}
|
|
234
198
|
|
|
235
199
|
/**
|
|
236
200
|
* Dispatches log events to `/v1/logs`. Independent from the metrics path —
|
|
237
|
-
* a logs failure doesn't affect the metrics
|
|
201
|
+
* a logs failure doesn't affect the metrics cursor and vice versa.
|
|
238
202
|
*/
|
|
239
203
|
suspend fun dispatchUnsentLogs(): Unit = logsDispatchMutex.withLock {
|
|
240
|
-
if (!pendingLogsManager.hasPendingLogs()) {
|
|
241
|
-
return
|
|
242
|
-
}
|
|
243
|
-
|
|
244
204
|
if (retryGateBlocks(logsRetryGate, "logs")) {
|
|
245
205
|
return
|
|
246
206
|
}
|
|
247
207
|
|
|
208
|
+
repairLogCursorIfStale(context, sessionManager)
|
|
248
209
|
if (!shouldDispatch()) {
|
|
249
|
-
|
|
210
|
+
val maxId = sessionManager.getMaxLogId() ?: -1
|
|
211
|
+
if (ObservePreferences.getLastDispatchedLogId(context) != maxId) {
|
|
212
|
+
ObservePreferences.setLastDispatchedLogId(context, maxId)
|
|
213
|
+
}
|
|
250
214
|
return
|
|
251
215
|
}
|
|
252
216
|
|
|
217
|
+
var cursor = ObservePreferences.getLastDispatchedLogId(context)
|
|
253
218
|
var chunkSize = dispatchChunkSize
|
|
254
219
|
while (currentCoroutineContext().isActive) {
|
|
255
|
-
val
|
|
256
|
-
// Use the default for the next batch unless a 413 below overrides it. Re-discovering
|
|
257
|
-
// the limit each batch is fine: the server accepts payloads over 1 MB, so the default
|
|
258
|
-
// chunk stays far below the limit and a 413 is exceptional.
|
|
220
|
+
val logs = sessionManager.getLogs(cursor, chunkSize)
|
|
259
221
|
chunkSize = dispatchChunkSize
|
|
260
|
-
if (
|
|
222
|
+
if (logs.isEmpty()) {
|
|
261
223
|
break
|
|
262
224
|
}
|
|
263
225
|
|
|
264
|
-
val
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
val orphanedIds = pendingIds.filter { it !in resolvedLogIds }
|
|
270
|
-
if (orphanedIds.isNotEmpty()) {
|
|
271
|
-
pendingLogsManager.removePendingLogs(orphanedIds)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (sessionsWithPendingLogs.isNotEmpty()) {
|
|
275
|
-
val events = sessionsWithPendingLogs.map { sessionWithLogs ->
|
|
226
|
+
val highestId = logs.last().id
|
|
227
|
+
val logsBySessionId = logs.groupBy { it.sessionId }
|
|
228
|
+
val sessions = sessionManager.getSessions(logsBySessionId.keys).associateBy { it.id }
|
|
229
|
+
val events = logsBySessionId.mapNotNull { (sessionId, sessionLogs) ->
|
|
230
|
+
sessions[sessionId]?.let { session ->
|
|
276
231
|
Event(
|
|
277
|
-
metadata = Metadata.fromSessionMetadata(
|
|
232
|
+
metadata = Metadata.fromSessionMetadata(session),
|
|
278
233
|
metrics = emptyList(),
|
|
279
|
-
logs =
|
|
234
|
+
logs = sessionLogs.map(LogEvent::fromLogRecord)
|
|
280
235
|
)
|
|
281
236
|
}
|
|
237
|
+
}
|
|
238
|
+
if (events.isEmpty()) {
|
|
239
|
+
cursor = highestId
|
|
240
|
+
ObservePreferences.setLastDispatchedLogId(context, cursor)
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
282
243
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
"
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
"Dropping batch of ${dispatchedLogIds.size} log event(s): ${result.reason}"
|
|
298
|
-
)
|
|
299
|
-
is DispatchResult.PayloadTooLarge ->
|
|
300
|
-
if (dispatchedLogIds.size == 1) {
|
|
301
|
-
Log.w(OBSERVE_TAG, "Dropping log event that exceeds the server's payload limit")
|
|
302
|
-
}
|
|
303
|
-
is DispatchResult.Success, is DispatchResult.RetryableFailure -> Unit
|
|
304
|
-
}
|
|
305
|
-
if (result is DispatchResult.PayloadTooLarge && dispatchedLogIds.size > 1) {
|
|
306
|
-
chunkSize = dispatchedLogIds.size / 2
|
|
307
|
-
// Keep the pending logs, but retry immediately with a smaller chunk.
|
|
308
|
-
continue
|
|
244
|
+
val result = eventDispatcher.dispatchLogs(events)
|
|
245
|
+
logsRetryGate = nextGate(logsRetryGate, result)
|
|
246
|
+
when (result) {
|
|
247
|
+
is DispatchResult.PartialSuccess ->
|
|
248
|
+
Log.w(
|
|
249
|
+
OBSERVE_TAG,
|
|
250
|
+
"Partial success on batch of ${logs.size} log event(s): " +
|
|
251
|
+
"server rejected ${result.partial.rejectedCount} " +
|
|
252
|
+
"(${result.partial.errorMessage ?: "no error message"})"
|
|
253
|
+
)
|
|
254
|
+
is DispatchResult.NonRetryableFailure ->
|
|
255
|
+
Log.w(OBSERVE_TAG, "Dropping batch of ${logs.size} log event(s): ${result.reason}")
|
|
256
|
+
DispatchResult.PayloadTooLarge -> if (logs.size == 1) {
|
|
257
|
+
Log.w(OBSERVE_TAG, "Dropping log event that exceeds the server's payload limit")
|
|
309
258
|
}
|
|
310
|
-
|
|
311
|
-
|
|
259
|
+
DispatchResult.Success, is DispatchResult.RetryableFailure -> Unit
|
|
260
|
+
}
|
|
261
|
+
if (result is DispatchResult.PayloadTooLarge && logs.size > 1) {
|
|
262
|
+
chunkSize = logs.size / 2
|
|
263
|
+
continue
|
|
264
|
+
}
|
|
265
|
+
when (result) {
|
|
266
|
+
DispatchResult.Success, is DispatchResult.PartialSuccess -> {
|
|
267
|
+
cursor = highestId
|
|
268
|
+
ObservePreferences.setLastDispatchedLogId(context, cursor)
|
|
312
269
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (result is DispatchResult.NonRetryableFailure || result is DispatchResult.PayloadTooLarge) {
|
|
270
|
+
is DispatchResult.NonRetryableFailure, DispatchResult.PayloadTooLarge -> {
|
|
271
|
+
ObservePreferences.setLastDispatchedLogId(context, highestId)
|
|
316
272
|
break
|
|
317
273
|
}
|
|
274
|
+
is DispatchResult.RetryableFailure -> break
|
|
318
275
|
}
|
|
319
276
|
}
|
|
320
277
|
}
|
|
@@ -338,10 +295,16 @@ class BaseObservabilityManager(
|
|
|
338
295
|
}
|
|
339
296
|
|
|
340
297
|
suspend fun cleanup() {
|
|
341
|
-
pendingMetricsManager.cleanupOldPendingMetrics()
|
|
342
|
-
pendingLogsManager.cleanupOldPendingLogs()
|
|
343
298
|
// TODO(@ubax): Move sessionManager.cleanupOldSessions out of eas observe
|
|
344
299
|
sessionManager.cleanupOldSessions()
|
|
300
|
+
// Remove the database used by the old pending telemetry queues.
|
|
301
|
+
context.deleteDatabase("eas_observe")
|
|
345
302
|
sessionManager.cleanupOldLogs()
|
|
346
303
|
}
|
|
304
|
+
|
|
305
|
+
companion object {
|
|
306
|
+
// Serialize foreground and background dispatches without blocking the other signal.
|
|
307
|
+
private val metricsDispatchMutex = Mutex()
|
|
308
|
+
private val logsDispatchMutex = Mutex()
|
|
309
|
+
}
|
|
347
310
|
}
|
|
@@ -8,6 +8,8 @@ import kotlinx.serialization.json.Json
|
|
|
8
8
|
private const val PREFS_NAME = "dev.expo.observe"
|
|
9
9
|
private const val KEY_CONFIG = "config"
|
|
10
10
|
private const val KEY_BUNDLE_DEFAULTS = "bundleDefaults"
|
|
11
|
+
private const val KEY_LAST_DISPATCHED_METRIC_ID = "lastDispatchedMetricId"
|
|
12
|
+
private const val KEY_LAST_DISPATCHED_LOG_ID = "lastDispatchedLogId"
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Snapshot of the last `configure(...)` payload
|
|
@@ -54,4 +56,24 @@ object ObservePreferences {
|
|
|
54
56
|
putString(KEY_BUNDLE_DEFAULTS, Json.encodeToString(defaults))
|
|
55
57
|
}
|
|
56
58
|
}
|
|
59
|
+
|
|
60
|
+
fun getLastDispatchedMetricId(context: Context): Long =
|
|
61
|
+
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
62
|
+
.getLong(KEY_LAST_DISPATCHED_METRIC_ID, -1)
|
|
63
|
+
|
|
64
|
+
fun setLastDispatchedMetricId(context: Context, id: Long) {
|
|
65
|
+
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit(commit = true) {
|
|
66
|
+
putLong(KEY_LAST_DISPATCHED_METRIC_ID, id)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fun getLastDispatchedLogId(context: Context): Long =
|
|
71
|
+
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
72
|
+
.getLong(KEY_LAST_DISPATCHED_LOG_ID, -1)
|
|
73
|
+
|
|
74
|
+
fun setLastDispatchedLogId(context: Context, id: Long) {
|
|
75
|
+
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit(commit = true) {
|
|
76
|
+
putLong(KEY_LAST_DISPATCHED_LOG_ID, id)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
57
79
|
}
|
package/expo-module.config.json
CHANGED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a23b4461bae18b20e1963631bbf5a6ad
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
86160bf79b776ee12b1d3ee5148d9c5fa8be615f
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2804e38922d1ce46c8ce12ee4672f58555a2aa5a8750aa40f87a647b40e44909
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
d0530049f5f76be03f57de762aafba77081039857864f6db1a920ddf9f42ec216d1bee6fca44783b8f5324f695b4fe3faed935befdd08614ef20e954a9906a72
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
d7092ee67bd0503b26e954ad059c8047
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
6cb935926fb92b62d3454565ea5ffe8478e90225
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
204f8f1b8fc9a619e9e556a596a55f1b06eb0761346d06857d6ccf8dd40b3499
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
441de4b1b747af8031431b6b7a5538a9b36f781d3d41a1cbb7d4c8a2161e2ebfe31ac3aaa82a592ad5dae9abe6d34e41511f80a07570ccc38c2b23e8c22ddbbf
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"component": {
|
|
4
4
|
"group": "expo.modules.observe",
|
|
5
5
|
"module": "expo.modules.observe",
|
|
6
|
-
"version": "57.0.
|
|
6
|
+
"version": "57.0.22",
|
|
7
7
|
"attributes": {
|
|
8
8
|
"org.gradle.status": "release"
|
|
9
9
|
}
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
{
|
|
27
|
-
"name": "expo.modules.observe-57.0.
|
|
28
|
-
"url": "expo.modules.observe-57.0.
|
|
29
|
-
"size":
|
|
30
|
-
"sha512": "
|
|
31
|
-
"sha256": "
|
|
32
|
-
"sha1": "
|
|
33
|
-
"md5": "
|
|
27
|
+
"name": "expo.modules.observe-57.0.22.aar",
|
|
28
|
+
"url": "expo.modules.observe-57.0.22.aar",
|
|
29
|
+
"size": 249243,
|
|
30
|
+
"sha512": "441de4b1b747af8031431b6b7a5538a9b36f781d3d41a1cbb7d4c8a2161e2ebfe31ac3aaa82a592ad5dae9abe6d34e41511f80a07570ccc38c2b23e8c22ddbbf",
|
|
31
|
+
"sha256": "204f8f1b8fc9a619e9e556a596a55f1b06eb0761346d06857d6ccf8dd40b3499",
|
|
32
|
+
"sha1": "6cb935926fb92b62d3454565ea5ffe8478e90225",
|
|
33
|
+
"md5": "d7092ee67bd0503b26e954ad059c8047"
|
|
34
34
|
}
|
|
35
35
|
]
|
|
36
36
|
},
|
|
@@ -61,21 +61,21 @@
|
|
|
61
61
|
"group": "expo.modules.appmetrics",
|
|
62
62
|
"module": "expo.modules.appmetrics",
|
|
63
63
|
"version": {
|
|
64
|
-
"requires": "57.0.
|
|
64
|
+
"requires": "57.0.19"
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
"group": "host.exp.exponent",
|
|
69
69
|
"module": "expo-eas-client",
|
|
70
70
|
"version": {
|
|
71
|
-
"requires": "57.0.
|
|
71
|
+
"requires": "57.0.4"
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"group": "host.exp.exponent",
|
|
76
76
|
"module": "expo-updates-interface",
|
|
77
77
|
"version": {
|
|
78
|
-
"requires": "57.0.
|
|
78
|
+
"requires": "57.0.2"
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
81
|
{
|
|
@@ -103,20 +103,6 @@
|
|
|
103
103
|
"requires": "1.5.1"
|
|
104
104
|
}
|
|
105
105
|
},
|
|
106
|
-
{
|
|
107
|
-
"group": "androidx.room",
|
|
108
|
-
"module": "room-runtime",
|
|
109
|
-
"version": {
|
|
110
|
-
"requires": "2.8.3"
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
"group": "androidx.room",
|
|
115
|
-
"module": "room-ktx",
|
|
116
|
-
"version": {
|
|
117
|
-
"requires": "2.8.3"
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
106
|
{
|
|
121
107
|
"group": "androidx.work",
|
|
122
108
|
"module": "work-runtime-ktx",
|
|
@@ -134,13 +120,13 @@
|
|
|
134
120
|
],
|
|
135
121
|
"files": [
|
|
136
122
|
{
|
|
137
|
-
"name": "expo.modules.observe-57.0.
|
|
138
|
-
"url": "expo.modules.observe-57.0.
|
|
139
|
-
"size":
|
|
140
|
-
"sha512": "
|
|
141
|
-
"sha256": "
|
|
142
|
-
"sha1": "
|
|
143
|
-
"md5": "
|
|
123
|
+
"name": "expo.modules.observe-57.0.22.aar",
|
|
124
|
+
"url": "expo.modules.observe-57.0.22.aar",
|
|
125
|
+
"size": 249243,
|
|
126
|
+
"sha512": "441de4b1b747af8031431b6b7a5538a9b36f781d3d41a1cbb7d4c8a2161e2ebfe31ac3aaa82a592ad5dae9abe6d34e41511f80a07570ccc38c2b23e8c22ddbbf",
|
|
127
|
+
"sha256": "204f8f1b8fc9a619e9e556a596a55f1b06eb0761346d06857d6ccf8dd40b3499",
|
|
128
|
+
"sha1": "6cb935926fb92b62d3454565ea5ffe8478e90225",
|
|
129
|
+
"md5": "d7092ee67bd0503b26e954ad059c8047"
|
|
144
130
|
}
|
|
145
131
|
]
|
|
146
132
|
},
|
|
@@ -154,13 +140,13 @@
|
|
|
154
140
|
},
|
|
155
141
|
"files": [
|
|
156
142
|
{
|
|
157
|
-
"name": "expo.modules.observe-57.0.
|
|
158
|
-
"url": "expo.modules.observe-57.0.
|
|
159
|
-
"size":
|
|
160
|
-
"sha512": "
|
|
161
|
-
"sha256": "
|
|
162
|
-
"sha1": "
|
|
163
|
-
"md5": "
|
|
143
|
+
"name": "expo.modules.observe-57.0.22-sources.jar",
|
|
144
|
+
"url": "expo.modules.observe-57.0.22-sources.jar",
|
|
145
|
+
"size": 23585,
|
|
146
|
+
"sha512": "d0530049f5f76be03f57de762aafba77081039857864f6db1a920ddf9f42ec216d1bee6fca44783b8f5324f695b4fe3faed935befdd08614ef20e954a9906a72",
|
|
147
|
+
"sha256": "2804e38922d1ce46c8ce12ee4672f58555a2aa5a8750aa40f87a647b40e44909",
|
|
148
|
+
"sha1": "86160bf79b776ee12b1d3ee5148d9c5fa8be615f",
|
|
149
|
+
"md5": "a23b4461bae18b20e1963631bbf5a6ad"
|
|
164
150
|
}
|
|
165
151
|
]
|
|
166
152
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
df2f92b04bef711bc2d09fcd0dc45e1c
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fe761aa4c81edd0ef4d7dd6a024b02703b55ea63
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
6532619652923a49f9c380203e0da5e7254a6218ea0e1b4277b9eaac96147499
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
e7e8cd414113842f7cc719161a9e155bcf9ee8acb77de34cf92f22ac297bda56ebcb14892e299c8895347e14e5a267f547ed34325b055095237303ad0dc614a6
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<modelVersion>4.0.0</modelVersion>
|
|
10
10
|
<groupId>expo.modules.observe</groupId>
|
|
11
11
|
<artifactId>expo.modules.observe</artifactId>
|
|
12
|
-
<version>57.0.
|
|
12
|
+
<version>57.0.22</version>
|
|
13
13
|
<packaging>aar</packaging>
|
|
14
14
|
<name>expo.modules.observe</name>
|
|
15
15
|
<url>https://github.com/expo/expo</url>
|
|
@@ -52,19 +52,19 @@
|
|
|
52
52
|
<dependency>
|
|
53
53
|
<groupId>expo.modules.appmetrics</groupId>
|
|
54
54
|
<artifactId>expo.modules.appmetrics</artifactId>
|
|
55
|
-
<version>57.0.
|
|
55
|
+
<version>57.0.19</version>
|
|
56
56
|
<scope>runtime</scope>
|
|
57
57
|
</dependency>
|
|
58
58
|
<dependency>
|
|
59
59
|
<groupId>host.exp.exponent</groupId>
|
|
60
60
|
<artifactId>expo-eas-client</artifactId>
|
|
61
|
-
<version>57.0.
|
|
61
|
+
<version>57.0.4</version>
|
|
62
62
|
<scope>runtime</scope>
|
|
63
63
|
</dependency>
|
|
64
64
|
<dependency>
|
|
65
65
|
<groupId>host.exp.exponent</groupId>
|
|
66
66
|
<artifactId>expo-updates-interface</artifactId>
|
|
67
|
-
<version>57.0.
|
|
67
|
+
<version>57.0.2</version>
|
|
68
68
|
<scope>runtime</scope>
|
|
69
69
|
</dependency>
|
|
70
70
|
<dependency>
|
|
@@ -90,18 +90,6 @@
|
|
|
90
90
|
<version>1.5.1</version>
|
|
91
91
|
<scope>runtime</scope>
|
|
92
92
|
</dependency>
|
|
93
|
-
<dependency>
|
|
94
|
-
<groupId>androidx.room</groupId>
|
|
95
|
-
<artifactId>room-runtime</artifactId>
|
|
96
|
-
<version>2.8.3</version>
|
|
97
|
-
<scope>runtime</scope>
|
|
98
|
-
</dependency>
|
|
99
|
-
<dependency>
|
|
100
|
-
<groupId>androidx.room</groupId>
|
|
101
|
-
<artifactId>room-ktx</artifactId>
|
|
102
|
-
<version>2.8.3</version>
|
|
103
|
-
<scope>runtime</scope>
|
|
104
|
-
</dependency>
|
|
105
93
|
<dependency>
|
|
106
94
|
<groupId>androidx.work</groupId>
|
|
107
95
|
<artifactId>work-runtime-ktx</artifactId>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
882b56b56bdd5210ca54f7de0ae9a823
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
691d5eca35b2bba6283e2240e9b6b9b97552e361
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
96dee9036188d706d86f610388558da3129ae48f5e566475cad117c4b67e1574
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
c38ef322262ee8053841ff46bd3c7897fd14f3c77053f2680871fb75fd975b00cf6008fc5e16f6376ee0220e40117a621532220a4ba66799d37abfd25fb45103
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<groupId>expo.modules.observe</groupId>
|
|
4
4
|
<artifactId>expo.modules.observe</artifactId>
|
|
5
5
|
<versioning>
|
|
6
|
-
<latest>57.0.
|
|
7
|
-
<release>57.0.
|
|
6
|
+
<latest>57.0.22</latest>
|
|
7
|
+
<release>57.0.22</release>
|
|
8
8
|
<versions>
|
|
9
|
-
<version>57.0.
|
|
9
|
+
<version>57.0.22</version>
|
|
10
10
|
</versions>
|
|
11
|
-
<lastUpdated>
|
|
11
|
+
<lastUpdated>20260915155138</lastUpdated>
|
|
12
12
|
</versioning>
|
|
13
13
|
</metadata>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
d2cff64db3bc41f15cc4c45e06a45c96
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
44eae1aadbe4523d38a02a3d149b9d55b7afe9fb
|
package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
e9f1962e0cba5319f80bf9bd1b4f57711837e889c99d30b5b095db6d60d95c5b
|
package/local-maven-repo/expo/modules/observe/expo.modules.observe/maven-metadata.xml.sha512
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
dbd9d144c69cb4642627975633e5d5185c567647b70e6cb7f75fa0b9947a922cf1f5f04d6e3aed716a1b4f6f056b75e7de7d3a86257c3ffdd166dede50085d54
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-observe",
|
|
3
3
|
"title": "Expo Observe",
|
|
4
|
-
"version": "57.0.
|
|
4
|
+
"version": "57.0.22",
|
|
5
5
|
"description": "Expo module that dispatches collected app metrics to EAS Observe",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"author": "650 Industries, Inc.",
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"expo-app-metrics": "~57.0.
|
|
39
|
-
"expo-eas-client": "~57.0.
|
|
38
|
+
"expo-app-metrics": "~57.0.19",
|
|
39
|
+
"expo-eas-client": "~57.0.4"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@react-navigation/native": "^7.1.33",
|
|
43
43
|
"@testing-library/react-native": "^13.3.0",
|
|
44
44
|
"@types/react": "~19.2.0",
|
|
45
|
-
"expo": "57.0.
|
|
45
|
+
"expo": "57.0.23",
|
|
46
46
|
"expo-module-scripts": "56.0.3"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"optional": true
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "f6f5af06b95cb7d511b52641885b2fb5adad525d",
|
|
64
64
|
"scripts": {
|
|
65
65
|
"build": "expo-module build",
|
|
66
66
|
"clean": "expo-module clean",
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
package expo.modules.observe.storage
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import androidx.room.Dao
|
|
5
|
-
import androidx.room.Database
|
|
6
|
-
import androidx.room.Entity
|
|
7
|
-
import androidx.room.Insert
|
|
8
|
-
import androidx.room.OnConflictStrategy
|
|
9
|
-
import androidx.room.PrimaryKey
|
|
10
|
-
import androidx.room.Query
|
|
11
|
-
import androidx.room.Room
|
|
12
|
-
import androidx.room.RoomDatabase
|
|
13
|
-
|
|
14
|
-
// TODO: Draining large backlogs from both pending tables reruns `ORDER BY addedAt`
|
|
15
|
-
// for every chunk. Consider `Index("addedAt")` and a version bump if this matters in practice.
|
|
16
|
-
@Entity(tableName = "pending_metrics")
|
|
17
|
-
data class PendingMetric(
|
|
18
|
-
@PrimaryKey val metricId: String,
|
|
19
|
-
// ISO 8601 timestamp
|
|
20
|
-
val addedAt: String
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
@Entity(tableName = "pending_logs")
|
|
24
|
-
data class PendingLog(
|
|
25
|
-
@PrimaryKey val logId: String,
|
|
26
|
-
// ISO 8601 timestamp
|
|
27
|
-
val addedAt: String
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
@Dao
|
|
31
|
-
interface PendingMetricDao {
|
|
32
|
-
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
|
33
|
-
suspend fun insertAll(metrics: List<PendingMetric>)
|
|
34
|
-
|
|
35
|
-
@Query("SELECT metricId FROM pending_metrics ORDER BY addedAt ASC LIMIT :limit")
|
|
36
|
-
suspend fun getMetricIds(limit: Int): List<String>
|
|
37
|
-
|
|
38
|
-
@Query("SELECT EXISTS(SELECT 1 FROM pending_metrics)")
|
|
39
|
-
suspend fun hasMetricIds(): Boolean
|
|
40
|
-
|
|
41
|
-
@Query("DELETE FROM pending_metrics")
|
|
42
|
-
suspend fun deleteAll()
|
|
43
|
-
|
|
44
|
-
@Query("DELETE FROM pending_metrics WHERE metricId IN (:metricIds)")
|
|
45
|
-
suspend fun deleteByIds(metricIds: List<String>)
|
|
46
|
-
|
|
47
|
-
@Query("DELETE FROM pending_metrics WHERE addedAt < :cutoffTimestamp")
|
|
48
|
-
suspend fun deleteOlderThan(cutoffTimestamp: String)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
@Dao
|
|
52
|
-
interface PendingLogDao {
|
|
53
|
-
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
|
54
|
-
suspend fun insertAll(logs: List<PendingLog>)
|
|
55
|
-
|
|
56
|
-
@Query("SELECT logId FROM pending_logs ORDER BY addedAt ASC LIMIT :limit")
|
|
57
|
-
suspend fun getLogIds(limit: Int): List<String>
|
|
58
|
-
|
|
59
|
-
@Query("SELECT EXISTS(SELECT 1 FROM pending_logs)")
|
|
60
|
-
suspend fun hasLogIds(): Boolean
|
|
61
|
-
|
|
62
|
-
@Query("DELETE FROM pending_logs")
|
|
63
|
-
suspend fun deleteAll()
|
|
64
|
-
|
|
65
|
-
@Query("DELETE FROM pending_logs WHERE logId IN (:logIds)")
|
|
66
|
-
suspend fun deleteByIds(logIds: List<String>)
|
|
67
|
-
|
|
68
|
-
@Query("DELETE FROM pending_logs WHERE addedAt < :cutoffTimestamp")
|
|
69
|
-
suspend fun deleteOlderThan(cutoffTimestamp: String)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
@Database(entities = [PendingMetric::class, PendingLog::class], version = 2, exportSchema = false)
|
|
73
|
-
abstract class ObserveDatabase : RoomDatabase() {
|
|
74
|
-
abstract fun pendingMetricDao(): PendingMetricDao
|
|
75
|
-
|
|
76
|
-
abstract fun pendingLogDao(): PendingLogDao
|
|
77
|
-
|
|
78
|
-
companion object {
|
|
79
|
-
@Volatile
|
|
80
|
-
private var INSTANCE: ObserveDatabase? = null
|
|
81
|
-
|
|
82
|
-
fun getDatabase(context: Context): ObserveDatabase =
|
|
83
|
-
INSTANCE ?: synchronized(this) {
|
|
84
|
-
INSTANCE ?: Room
|
|
85
|
-
.databaseBuilder(
|
|
86
|
-
context.applicationContext,
|
|
87
|
-
ObserveDatabase::class.java,
|
|
88
|
-
"eas_observe"
|
|
89
|
-
).fallbackToDestructiveMigration()
|
|
90
|
-
.build()
|
|
91
|
-
.also { INSTANCE = it }
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
package expo.modules.observe.storage
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import androidx.room.withTransaction
|
|
5
|
-
import expo.modules.appmetrics.utils.TimeUtils
|
|
6
|
-
import expo.modules.observe.SQLITE_MAX_BIND_VARIABLES
|
|
7
|
-
|
|
8
|
-
class PendingLogsManager(
|
|
9
|
-
context: Context,
|
|
10
|
-
database: ObserveDatabase? = null
|
|
11
|
-
) {
|
|
12
|
-
private val database: ObserveDatabase = database ?: ObserveDatabase.getDatabase(context)
|
|
13
|
-
|
|
14
|
-
suspend fun addPendingLogs(logIds: List<String>) {
|
|
15
|
-
val now = TimeUtils.getCurrentTimestampInISOFormat()
|
|
16
|
-
val pendingLogs = logIds.map { PendingLog(logId = it, addedAt = now) }
|
|
17
|
-
database.pendingLogDao().insertAll(pendingLogs)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
suspend fun getPendingLogIds(limit: Int): List<String> = database.pendingLogDao().getLogIds(limit)
|
|
21
|
-
|
|
22
|
-
suspend fun hasPendingLogs(): Boolean = database.pendingLogDao().hasLogIds()
|
|
23
|
-
|
|
24
|
-
suspend fun removeAllPendingLogs() = database.pendingLogDao().deleteAll()
|
|
25
|
-
|
|
26
|
-
suspend fun removePendingLogs(logIds: List<String>) {
|
|
27
|
-
database.withTransaction {
|
|
28
|
-
logIds.chunked(SQLITE_MAX_BIND_VARIABLES).forEach { chunk ->
|
|
29
|
-
database.pendingLogDao().deleteByIds(chunk)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
suspend fun cleanupOldPendingLogs() {
|
|
35
|
-
val cutoffTimestamp = TimeUtils.getTimestampInISOFormatFromPast(SECONDS_TO_REMOVE_OLD_PENDING_LOGS)
|
|
36
|
-
database.pendingLogDao().deleteOlderThan(cutoffTimestamp)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
companion object {
|
|
40
|
-
private const val SECONDS_TO_REMOVE_OLD_PENDING_LOGS: Long = 7 * 24 * 60 * 60 // 7 days in seconds
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
package expo.modules.observe.storage
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import androidx.room.withTransaction
|
|
5
|
-
import expo.modules.appmetrics.utils.TimeUtils
|
|
6
|
-
import expo.modules.observe.SQLITE_MAX_BIND_VARIABLES
|
|
7
|
-
|
|
8
|
-
class PendingMetricsManager(
|
|
9
|
-
context: Context,
|
|
10
|
-
database: ObserveDatabase? = null
|
|
11
|
-
) {
|
|
12
|
-
private val database: ObserveDatabase = database ?: ObserveDatabase.getDatabase(context)
|
|
13
|
-
|
|
14
|
-
suspend fun addPendingMetrics(metricIds: List<String>) {
|
|
15
|
-
val now = TimeUtils.getCurrentTimestampInISOFormat()
|
|
16
|
-
val pendingMetrics = metricIds.map { PendingMetric(metricId = it, addedAt = now) }
|
|
17
|
-
database.pendingMetricDao().insertAll(pendingMetrics)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
suspend fun getPendingMetricIds(limit: Int): List<String> = database.pendingMetricDao().getMetricIds(limit)
|
|
21
|
-
|
|
22
|
-
suspend fun hasPendingMetrics(): Boolean = database.pendingMetricDao().hasMetricIds()
|
|
23
|
-
|
|
24
|
-
suspend fun removeAllPendingMetrics() = database.pendingMetricDao().deleteAll()
|
|
25
|
-
|
|
26
|
-
suspend fun removePendingMetrics(metricIds: List<String>) {
|
|
27
|
-
database.withTransaction {
|
|
28
|
-
metricIds.chunked(SQLITE_MAX_BIND_VARIABLES).forEach { chunk ->
|
|
29
|
-
database.pendingMetricDao().deleteByIds(chunk)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
suspend fun cleanupOldPendingMetrics() {
|
|
35
|
-
val cutoffTimestamp = TimeUtils.getTimestampInISOFormatFromPast(SECONDS_TO_REMOVE_OLD_PENDING_METRICS)
|
|
36
|
-
database.pendingMetricDao().deleteOlderThan(cutoffTimestamp)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
companion object {
|
|
40
|
-
private const val SECONDS_TO_REMOVE_OLD_PENDING_METRICS: Long = 7 * 24 * 60 * 60 // 7 days in seconds
|
|
41
|
-
}
|
|
42
|
-
}
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
101a43989137ebb70aabefaf9a2dc10f
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
66178e77bb77fab970066d8e7d3007278d5d42e7
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
72bc4b79e95af73fb199265326ab3aad5ec145d8d3d5b74e710fbad2a51ffdbc
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
dc1e5b0d7d55d2541259696a0c80fc9027f870bc6098795c406318200ee133e96c9f1530e497b0071814e97cbbf2f030888753805c5f872ce88a0ba6bb2d886e
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
6f1ebe2d017e285613ebf412c0534170
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
64c582d3dce97b3992ee5787a183a3548edfbda8
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
338b5ae19b1db326442caf9d501bf5e2a0fa9ccdb0b659c014a08a330bfd2d2b
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1faa3721b4a32ff829d9c3b090c0395259dbfc7a4cf8824734cacecbcb97c5cbf6808d8aa00594ef8b39b8b575738245b76b950e01e71d2e970d0ae4762e9391
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
5e3e34618b3b2a33ba9cafe324de1e6f
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2c48ad595a0bf60b6c06cd4092f3156117009f24
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2d3233b298debd978bf33448f54ba7ae4024e0197b2cd842d98d211d04d08b75
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
6e994053444daf21580be5ac0955fe89ab8fde987ea21c169c0e8b6b8fe4a85e5e2e1b8647553189a673f21312a15cc72f03031da5bb5fd30b926e5f66098c36
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2ccd2a8583a78e1eeea288b47a046161
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
5a52cab46034683f2cc3b187030bf4bfa609110f
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
487e57c4e1833058c62e49eef905b4ddb6f835fc56a513ff7361fcc84e2507da
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a886ff4bf5ce99c1f726c2b4ca0872a3a58cf65137e36c57882034c969be53f34d7baf27ed7b7ef696c3aeb749bb6476254efaaf948589808f040ebe41676558
|