You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
By default offline caching is enabled for Android but disabled for JVM.
8
+
It can be enabled by setting SentryOptions.cacheDirPath.
9
+
10
+
For Android, AndroidEnvelopeCache is used. For JVM, if cache path has been configured, EnvelopeCache will be used.
11
+
12
+
Any error, event, transaction, profile, replay etc. is turned into an envelope and then sent into ITransport.send.
13
+
The default implementation is AsyncHttpTransport.
14
+
15
+
If an envelope is dropped due to rate limit and has previously been cached (Cached hint) it will be discarded from the IEnvelopeCache.
16
+
17
+
AsyncHttpTransport.send will enqueue an AsyncHttpTransport.EnvelopeSender task onto an executor.
18
+
19
+
Any envelope that doesn't have the Cached hint will be stored in IEnvelopeCache by the EventSender task. Previously cached envelopes (Cached hint) will have a noop cache passed to AsyncHttpTransport.EnvelopeSender and thus not cache again. It is also possible cache is disabled in general.
20
+
21
+
An envelope being sent directly from SDK API like Sentry.captureException will not have the Retryable hint.
22
+
23
+
In case the SDK is offline, it'll mark the envelope to be retried if it has the Retryable hint.
24
+
If the envelope is not retryable and hasn't been sent to offline cache, it's recorded as lost in a client report.
25
+
26
+
In case the envelope can't be sent due to an error or network connection problems it'll be marked for retry if it has the Retryable hint.
27
+
If it's not retryable and hasn't been cached, it's recorded as lost in a client report.
28
+
29
+
In case the envelope is sent successfully, it'll be discarded from cache.
30
+
31
+
The SDK has multiple mechanisms to deal with envelopes on disk.
32
+
- OutboxSender: Sends events coming from other SDKs like NDK that wrote them to disk.
33
+
- io.sentry.EnvelopeSender: This is the offline cache.
34
+
35
+
Both of these are set up through an integration (SendCachedEnvelopeIntegration) which is configured to use SendFireAndForgetOutboxSender or SendFireAndForgetEnvelopeSender.
36
+
37
+
io.sentry.EnvelopeSender is able to pick up files in the cache directory and send them.
38
+
It will trigger sending envelopes in cache dir on init and when the connection status changes (e.g. the SDK comes back online, meaning it has Internet connection again).
39
+
40
+
## When Envelope Files Are Removed From Cache
41
+
42
+
Envelope files are removed from the cache directory in the following scenarios:
43
+
44
+
### 1. Successful Send to Sentry Server
45
+
When `AsyncHttpTransport` successfully sends an envelope to the Sentry server, it calls `envelopeCache.discard(envelope)` to remove the cached file. This happens in `AsyncHttpTransport.EnvelopeSender.flush()` when `result.isSuccess()` is true.
46
+
47
+
### 2. Rate Limited Previously Cached Envelopes
48
+
If an envelope is dropped due to rate limiting **and** has previously been cached (indicated by the `Cached` hint), it gets discarded immediately via `envelopeCache.discard(envelope)` in `AsyncHttpTransport.send()`.
49
+
In this case the discarded envelope is recorded as lost in client reports.
50
+
51
+
### 3. Offline Cache Processing (EnvelopeSender)
52
+
When the SDK processes cached envelope files from disk (via `EnvelopeSender`), files are deleted after processing **unless** they are marked for retry. In `EnvelopeSender.processFile()`, the file is deleted with `safeDelete(file)` if `!retryable.isRetry()`.
53
+
54
+
### 4. Session File Management
55
+
Session-related files (session.json, previous_session.json) are removed during session lifecycle events like session start/end and abnormal exits.
56
+
57
+
### 5. Cache rotation
58
+
If the number of files in the cache directory has reached the configured limit (SentryOptions.maxCacheItems), the oldest file will be deleted to make room.
59
+
This happens in `CacheStrategy.rotateCacheIfNeeded`. The deleted envelope will be recorded as lost in client reports.
60
+
61
+
## Retry Mechanism
62
+
63
+
**Important**: The SDK does NOT implement a traditional "max retry count" mechanism. Instead:
64
+
65
+
### Infinite Retry Approach
66
+
- **Retryable envelopes**: Stay in cache indefinitely and are retried when conditions improve (network connectivity restored, rate limits expire, etc.)
67
+
- **Non-retryable envelopes**: If they fail to send, they're immediately recorded as lost (not cached for retry)
68
+
69
+
### When Envelopes Are Permanently Lost (Not Due to Retry Limits)
70
+
71
+
1. **Queue Overflow**: When the transport executor queue is full - recorded as `DiscardReason.QUEUE_OVERFLOW`
72
+
73
+
2. **Network Errors (Non-Retryable)**: When an envelope isn't marked as retryable and fails due to network issues - recorded as `DiscardReason.NETWORK_ERROR`
74
+
75
+
3. **Rate Limiting**: When envelope items are dropped due to active rate limits - recorded as `DiscardReason.RATELIMIT_BACKOFF`
76
+
77
+
4. **Cache Overflow**: When the cache directory has reached maxCacheItems, old files are deleted - recorded as `DiscardReason.CACHE_OVERFLOW`
78
+
79
+
### Cache Processing Triggers
80
+
Cached envelopes are processed when:
81
+
- Network connectivity is restored (via connection status observer)
82
+
- SDK initialization occurs
83
+
- Rate limits expire
84
+
- Manual flush operations
85
+
86
+
### File Deletion Implementation
87
+
The actual file deletion is handled by `EnvelopeCache.discard()` which calls `envelopeFile.delete()` and logs errors if deletion fails.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+21Lines changed: 21 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,27 @@
2
2
3
3
## Unreleased
4
4
5
+
### Improvements
6
+
7
+
- Session Replay: Use main thread looper to schedule replay capture ([#4542](https://github.com/getsentry/sentry-java/pull/4542))
8
+
- Use single `LifecycleObserver` and multi-cast it to the integrations interested in lifecycle states ([#4567](https://github.com/getsentry/sentry-java/pull/4567))
9
+
10
+
### Fixes
11
+
12
+
- Cache network capabilities and status to reduce IPC calls ([#4560](https://github.com/getsentry/sentry-java/pull/4560))
- Remove unused method in ManifestMetadataReader ([#4585](https://github.com/getsentry/sentry-java/pull/4585))
15
+
- Have single `NetworkCallback` registered at a time to reduce IPC calls ([#4562](https://github.com/getsentry/sentry-java/pull/4562))
16
+
- Limit ProGuard keep rules for native methods within `sentry-android-ndk` to the `io.sentry.**` namespace. ([#4427](https://github.com/getsentry/sentry-java/pull/4427))
17
+
- If you relied on the Sentry SDK to keep native method names for JNI compatibility within your namespace, please review your ProGuard rules and ensure the configuration still works. Especially when you're not consuming any of the default Android proguard rules (`proguard-android.txt` or `proguard-android-optimize.txt`) the following config should be present:
Copy file name to clipboardExpand all lines: sentry-android-core/api/sentry-android-core.api
+11-3Lines changed: 11 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -166,11 +166,17 @@ public final class io/sentry/android/core/AppLifecycleIntegration : io/sentry/In
166
166
public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V
167
167
}
168
168
169
-
public final class io/sentry/android/core/AppState {
169
+
public final class io/sentry/android/core/AppState : java/io/Closeable {
170
+
public fun close ()V
170
171
public static fun getInstance ()Lio/sentry/android/core/AppState;
171
172
public fun isInBackground ()Ljava/lang/Boolean;
172
173
}
173
174
175
+
public abstract interface class io/sentry/android/core/AppState$AppStateListener {
176
+
public abstract fun onBackground ()V
177
+
public abstract fun onForeground ()V
178
+
}
179
+
174
180
public final class io/sentry/android/core/BuildConfig {
175
181
public static final field BUILD_TYPE Ljava/lang/String;
176
182
public static final field DEBUG Z
@@ -263,7 +269,7 @@ public final class io/sentry/android/core/NdkIntegration : io/sentry/Integration
263
269
}
264
270
265
271
public final class io/sentry/android/core/NetworkBreadcrumbsIntegration : io/sentry/Integration, java/io/Closeable {
266
-
public fun <init> (Landroid/content/Context;Lio/sentry/android/core/BuildInfoProvider;Lio/sentry/ILogger;)V
272
+
public fun <init> (Landroid/content/Context;Lio/sentry/android/core/BuildInfoProvider;)V
267
273
public fun close ()V
268
274
public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V
269
275
}
@@ -422,11 +428,13 @@ public class io/sentry/android/core/SpanFrameMetricsCollector : io/sentry/IPerfo
422
428
public fun onSpanStarted (Lio/sentry/ISpan;)V
423
429
}
424
430
425
-
public final class io/sentry/android/core/SystemEventsBreadcrumbsIntegration : io/sentry/Integration, java/io/Closeable {
431
+
public final class io/sentry/android/core/SystemEventsBreadcrumbsIntegration : io/sentry/Integration, io/sentry/android/core/AppState$AppStateListener, java/io/Closeable {
426
432
public fun <init> (Landroid/content/Context;)V
427
433
public fun <init> (Landroid/content/Context;Ljava/util/List;)V
428
434
public fun close ()V
429
435
public static fun getDefaultActions ()Ljava/util/List;
436
+
public fun onBackground ()V
437
+
public fun onForeground ()V
430
438
public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V
0 commit comments