11import { 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' ;
44import * as hoistNonReactStatic from 'hoist-non-react-statics' ;
55import * as React from 'react' ;
66
77export const UNKNOWN_COMPONENT = 'unknown' ;
88
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 ( ) ;
4214 }
4315
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 ;
7717}
7818
7919export type ProfilerProps = {
@@ -95,8 +35,6 @@ export type ProfilerProps = {
9535 * spans based on component lifecycles.
9636 */
9737class Profiler extends React . Component < ProfilerProps > {
98- // The activity representing how long it takes to mount a component.
99- public mountActivity : number | null = null ;
10038 // The span of the mount activity
10139 public mountSpan : Span | undefined = undefined ;
10240 // The span of the render
@@ -116,18 +54,21 @@ class Profiler extends React.Component<ProfilerProps> {
11654 return ;
11755 }
11856
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+ } ) ;
12364 }
12465 }
12566
12667 // If a component mounted, we can finish the mount activity.
12768 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+ }
13172 }
13273
13374 public componentDidUpdate ( { updateProps, includeUpdates = true } : ProfilerProps ) : void {
@@ -221,22 +162,27 @@ function useProfiler(
221162 hasRenderSpan : true ,
222163 } ,
223164) : void {
224- const [ mountActivity ] = React . useState ( ( ) => {
165+ const [ mountSpan ] = React . useState ( ( ) => {
225166 if ( options && options . disabled ) {
226- return null ;
167+ return undefined ;
227168 }
228169
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+ } ) ;
231177 }
232178
233- warnAboutTracing ( name ) ;
234- return null ;
179+ return undefined ;
235180 } ) ;
236181
237182 React . useEffect ( ( ) => {
238- const mountSpan = getActivitySpan ( mountActivity ) ;
239- popActivity ( mountActivity ) ;
183+ if ( mountSpan ) {
184+ mountSpan . finish ( ) ;
185+ }
240186
241187 return ( ) => {
242188 if ( mountSpan && options . hasRenderSpan ) {
0 commit comments