@@ -28,6 +28,8 @@ import {
28
28
isCollapsedNode ,
29
29
isEAPSpanNode ,
30
30
isEAPTraceNode ,
31
+ isEAPTransaction ,
32
+ isEAPTransactionNode ,
31
33
isJavascriptSDKEvent ,
32
34
isMissingInstrumentationNode ,
33
35
isPageloadTransactionNode ,
@@ -322,7 +324,16 @@ export class TraceTree extends TraceTreeEventDispatcher {
322
324
slug : value . project_slug ,
323
325
} ) ;
324
326
325
- const node = new TraceTreeNode ( parent , value , {
327
+ let parentNode ;
328
+ if ( isEAPTransaction ( value ) ) {
329
+ // For collapsed eap-transactions we still render the embedded eap-transactions as visible children.
330
+ // Mimics the behavior of non-eap traces, enabling a less noisy/summarized view of the trace.
331
+ parentNode = TraceTree . ParentEAPTransaction ( parent ) ?? parent ;
332
+ } else {
333
+ parentNode = parent ;
334
+ }
335
+
336
+ const node = new TraceTreeNode ( parentNode , value , {
326
337
spans : options . meta ?. transaction_child_count_map [ value . event_id ] ?? 0 ,
327
338
project_slug : value && 'project_slug' in value ? value . project_slug : undefined ,
328
339
event_id : value && 'event_id' in value ? value . event_id : undefined ,
@@ -343,7 +354,7 @@ export class TraceTree extends TraceTreeEventDispatcher {
343
354
}
344
355
}
345
356
346
- parent . children . push ( node ) ;
357
+ parentNode . children . push ( node ) ;
347
358
348
359
if ( node . value && 'children' in node . value ) {
349
360
// EAP spans are not sorted by default
@@ -1027,6 +1038,16 @@ export class TraceTree extends TraceTreeEventDispatcher {
1027
1038
return node . tail . children ;
1028
1039
}
1029
1040
1041
+ if ( isEAPTransaction ( node . value ) ) {
1042
+ if ( ! node . expanded ) {
1043
+ // For collapsed eap-transactions we still render the embedded eap-transactions as visible children.
1044
+ // Mimics the behavior of non-eap traces, enabling a less noisy/summarized view of the trace.
1045
+ return node . children . filter ( child => isEAPTransaction ( child . value ) ) ;
1046
+ }
1047
+
1048
+ return node . children ;
1049
+ }
1050
+
1030
1051
return node . children ;
1031
1052
}
1032
1053
@@ -1036,7 +1057,7 @@ export class TraceTree extends TraceTreeEventDispatcher {
1036
1057
const queue : Array < TraceTreeNode < TraceTree . NodeValue > > = [ ] ;
1037
1058
const visibleChildren : Array < TraceTreeNode < TraceTree . NodeValue > > = [ ] ;
1038
1059
1039
- if ( root . expanded || isParentAutogroupedNode ( root ) ) {
1060
+ if ( root . expanded || isParentAutogroupedNode ( root ) || isEAPTransaction ( root . value ) ) {
1040
1061
const children = TraceTree . DirectVisibleChildren ( root ) ;
1041
1062
1042
1063
for ( let i = children . length - 1 ; i >= 0 ; i -- ) {
@@ -1050,7 +1071,11 @@ export class TraceTree extends TraceTreeEventDispatcher {
1050
1071
visibleChildren . push ( node ) ;
1051
1072
1052
1073
// iterate in reverse to ensure nodes are processed in order
1053
- if ( node . expanded || isParentAutogroupedNode ( node ) ) {
1074
+ if (
1075
+ node . expanded ||
1076
+ isParentAutogroupedNode ( node ) ||
1077
+ isEAPTransaction ( node . value )
1078
+ ) {
1054
1079
const children = TraceTree . DirectVisibleChildren ( node ) ;
1055
1080
1056
1081
for ( let i = children . length - 1 ; i >= 0 ; i -- ) {
@@ -1284,6 +1309,11 @@ export class TraceTree extends TraceTreeEventDispatcher {
1284
1309
}
1285
1310
}
1286
1311
}
1312
+ if ( isEAPSpanNode ( n ) ) {
1313
+ if ( n . value . event_id === eventId ) {
1314
+ return true ;
1315
+ }
1316
+ }
1287
1317
if ( isTraceErrorNode ( n ) ) {
1288
1318
return n . value . event_id === eventId ;
1289
1319
}
@@ -1316,21 +1346,73 @@ export class TraceTree extends TraceTreeEventDispatcher {
1316
1346
} ) ;
1317
1347
}
1318
1348
1319
- static ParentTransaction (
1320
- node : TraceTreeNode < TraceTree . NodeValue >
1321
- ) : TraceTreeNode < TraceTree . Transaction > | null {
1349
+ static ParentNode < T extends TraceTree . NodeValue > (
1350
+ node : TraceTreeNode < TraceTree . NodeValue > | null ,
1351
+ predicate : ( n : TraceTreeNode < TraceTree . NodeValue > ) => boolean
1352
+ ) : TraceTreeNode < T > | null {
1353
+ if ( ! node ) {
1354
+ return null ;
1355
+ }
1356
+
1322
1357
let next : TraceTreeNode < TraceTree . NodeValue > | null = node . parent ;
1323
1358
1324
1359
while ( next ) {
1325
- if ( isTransactionNode ( next ) ) {
1326
- return next ;
1360
+ if ( predicate ( next ) ) {
1361
+ return next as TraceTreeNode < T > ;
1327
1362
}
1328
1363
next = next . parent ;
1329
1364
}
1330
1365
1331
1366
return null ;
1332
1367
}
1333
1368
1369
+ static ParentTransaction (
1370
+ node : TraceTreeNode < TraceTree . NodeValue > | null
1371
+ ) : TraceTreeNode < TraceTree . Transaction > | null {
1372
+ return TraceTree . ParentNode < TraceTree . Transaction > ( node , isTransactionNode ) ;
1373
+ }
1374
+
1375
+ static ParentEAPTransaction (
1376
+ node : TraceTreeNode < TraceTree . NodeValue > | null
1377
+ ) : TraceTreeNode < TraceTree . EAPSpan > | null {
1378
+ return TraceTree . ParentNode < TraceTree . EAPSpan > ( node , isEAPTransactionNode ) ;
1379
+ }
1380
+
1381
+ static ReparentEAPTransactions (
1382
+ node : TraceTreeNode < TraceTree . EAPSpan > ,
1383
+ findNewParent : (
1384
+ t : TraceTreeNode < TraceTree . EAPSpan >
1385
+ ) => TraceTreeNode < TraceTree . NodeValue > | null
1386
+ ) : void {
1387
+ // Find all embedded eap-transactions, excluding the node itself
1388
+ const eapTransactions = TraceTree . FindAll (
1389
+ node ,
1390
+ n => isEAPTransactionNode ( n ) && n !== node
1391
+ ) ;
1392
+
1393
+ for ( const t of eapTransactions ) {
1394
+ if ( isEAPTransactionNode ( t ) ) {
1395
+ const newParent = findNewParent ( t ) ;
1396
+
1397
+ // If the transaction already has the correct parent, we can continue
1398
+ if ( newParent === t . parent ) {
1399
+ continue ;
1400
+ }
1401
+
1402
+ // If we have found a new parent to reparent the transaction under,
1403
+ // remove it from its current parent's children and add it to the new parent
1404
+ if ( newParent ) {
1405
+ if ( t . parent ) {
1406
+ t . parent . children = t . parent . children . filter ( c => c !== t ) ;
1407
+ }
1408
+ newParent . children . push ( t ) ;
1409
+ t . parent = newParent ;
1410
+ t . parent . children . sort ( traceChronologicalSort ) ;
1411
+ }
1412
+ }
1413
+ }
1414
+ }
1415
+
1334
1416
expand ( node : TraceTreeNode < TraceTree . NodeValue > , expanded : boolean ) : boolean {
1335
1417
// Trace root nodes are not expandable or collapsable
1336
1418
if ( isTraceNode ( node ) ) {
@@ -1378,7 +1460,22 @@ export class TraceTree extends TraceTreeEventDispatcher {
1378
1460
}
1379
1461
1380
1462
if ( expanded ) {
1463
+ if ( isEAPTransactionNode ( node ) ) {
1464
+ const index = this . list . indexOf ( node ) ;
1465
+ this . list . splice ( index + 1 , TraceTree . VisibleChildren ( node ) . length ) ;
1466
+ }
1467
+
1468
+ // Flip expanded so that we can collect visible children
1381
1469
node . expanded = expanded ;
1470
+
1471
+ // When eap-transaction nodes are expanded, we need to reparent the transactions under
1472
+ // the eap-spans (by their parent_span_id) that were previously hidden.
1473
+ if ( isEAPTransactionNode ( node ) ) {
1474
+ TraceTree . ReparentEAPTransactions ( node , t =>
1475
+ TraceTree . FindByID ( node , t . value . parent_span_id )
1476
+ ) ;
1477
+ }
1478
+
1382
1479
// Flip expanded so that we can collect visible children
1383
1480
const index = this . list . indexOf ( node ) ;
1384
1481
this . list . splice ( index + 1 , 0 , ...TraceTree . VisibleChildren ( node ) ) ;
@@ -1387,8 +1484,18 @@ export class TraceTree extends TraceTreeEventDispatcher {
1387
1484
this . list . splice ( index + 1 , TraceTree . VisibleChildren ( node ) . length ) ;
1388
1485
1389
1486
node . expanded = expanded ;
1487
+
1488
+ // When eap-transaction nodes are collapsed, they still render transactions as visible children.
1489
+ // Reparent the transactions from under the eap-spans in the expanded state, to under the closest eap-transaction
1490
+ // in the collapsed state.
1491
+ if ( isEAPTransactionNode ( node ) ) {
1492
+ TraceTree . ReparentEAPTransactions ( node , t =>
1493
+ TraceTree . ParentEAPTransaction ( t . parent )
1494
+ ) ;
1495
+ }
1496
+
1390
1497
// When transaction nodes are collapsed, they still render child transactions
1391
- if ( isTransactionNode ( node ) ) {
1498
+ if ( isTransactionNode ( node ) || isEAPTransactionNode ( node ) ) {
1392
1499
this . list . splice ( index + 1 , 0 , ...TraceTree . VisibleChildren ( node ) ) ;
1393
1500
}
1394
1501
}
@@ -1672,30 +1779,12 @@ export class TraceTree extends TraceTreeEventDispatcher {
1672
1779
return false ;
1673
1780
}
1674
1781
1675
- if ( isParentAutogroupedNode ( n . parent ) ) {
1676
- if ( n . parent . expanded ) {
1677
- // The autogrouped
1678
- return true ;
1679
- }
1680
- return n . parent . tail . children [ n . parent . tail . children . length - 1 ] === n ;
1681
- }
1682
-
1683
- return n . parent . children [ n . parent . children . length - 1 ] === n ;
1782
+ const visibleChildren = TraceTree . DirectVisibleChildren ( n . parent ) ;
1783
+ return visibleChildren [ visibleChildren . length - 1 ] === n ;
1684
1784
}
1685
1785
1686
1786
static HasVisibleChildren ( node : TraceTreeNode < TraceTree . NodeValue > ) : boolean {
1687
- if ( isParentAutogroupedNode ( node ) ) {
1688
- if ( node . expanded ) {
1689
- return node . head . children . length > 0 ;
1690
- }
1691
- return node . tail . children . length > 0 ;
1692
- }
1693
-
1694
- if ( node . expanded ) {
1695
- return node . children . length > 0 ;
1696
- }
1697
-
1698
- return false ;
1787
+ return TraceTree . VisibleChildren ( node ) . length > 0 ;
1699
1788
}
1700
1789
1701
1790
/**
@@ -2003,7 +2092,8 @@ function printTraceTreeNode(
2003
2092
padding +
2004
2093
( t . value . op || 'unknown span' ) +
2005
2094
' - ' +
2006
- ( t . value . description || 'unknown description' )
2095
+ ( t . value . description || 'unknown description' ) +
2096
+ ( isEAPTransactionNode ( t ) ? ` (eap-transaction)` : '' )
2007
2097
) ;
2008
2098
}
2009
2099
if ( isTransactionNode ( t ) ) {
0 commit comments