1
1
import { getCurrentHub } from '@sentry/browser' ;
2
- import { Integration , IntegrationClass , Span } from '@sentry/types' ;
3
- import { logger , timestampWithMs } from '@sentry/utils' ;
2
+ import { Span , Transaction } from '@sentry/types' ;
3
+ import { timestampWithMs } from '@sentry/utils' ;
4
4
import * as hoistNonReactStatic from 'hoist-non-react-statics' ;
5
5
import * as React from 'react' ;
6
6
7
7
export const UNKNOWN_COMPONENT = 'unknown' ;
8
8
9
- const TRACING_GETTER = ( {
10
- id : 'Tracing' ,
11
- } as any ) as IntegrationClass < Integration > ;
12
-
13
- let globalTracingIntegration : Integration | null = null ;
14
- const getTracingIntegration = ( ) => {
15
- if ( globalTracingIntegration ) {
16
- return globalTracingIntegration ;
17
- }
18
-
19
- globalTracingIntegration = getCurrentHub ( ) . getIntegration ( TRACING_GETTER ) ;
20
- return globalTracingIntegration ;
21
- } ;
22
-
23
- /**
24
- * Warn if tracing integration not configured. Will only warn once.
25
- */
26
- function warnAboutTracing ( name : string ) : void {
27
- if ( globalTracingIntegration === null ) {
28
- logger . warn (
29
- `Unable to profile component ${ name } due to invalid Tracing Integration. Please make sure the Tracing integration is setup properly.` ,
30
- ) ;
31
- }
32
- }
33
-
34
- /**
35
- * pushActivity creates an new react activity.
36
- * Is a no-op if Tracing integration is not valid
37
- * @param name displayName of component that started activity
38
- */
39
- function pushActivity ( name : string , op : string ) : number | null {
40
- if ( globalTracingIntegration === null ) {
41
- return null ;
9
+ function getActiveTransaction ( ) : Transaction | undefined {
10
+ const hub = getCurrentHub ( ) ;
11
+ const scope = hub . getScope ( ) ;
12
+ if ( scope ) {
13
+ return scope . getTransaction ( ) ;
42
14
}
43
15
44
- // tslint:disable-next-line:no-unsafe-any
45
- return ( globalTracingIntegration as any ) . constructor . pushActivity ( name , {
46
- description : `<${ name } >` ,
47
- op : `react.${ op } ` ,
48
- } ) ;
49
- }
50
-
51
- /**
52
- * popActivity removes a React activity.
53
- * Is a no-op if Tracing integration is not valid.
54
- * @param activity id of activity that is being popped
55
- */
56
- function popActivity ( activity : number | null ) : void {
57
- if ( activity === null || globalTracingIntegration === null ) {
58
- return ;
59
- }
60
-
61
- // tslint:disable-next-line:no-unsafe-any
62
- ( globalTracingIntegration as any ) . constructor . popActivity ( activity ) ;
63
- }
64
-
65
- /**
66
- * Obtain a span given an activity id.
67
- * Is a no-op if Tracing integration is not valid.
68
- * @param activity activity id associated with obtained span
69
- */
70
- function getActivitySpan ( activity : number | null ) : Span | undefined {
71
- if ( activity === null || globalTracingIntegration === null ) {
72
- return undefined ;
73
- }
74
-
75
- // tslint:disable-next-line:no-unsafe-any
76
- return ( globalTracingIntegration as any ) . constructor . getActivitySpan ( activity ) as Span | undefined ;
16
+ return undefined ;
77
17
}
78
18
79
19
export type ProfilerProps = {
@@ -95,8 +35,6 @@ export type ProfilerProps = {
95
35
* spans based on component lifecycles.
96
36
*/
97
37
class Profiler extends React . Component < ProfilerProps > {
98
- // The activity representing how long it takes to mount a component.
99
- public mountActivity : number | null = null ;
100
38
// The span of the mount activity
101
39
public mountSpan : Span | undefined = undefined ;
102
40
// The span of the render
@@ -116,18 +54,21 @@ class Profiler extends React.Component<ProfilerProps> {
116
54
return ;
117
55
}
118
56
119
- if ( getTracingIntegration ( ) ) {
120
- this . mountActivity = pushActivity ( name , 'mount' ) ;
121
- } else {
122
- warnAboutTracing ( name ) ;
57
+ const activeTransaction = getActiveTransaction ( ) ;
58
+
59
+ if ( activeTransaction ) {
60
+ this . mountSpan = activeTransaction . startChild ( {
61
+ description : `<${ name } >` ,
62
+ op : 'react.mount' ,
63
+ } ) ;
123
64
}
124
65
}
125
66
126
67
// If a component mounted, we can finish the mount activity.
127
68
public componentDidMount ( ) : void {
128
- this . mountSpan = getActivitySpan ( this . mountActivity ) ;
129
- popActivity ( this . mountActivity ) ;
130
- this . mountActivity = null ;
69
+ if ( this . mountSpan ) {
70
+ this . mountSpan . finish ( ) ;
71
+ }
131
72
}
132
73
133
74
public componentDidUpdate ( { updateProps, includeUpdates = true } : ProfilerProps ) : void {
@@ -221,22 +162,27 @@ function useProfiler(
221
162
hasRenderSpan : true ,
222
163
} ,
223
164
) : void {
224
- const [ mountActivity ] = React . useState ( ( ) => {
165
+ const [ mountSpan ] = React . useState ( ( ) => {
225
166
if ( options && options . disabled ) {
226
- return null ;
167
+ return undefined ;
227
168
}
228
169
229
- if ( getTracingIntegration ( ) ) {
230
- return pushActivity ( name , 'mount' ) ;
170
+ const activeTransaction = getActiveTransaction ( ) ;
171
+
172
+ if ( activeTransaction ) {
173
+ return activeTransaction . startChild ( {
174
+ description : `<${ name } >` ,
175
+ op : 'react.mount' ,
176
+ } ) ;
231
177
}
232
178
233
- warnAboutTracing ( name ) ;
234
- return null ;
179
+ return undefined ;
235
180
} ) ;
236
181
237
182
React . useEffect ( ( ) => {
238
- const mountSpan = getActivitySpan ( mountActivity ) ;
239
- popActivity ( mountActivity ) ;
183
+ if ( mountSpan ) {
184
+ mountSpan . finish ( ) ;
185
+ }
240
186
241
187
return ( ) => {
242
188
if ( mountSpan && options . hasRenderSpan ) {
0 commit comments