1717
1818/**
1919 * Utility class that limits event size to 1MB by incrementally dropping fields when the event
20- * exceeds the limit. This runs after beforeSend and right before sending the event.
21- *
22- * <p>Fields are reduced in order of least importance:
23- *
24- * <ol>
25- * <li>All breadcrumbs
26- * <li>Exception stack frames (keep 250 frames from start and 250 frames from end, removing
27- * middle)
28- * </ol>
29- *
30- * <p>Note: Extras, tags, threads, request data, debug meta, and contexts are preserved.
20+ * exceeds the limit.
3121 */
3222@ ApiStatus .Internal
3323public final class EventSizeLimitingUtils {
3424
35- private static final long MAX_EVENT_SIZE_BYTES = 1024 * 1024 ; // 1MB
36- private static final int FRAMES_PER_SIDE = 250 ; // Keep 250 frames from start and 250 from end
25+ private static final long MAX_EVENT_SIZE_BYTES = 1024 * 1024 ;
26+ private static final int FRAMES_PER_SIDE = 250 ;
3727
3828 private EventSizeLimitingUtils () {}
3929
@@ -57,22 +47,20 @@ private EventSizeLimitingUtils() {}
5747 return event ;
5848 }
5949
60- long eventSize = byteSizeOf (event , options );
6150 options
6251 .getLogger ()
6352 .log (
6453 SentryLevel .INFO ,
65- "Event size (%d bytes) exceeds %d bytes limit. Reducing size by dropping fields." ,
66- eventSize ,
54+ "Event %s exceeds %d bytes limit. Reducing size by dropping fields." ,
55+ event . getEventId () ,
6756 MAX_EVENT_SIZE_BYTES );
6857
69- SentryEvent reducedEvent = event ;
58+ @ NotNull SentryEvent reducedEvent = event ;
7059
71- // Step 0: Invoke custom callback if defined
72- final SentryOptions .OnOversizedErrorCallback onOversizedError = options .getOnOversizedError ();
73- if (onOversizedError != null ) {
60+ final @ Nullable SentryOptions .OnOversizedErrorCallback callback = options .getOnOversizedError ();
61+ if (callback != null ) {
7462 try {
75- reducedEvent = onOversizedError .execute (reducedEvent , hint );
63+ reducedEvent = callback .execute (reducedEvent , hint );
7664 if (!isTooLarge (reducedEvent , options )) {
7765 return reducedEvent ;
7866 }
@@ -83,48 +71,33 @@ private EventSizeLimitingUtils() {}
8371 SentryLevel .ERROR ,
8472 "The onOversizedError callback threw an exception. It will be ignored and automatic reduction will continue." ,
8573 e );
86- // Continue with automatic reduction if callback fails
8774 reducedEvent = event ;
8875 }
8976 }
9077
91- // Step 1: Remove all breadcrumbs
9278 reducedEvent = removeAllBreadcrumbs (reducedEvent , options );
9379 if (!isTooLarge (reducedEvent , options )) {
9480 return reducedEvent ;
9581 }
9682
97- // Step 2: Truncate stack frames (keep 250 from start and 250 from end)
9883 reducedEvent = truncateStackFrames (reducedEvent , options );
9984 if (isTooLarge (reducedEvent , options )) {
100- long finalEventSize = byteSizeOf (reducedEvent , options );
10185 options
10286 .getLogger ()
10387 .log (
10488 SentryLevel .WARNING ,
105- "Event size (%d bytes) still exceeds limit after reducing all fields. Event may be rejected by server." ,
106- finalEventSize );
89+ "Event %s still exceeds size limit after reducing all fields. Event may be rejected by server." ,
90+ event . getEventId () );
10791 }
10892
10993 return reducedEvent ;
11094 }
11195
112- /**
113- * Checks if the event exceeds the size limit.
114- *
115- * @param event the event to check
116- * @param options the SentryOptions
117- * @return true if the event exceeds the size limit
118- */
11996 private static boolean isTooLarge (
12097 final @ NotNull SentryEvent event , final @ NotNull SentryOptions options ) {
121- return byteSizeOf (event , options ) > MAX_EVENT_SIZE_BYTES ;
122- }
123-
124- /** Calculates the size of the event when serialized to JSON without actually storing the data. */
125- private static long byteSizeOf (
126- final @ NotNull SentryEvent event , final @ NotNull SentryOptions options ) {
127- return JsonSerializationUtils .byteSizeOf (options .getSerializer (), options .getLogger (), event );
98+ final long size =
99+ JsonSerializationUtils .byteSizeOf (options .getSerializer (), options .getLogger (), event );
100+ return size > MAX_EVENT_SIZE_BYTES ;
128101 }
129102
130103 private static @ NotNull SentryEvent removeAllBreadcrumbs (
@@ -135,59 +108,54 @@ private static long byteSizeOf(
135108 options
136109 .getLogger ()
137110 .log (
138- SentryLevel .DEBUG , "Removed %d breadcrumbs to reduce event size" , breadcrumbs .size ());
111+ SentryLevel .DEBUG ,
112+ "Removed breadcrumbs to reduce size of event %s" ,
113+ event .getEventId ());
139114 }
140115 return event ;
141116 }
142117
143118 private static @ NotNull SentryEvent truncateStackFrames (
144119 final @ NotNull SentryEvent event , final @ NotNull SentryOptions options ) {
145- final List <SentryException > exceptions = event .getExceptions ();
120+ final @ Nullable List <SentryException > exceptions = event .getExceptions ();
146121 if (exceptions != null ) {
147- for (final SentryException exception : exceptions ) {
148- final SentryStackTrace stacktrace = exception .getStacktrace ();
122+ for (final @ NotNull SentryException exception : exceptions ) {
123+ final @ Nullable SentryStackTrace stacktrace = exception .getStacktrace ();
149124 if (stacktrace != null ) {
150- final List <SentryStackFrame > frames = stacktrace .getFrames ();
151- if (frames != null && frames .size () > FRAMES_PER_SIDE * 2 ) {
152- // Keep first 250 frames and last 250 frames, removing middle
153- final List <SentryStackFrame > truncatedFrames = new ArrayList <>();
125+ final @ Nullable List <SentryStackFrame > frames = stacktrace .getFrames ();
126+ if (frames != null && frames .size () > (FRAMES_PER_SIDE * 2 )) {
127+ final @ NotNull List <SentryStackFrame > truncatedFrames = new ArrayList <>();
154128 truncatedFrames .addAll (frames .subList (0 , FRAMES_PER_SIDE ));
155129 truncatedFrames .addAll (frames .subList (frames .size () - FRAMES_PER_SIDE , frames .size ()));
156130 stacktrace .setFrames (truncatedFrames );
157131 options
158132 .getLogger ()
159133 .log (
160134 SentryLevel .DEBUG ,
161- "Truncated stack frames from %d to %d (removed middle) for exception %s" ,
162- frames .size (),
163- truncatedFrames .size (),
164- exception .getType ());
135+ "Truncated exception stack frames of event %s" ,
136+ event .getEventId ());
165137 }
166138 }
167139 }
168140 }
169141
170- // Also truncate thread stack traces
171- final List <SentryThread > threads = event .getThreads ();
142+ final @ Nullable List <SentryThread > threads = event .getThreads ();
172143 if (threads != null ) {
173144 for (final SentryThread thread : threads ) {
174- final SentryStackTrace stacktrace = thread .getStacktrace ();
145+ final @ Nullable SentryStackTrace stacktrace = thread .getStacktrace ();
175146 if (stacktrace != null ) {
176- final List <SentryStackFrame > frames = stacktrace .getFrames ();
177- if (frames != null && frames .size () > FRAMES_PER_SIDE * 2 ) {
178- // Keep first 250 frames and last 250 frames, removing middle
179- final List <SentryStackFrame > truncatedFrames = new ArrayList <>();
147+ final @ Nullable List <SentryStackFrame > frames = stacktrace .getFrames ();
148+ if (frames != null && frames .size () > (FRAMES_PER_SIDE * 2 )) {
149+ final @ NotNull List <SentryStackFrame > truncatedFrames = new ArrayList <>();
180150 truncatedFrames .addAll (frames .subList (0 , FRAMES_PER_SIDE ));
181151 truncatedFrames .addAll (frames .subList (frames .size () - FRAMES_PER_SIDE , frames .size ()));
182152 stacktrace .setFrames (truncatedFrames );
183153 options
184154 .getLogger ()
185155 .log (
186156 SentryLevel .DEBUG ,
187- "Truncated stack frames from %d to %d (removed middle) for thread %d" ,
188- frames .size (),
189- truncatedFrames .size (),
190- thread .getId ());
157+ "Truncated thread stack frames for event %s" ,
158+ event .getEventId ());
191159 }
192160 }
193161 }
0 commit comments