@logspace/sdk 1.1.7 → 1.1.9
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/logspace.esm.js +84 -10
- package/logspace.esm.js.map +1 -1
- package/logspace.iife.js +1 -1
- package/logspace.iife.js.map +1 -1
- package/logspace.umd.js +1 -1
- package/logspace.umd.js.map +1 -1
- package/package.json +1 -1
- package/sdk/storage/indexed-db.d.ts +10 -0
package/logspace.esm.js
CHANGED
|
@@ -14490,7 +14490,7 @@ async function updatePendingSessionRetry(sessionId) {
|
|
|
14490
14490
|
async function cleanupOldPendingSessions() {
|
|
14491
14491
|
if (!isIndexedDBAvailable()) return;
|
|
14492
14492
|
const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
14493
|
-
const MAX_RETRIES =
|
|
14493
|
+
const MAX_RETRIES = 10;
|
|
14494
14494
|
const now = Date.now();
|
|
14495
14495
|
try {
|
|
14496
14496
|
const sessions = await getPendingSessions();
|
|
@@ -14505,6 +14505,18 @@ async function cleanupOldPendingSessions() {
|
|
|
14505
14505
|
console.warn("[LogSpace] Failed to cleanup old sessions:", error);
|
|
14506
14506
|
}
|
|
14507
14507
|
}
|
|
14508
|
+
function getBackoffInterval(retryCount) {
|
|
14509
|
+
const baseMs = 60 * 1e3;
|
|
14510
|
+
const maxMs = 60 * 60 * 1e3;
|
|
14511
|
+
const backoff = Math.min(baseMs * Math.pow(2, retryCount), maxMs);
|
|
14512
|
+
return backoff;
|
|
14513
|
+
}
|
|
14514
|
+
function isReadyForRetry(session2) {
|
|
14515
|
+
if (!session2.lastRetryAt) return true;
|
|
14516
|
+
const backoffMs = getBackoffInterval(session2.retryCount);
|
|
14517
|
+
const timeSinceLastRetry = Date.now() - session2.lastRetryAt;
|
|
14518
|
+
return timeSinceLastRetry >= backoffMs;
|
|
14519
|
+
}
|
|
14508
14520
|
function closeDB() {
|
|
14509
14521
|
if (db) {
|
|
14510
14522
|
db.close();
|
|
@@ -14524,15 +14536,17 @@ let rateLimiter = { count: 0, windowStart: 0 };
|
|
|
14524
14536
|
let deduplication = { lastLogHash: null, lastLogCount: 0 };
|
|
14525
14537
|
let currentSize = 0;
|
|
14526
14538
|
let rrwebSize = 0;
|
|
14527
|
-
let
|
|
14528
|
-
|
|
14539
|
+
let rateLimitWarnedThisWindow = false;
|
|
14540
|
+
let rateLimitDroppedCount = 0;
|
|
14529
14541
|
let rrwebRateLimiter = { count: 0, windowStart: 0 };
|
|
14530
14542
|
const RRWEB_RATE_LIMIT = 500;
|
|
14531
14543
|
let idleTimer = null;
|
|
14532
14544
|
let durationTimer = null;
|
|
14533
14545
|
let checkpointTimer = null;
|
|
14534
14546
|
let visibilityTimer = null;
|
|
14547
|
+
let pendingRetryTimer = null;
|
|
14535
14548
|
let lastMouseMoveTime = 0;
|
|
14549
|
+
const PENDING_RETRY_INTERVAL = 2 * 60 * 1e3;
|
|
14536
14550
|
let samplingTriggered = false;
|
|
14537
14551
|
let samplingTriggerType = null;
|
|
14538
14552
|
let samplingFirstTriggerTime = null;
|
|
@@ -14748,18 +14762,23 @@ function checkRateLimit() {
|
|
|
14748
14762
|
const now = Date.now();
|
|
14749
14763
|
const windowMs = 1e3;
|
|
14750
14764
|
if (now - rateLimiter.windowStart > windowMs) {
|
|
14765
|
+
if (config.debug && rateLimitDroppedCount > 0) {
|
|
14766
|
+
console.warn(`[LogSpace] Rate limit: dropped ${rateLimitDroppedCount} logs in last second`);
|
|
14767
|
+
}
|
|
14751
14768
|
rateLimiter.windowStart = now;
|
|
14752
14769
|
rateLimiter.count = 1;
|
|
14770
|
+
rateLimitWarnedThisWindow = false;
|
|
14771
|
+
rateLimitDroppedCount = 0;
|
|
14753
14772
|
return true;
|
|
14754
14773
|
}
|
|
14755
14774
|
rateLimiter.count++;
|
|
14756
14775
|
if (rateLimiter.count > config.limits.rateLimit) {
|
|
14757
|
-
|
|
14758
|
-
|
|
14759
|
-
|
|
14760
|
-
|
|
14761
|
-
|
|
14762
|
-
|
|
14776
|
+
rateLimitDroppedCount++;
|
|
14777
|
+
if (!rateLimitWarnedThisWindow) {
|
|
14778
|
+
rateLimitWarnedThisWindow = true;
|
|
14779
|
+
console.warn(
|
|
14780
|
+
`[LogSpace] Rate limit exceeded (${config.limits.rateLimit}/sec). Logs are being dropped. Consider increasing limits.rateLimit or disabling noisy captures.`
|
|
14781
|
+
);
|
|
14763
14782
|
}
|
|
14764
14783
|
return false;
|
|
14765
14784
|
}
|
|
@@ -15187,6 +15206,48 @@ function stopCheckpointTimer() {
|
|
|
15187
15206
|
checkpointTimer = null;
|
|
15188
15207
|
}
|
|
15189
15208
|
}
|
|
15209
|
+
function startPendingRetryTimer() {
|
|
15210
|
+
if (pendingRetryTimer || !isIndexedDBAvailable()) return;
|
|
15211
|
+
pendingRetryTimer = setInterval(async () => {
|
|
15212
|
+
if (!config?.serverUrl) return;
|
|
15213
|
+
try {
|
|
15214
|
+
const pendingSessions = await getPendingSessions();
|
|
15215
|
+
if (pendingSessions.length === 0) return;
|
|
15216
|
+
const readySessions = pendingSessions.filter(isReadyForRetry);
|
|
15217
|
+
if (readySessions.length === 0) return;
|
|
15218
|
+
if (config.debug) {
|
|
15219
|
+
console.log(
|
|
15220
|
+
`[LogSpace] Periodic retry: ${readySessions.length} of ${pendingSessions.length} pending sessions ready`
|
|
15221
|
+
);
|
|
15222
|
+
}
|
|
15223
|
+
for (const stored of readySessions) {
|
|
15224
|
+
try {
|
|
15225
|
+
await sendPayloadToServer(stored.data);
|
|
15226
|
+
await removePendingSession(stored.id);
|
|
15227
|
+
if (config.debug) {
|
|
15228
|
+
console.log("[LogSpace] Pending session sent:", stored.id);
|
|
15229
|
+
}
|
|
15230
|
+
} catch (error) {
|
|
15231
|
+
await updatePendingSessionRetry(stored.id);
|
|
15232
|
+
if (config.debug) {
|
|
15233
|
+
const nextBackoff = Math.ceil(getBackoffInterval(stored.retryCount + 1) / 1e3);
|
|
15234
|
+
console.warn(`[LogSpace] Retry failed for ${stored.id}, next retry in ${nextBackoff}s`);
|
|
15235
|
+
}
|
|
15236
|
+
}
|
|
15237
|
+
}
|
|
15238
|
+
} catch (error) {
|
|
15239
|
+
if (config?.debug) {
|
|
15240
|
+
console.warn("[LogSpace] Periodic retry check failed:", error);
|
|
15241
|
+
}
|
|
15242
|
+
}
|
|
15243
|
+
}, PENDING_RETRY_INTERVAL);
|
|
15244
|
+
}
|
|
15245
|
+
function stopPendingRetryTimer() {
|
|
15246
|
+
if (pendingRetryTimer) {
|
|
15247
|
+
clearInterval(pendingRetryTimer);
|
|
15248
|
+
pendingRetryTimer = null;
|
|
15249
|
+
}
|
|
15250
|
+
}
|
|
15190
15251
|
const EMERGENCY_BACKUP_KEY = "__logspace_emergency_backup";
|
|
15191
15252
|
const SAMPLING_STATE_KEY = "__logspace_sampling_state";
|
|
15192
15253
|
function saveSamplingState(sessionId) {
|
|
@@ -15837,6 +15898,16 @@ async function recoverPendingSessions() {
|
|
|
15837
15898
|
await removePendingSession(stored.id);
|
|
15838
15899
|
continue;
|
|
15839
15900
|
}
|
|
15901
|
+
if (!isReadyForRetry(stored)) {
|
|
15902
|
+
if (config.debug) {
|
|
15903
|
+
const backoffMs = getBackoffInterval(stored.retryCount);
|
|
15904
|
+
const waitTime = Math.ceil((backoffMs - (Date.now() - (stored.lastRetryAt || 0))) / 1e3);
|
|
15905
|
+
console.log(
|
|
15906
|
+
`[LogSpace] Pending session ${stored.id} waiting for backoff (${waitTime}s remaining, retry #${stored.retryCount})`
|
|
15907
|
+
);
|
|
15908
|
+
}
|
|
15909
|
+
continue;
|
|
15910
|
+
}
|
|
15840
15911
|
try {
|
|
15841
15912
|
await sendPayloadToServer(stored.data);
|
|
15842
15913
|
processedRecoverySessionIds.add(stored.id);
|
|
@@ -15929,6 +16000,7 @@ const LogSpace = {
|
|
|
15929
16000
|
}
|
|
15930
16001
|
unloadHandled = false;
|
|
15931
16002
|
isNavigatingAway = false;
|
|
16003
|
+
startPendingRetryTimer();
|
|
15932
16004
|
this.startSession();
|
|
15933
16005
|
recoverPendingSessions().catch(() => {
|
|
15934
16006
|
});
|
|
@@ -15961,7 +16033,8 @@ const LogSpace = {
|
|
|
15961
16033
|
rrwebSize = 0;
|
|
15962
16034
|
rateLimiter = { count: 0, windowStart: 0 };
|
|
15963
16035
|
rrwebRateLimiter = { count: 0, windowStart: 0 };
|
|
15964
|
-
|
|
16036
|
+
rateLimitWarnedThisWindow = false;
|
|
16037
|
+
rateLimitDroppedCount = 0;
|
|
15965
16038
|
deduplication = { lastLogHash: null, lastLogCount: 0 };
|
|
15966
16039
|
endReason = "manual";
|
|
15967
16040
|
let sessionId;
|
|
@@ -16299,6 +16372,7 @@ const LogSpace = {
|
|
|
16299
16372
|
visibilityTimer = null;
|
|
16300
16373
|
}
|
|
16301
16374
|
stopCheckpointTimer();
|
|
16375
|
+
stopPendingRetryTimer();
|
|
16302
16376
|
stopSamplingTrimTimer();
|
|
16303
16377
|
if (config?.capture.rrweb) {
|
|
16304
16378
|
stopRRWebRecording();
|