@@ -100,6 +100,8 @@ import {
100100 type SearchCoverage ,
101101 type SessionTraceFilter ,
102102} from "./sessionTraceSearch" ;
103+ import { newBudgetOutcomes , searchStarted , type BudgetOutcome } from "./sessionViewAnalytics" ;
104+ import { useSessionViewAnalytics } from "./useSessionViewAnalytics" ;
103105import type { SessionTraceRun } from "./sessionTraceTypes" ;
104106import { SESSION_TREE_AUTOSAVE_ID , runReasonLabel } from "./sessionTraceView" ;
105107import {
@@ -151,9 +153,18 @@ export function SessionTraceTab({
151153 interval : newRunsPollIntervalMs ( pages . runs ?? [ ] , pages . totalCount , checkedAtMs ) ,
152154 disabled : pages . state . status !== "ready" ,
153155 onLoad : false ,
156+ onFocus : false ,
154157 callback : ( ) => {
155158 setCheckedAtMs ( Date . now ( ) ) ;
156- pages . refreshNewest ( ) ;
159+ pages . refreshNewest ( "poll" ) ;
160+ } ,
161+ } ) ;
162+ useInterval ( {
163+ disabled : pages . state . status !== "ready" ,
164+ onLoad : false ,
165+ callback : ( ) => {
166+ setCheckedAtMs ( Date . now ( ) ) ;
167+ pages . refreshNewest ( "focus" ) ;
157168 } ,
158169 } ) ;
159170
@@ -304,6 +315,22 @@ function SessionTraceTree({
304315 // The page check runs between renders, so it reads a ref that every event updates at once,
305316 // with the same reducer React runs: a count or a release just sent is never missed.
306317 const loadsRef = useRef ( loads ) ;
318+ const track = useSessionViewAnalytics ( runs . length ) ;
319+ // Budget reached: each outcome once per page view (Expand all near the limit hits it often).
320+ const reportedBudget = useRef ( new Set < BudgetOutcome > ( ) ) ;
321+ const lastBudgetHits = useRef ( loads . budgetHits ) ;
322+ useEffect ( ( ) => {
323+ const outcomes = newBudgetOutcomes (
324+ lastBudgetHits . current ,
325+ loads . budgetHits ,
326+ reportedBudget . current
327+ ) ;
328+ lastBudgetHits . current = loads . budgetHits ;
329+ for ( const outcome of outcomes ) {
330+ reportedBudget . current . add ( outcome ) ;
331+ track ( "budget_reached" , { outcome } ) ;
332+ }
333+ } , [ loads . budgetHits , track ] ) ;
307334 const dispatchLoads = useCallback ( ( action : SessionRunLoadsAction ) => {
308335 loadsRef . current = sessionRunLoadsReducer ( loadsRef . current , action ) ;
309336 reactDispatchLoads ( action ) ;
@@ -618,10 +645,12 @@ function SessionTraceTree({
618645 ) ,
619646 } ;
620647 newestFirst . runFriendlyIds . reverse ( ) ;
648+ track ( "expand_all" , { runs : newestFirst . runFriendlyIds . length } ) ;
621649 dispatchLoads ( newestFirst ) ;
622650 expandNodes ( collapsed . map ( ( node ) => node . id ) ) ;
623651 } ;
624652 const retryRun = ( runFriendlyId : string ) => {
653+ track ( "retry" ) ;
625654 dispatchLoads ( { type : "retry" , runFriendlyId } ) ;
626655 const id = runNodeId ( runFriendlyId ) ;
627656 if ( ! nodes [ id ] ?. expanded ) expandNode ( id , false ) ;
@@ -677,6 +706,7 @@ function SessionTraceTree({
677706 ) ;
678707 const loadAllForSearch = ( ) => {
679708 if ( coverage . toLoad . length === 0 ) return ;
709+ track ( "load_all" , { runs : coverage . toLoad . length } ) ;
680710 for ( const id of coverage . toLoad ) openedForSearch . current . add ( id ) ;
681711 dispatchLoads ( { type : "expandedMany" , runFriendlyIds : coverage . toLoad } ) ;
682712 searchMovingRows . current = true ;
@@ -697,6 +727,9 @@ function SessionTraceTree({
697727 next . text = next . text . toLowerCase ( ) ;
698728 if ( ! isFiltering ( next ) ) next . hiddenRuns = [ ] ;
699729 setFilter ( next ) ;
730+ if ( searchStarted ( filter , next ) ) {
731+ track ( "search_used" , { text : next . text !== "" , errors_only : next . errorsOnly } ) ;
732+ }
700733 if ( ! filtering || isFiltering ( next ) ) return ;
701734
702735 const keep = new Set ( touchedDuringSearch . current ) ;
@@ -1294,6 +1327,7 @@ function RunRowContent({
12941327
12951328// A run whose trace can't load here links to its run page instead of expanding.
12961329function RunFallback ( { run, block } : { run : SessionTraceRun ; block : RunLoadBlock } ) {
1330+ const track = useSessionViewAnalytics ( ) ;
12971331 const organization = useOrganization ( ) ;
12981332 const project = useProject ( ) ;
12991333 const environment = useEnvironment ( ) ;
@@ -1313,7 +1347,10 @@ function RunFallback({ run, block }: { run: SessionTraceRun; block: RunLoadBlock
13131347 < LinkButton
13141348 variant = "secondary/small"
13151349 to = { v3RunPath ( organization , project , environment , run ) }
1316- onClick = { ( event ) => event . stopPropagation ( ) }
1350+ onClick = { ( event ) => {
1351+ event . stopPropagation ( ) ;
1352+ track ( "open_run_page" , { from : "row" , reason : block . kind } ) ;
1353+ } }
13171354 >
13181355 Open run page
13191356 </ LinkButton >
@@ -1337,6 +1374,7 @@ function RunLoadStatus({
13371374 spanCount : number ;
13381375 onRetry : ( runFriendlyId : string ) => void ;
13391376} ) {
1377+ const track = useSessionViewAnalytics ( ) ;
13401378 const organization = useOrganization ( ) ;
13411379 const project = useProject ( ) ;
13421380 const environment = useEnvironment ( ) ;
@@ -1395,7 +1433,10 @@ function RunLoadStatus({
13951433 < LinkButton
13961434 variant = "secondary/small"
13971435 to = { v3RunPath ( organization , project , environment , run ) }
1398- onClick = { ( event ) => event . stopPropagation ( ) }
1436+ onClick = { ( event ) => {
1437+ event . stopPropagation ( ) ;
1438+ track ( "open_run_page" , { from : "row" , reason : "cutOff" } ) ;
1439+ } }
13991440 >
14001441 Open run page
14011442 </ LinkButton >
0 commit comments