@@ -2,12 +2,14 @@ import type { EventProcessor, Hub, Integration, Transaction } from '@sentry/type
2
2
import type { Profile } from '@sentry/types/src/profiling' ;
3
3
import { logger } from '@sentry/utils' ;
4
4
5
- import type { BrowserClient } from './../client' ;
6
5
import { wrapTransactionWithProfiling } from './hubextensions' ;
7
- import type { ProfiledEvent } from './utils' ;
6
+ import { getAutomatedPageLoadProfile , ProfiledEvent , addProfileToMap , AUTOMATED_PAGELOAD_PROFILE_ID } from './utils' ;
7
+ import { getMainCarrier } from '@sentry/core' ;
8
+ import { JSSelfProfile } from '../../build/npm/types/profiling/jsSelfProfiling' ;
8
9
import {
9
10
addProfilesToEnvelope ,
10
11
createProfilingEvent ,
12
+ isAutomatedPageLoadTransaction ,
11
13
findProfiledTransactionsFromEnvelope ,
12
14
PROFILE_MAP ,
13
15
} from './utils' ;
@@ -37,13 +39,58 @@ export class BrowserProfilingIntegration implements Integration {
37
39
*/
38
40
public setupOnce ( _addGlobalEventProcessor : ( callback : EventProcessor ) => void , getCurrentHub : ( ) => Hub ) : void {
39
41
this . getCurrentHub = getCurrentHub ;
40
- const client = this . getCurrentHub ( ) . getClient ( ) as BrowserClient ;
42
+
43
+ const hub = this . getCurrentHub ( ) ;
44
+ const client = hub . getClient ( ) ;
45
+ const carrier = getMainCarrier ( ) ;
41
46
42
47
if ( client && typeof client . on === 'function' ) {
43
48
client . on ( 'startTransaction' , ( transaction : Transaction ) => {
44
49
wrapTransactionWithProfiling ( transaction ) ;
45
50
} ) ;
46
51
52
+ // If a pageload profile exists, attach finishTransaction handler and set profile_id to the reserved
53
+ // automated page load profile id so that it will get picked up by the beforeEnvelope hook.
54
+ const pageLoadProfile = getAutomatedPageLoadProfile ( carrier ) ;
55
+ if ( pageLoadProfile ) {
56
+ client . on ( 'finishTransaction' , ( transaction : Transaction ) => {
57
+ if ( ! isAutomatedPageLoadTransaction ( transaction ) ) {
58
+ return ;
59
+ }
60
+
61
+ transaction . setContext ( 'profile' , { profile_id : AUTOMATED_PAGELOAD_PROFILE_ID } ) ;
62
+ pageLoadProfile
63
+ . stop ( )
64
+ . then ( ( p : JSSelfProfile ) : null => {
65
+ if ( __DEBUG_BUILD__ ) {
66
+ logger . log (
67
+ `[Profiling] stopped profiling of transaction: ${ transaction . name || transaction . description } ` ,
68
+ ) ;
69
+ }
70
+
71
+ // In case of an overlapping transaction, stopProfiling may return null and silently ignore the overlapping profile.
72
+ if ( ! p ) {
73
+ if ( __DEBUG_BUILD__ ) {
74
+ logger . log (
75
+ `[Profiling] profiler returned null profile for: ${ transaction . name || transaction . description } ` ,
76
+ 'this may indicate an overlapping transaction or a call to stopProfiling with a profile title that was never started' ,
77
+ ) ;
78
+ }
79
+ return null ;
80
+ }
81
+
82
+ addProfileToMap ( AUTOMATED_PAGELOAD_PROFILE_ID , p ) ;
83
+ return null ;
84
+ } )
85
+ . catch ( error => {
86
+ if ( __DEBUG_BUILD__ ) {
87
+ logger . log ( '[Profiling] error while stopping profiler:' , error ) ;
88
+ }
89
+ return null ;
90
+ } ) ;
91
+ } ) ;
92
+ }
93
+
47
94
client . on ( 'beforeEnvelope' , ( envelope ) : void => {
48
95
// if not profiles are in queue, there is nothing to add to the envelope.
49
96
if ( ! PROFILE_MAP [ 'size' ] ) {
@@ -59,7 +106,13 @@ export class BrowserProfilingIntegration implements Integration {
59
106
60
107
for ( const profiledTransaction of profiledTransactionEvents ) {
61
108
const context = profiledTransaction && profiledTransaction . contexts ;
62
- const profile_id = context && context [ 'profile' ] && ( context [ 'profile' ] [ 'profile_id' ] as string ) ;
109
+ const profile_id = context && context [ 'profile' ] && context [ 'profile' ] [ 'profile_id' ] ;
110
+
111
+ if ( typeof profile_id !== "string" ) {
112
+ __DEBUG_BUILD__ &&
113
+ logger . log ( '[Profiling] cannot find profile for a transaction without a profile context' ) ;
114
+ continue ;
115
+ }
63
116
64
117
if ( ! profile_id ) {
65
118
__DEBUG_BUILD__ &&
0 commit comments