@@ -200,26 +200,45 @@ public void finish(
200200 if (!root .isFinished ()
201201 && (!transactionOptions .isWaitForChildren () || hasAllChildrenFinished ())) {
202202
203+ final @ NotNull AtomicReference <List <PerformanceCollectionData >> performanceCollectionData =
204+ new AtomicReference <>();
205+ // We set the new spanFinishedCallback here instead of creation time, calling the old one to
206+ // avoid the user overwrites it by setting a custom spanFinishedCallback on the root span
207+ final @ Nullable SpanFinishedCallback oldCallback = this .root .getSpanFinishedCallback ();
208+ this .root .setSpanFinishedCallback (
209+ span -> {
210+ if (oldCallback != null ) {
211+ oldCallback .execute (span );
212+ }
213+
214+ // Let's call the finishCallback here, when the root span has a finished date but it's
215+ // not finished, yet
216+ final @ Nullable TransactionFinishedCallback finishedCallback =
217+ transactionOptions .getTransactionFinishedCallback ();
218+ if (finishedCallback != null ) {
219+ finishedCallback .execute (this );
220+ }
221+
222+ if (transactionPerformanceCollector != null ) {
223+ performanceCollectionData .set (transactionPerformanceCollector .stop (this ));
224+ }
225+ });
226+
203227 // any un-finished childs will remain unfinished
204228 // as relay takes care of setting the end-timestamp + deadline_exceeded
205229 // see
206230 // https://github.com/getsentry/relay/blob/40697d0a1c54e5e7ad8d183fc7f9543b94fe3839/relay-general/src/store/transactions/processor.rs#L374-L378
207231 root .finish (finishStatus .spanStatus , finishTimestamp );
208232
209- List <PerformanceCollectionData > performanceCollectionData = null ;
210- if (transactionPerformanceCollector != null ) {
211- performanceCollectionData = transactionPerformanceCollector .stop (this );
212- }
213-
214233 ProfilingTraceData profilingTraceData = null ;
215234 if (Boolean .TRUE .equals (isSampled ()) && Boolean .TRUE .equals (isProfileSampled ())) {
216235 profilingTraceData =
217236 hub .getOptions ()
218237 .getTransactionProfiler ()
219- .onTransactionFinish (this , performanceCollectionData , hub .getOptions ());
238+ .onTransactionFinish (this , performanceCollectionData . get () , hub .getOptions ());
220239 }
221- if (performanceCollectionData != null ) {
222- performanceCollectionData .clear ();
240+ if (performanceCollectionData . get () != null ) {
241+ performanceCollectionData .get (). clear ();
223242 }
224243
225244 hub .configureScope (
@@ -232,11 +251,6 @@ public void finish(
232251 });
233252 });
234253 final SentryTransaction transaction = new SentryTransaction (this );
235- final TransactionFinishedCallback finishedCallback =
236- transactionOptions .getTransactionFinishedCallback ();
237- if (finishedCallback != null ) {
238- finishedCallback .execute (this );
239- }
240254
241255 if (timer != null ) {
242256 synchronized (timerLock ) {
@@ -605,7 +619,9 @@ private boolean hasAllChildrenFinished() {
605619 final List <Span > spans = new ArrayList <>(this .children );
606620 if (!spans .isEmpty ()) {
607621 for (final Span span : spans ) {
608- if (!span .isFinished ()) {
622+ // This is used in the spanFinishCallback, when the span isn't finished, but has a finish
623+ // date
624+ if (!span .isFinished () && span .getFinishDate () == null ) {
609625 return false ;
610626 }
611627 }
0 commit comments