Skip to content

Commit 68f26cc

Browse files
authored
ref(react): Use new span API in React Profiler (#10104)
1 parent b13a82e commit 68f26cc

File tree

3 files changed

+104
-123
lines changed

3 files changed

+104
-123
lines changed

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export {
3535
startSession,
3636
endSession,
3737
captureSession,
38+
withActiveSpan,
3839
} from './exports';
3940
export {
4041
// eslint-disable-next-line deprecation/deprecation

packages/react/src/profiler.tsx

Lines changed: 54 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
2-
/* eslint-disable @typescript-eslint/no-explicit-any */
3-
import type { Hub } from '@sentry/browser';
4-
import { getCurrentHub } from '@sentry/browser';
5-
import { spanToJSON } from '@sentry/core';
6-
import type { Span, Transaction } from '@sentry/types';
1+
import { startInactiveSpan } from '@sentry/browser';
2+
import { spanToJSON, withActiveSpan } from '@sentry/core';
3+
import type { Span } from '@sentry/types';
74
import { timestampInSeconds } from '@sentry/utils';
85
import hoistNonReactStatics from 'hoist-non-react-statics';
96
import * as React from 'react';
@@ -58,16 +55,12 @@ class Profiler extends React.Component<ProfilerProps> {
5855
return;
5956
}
6057

61-
const activeTransaction = getActiveTransaction();
62-
if (activeTransaction) {
63-
// eslint-disable-next-line deprecation/deprecation
64-
this._mountSpan = activeTransaction.startChild({
65-
description: `<${name}>`,
66-
op: REACT_MOUNT_OP,
67-
origin: 'auto.ui.react.profiler',
68-
data: { 'ui.component_name': name },
69-
});
70-
}
58+
this._mountSpan = startInactiveSpan({
59+
name: `<${name}>`,
60+
op: REACT_MOUNT_OP,
61+
origin: 'auto.ui.react.profiler',
62+
attributes: { 'ui.component_name': name },
63+
});
7164
}
7265

7366
// If a component mounted, we can finish the mount activity.
@@ -87,16 +80,17 @@ class Profiler extends React.Component<ProfilerProps> {
8780
const changedProps = Object.keys(updateProps).filter(k => updateProps[k] !== this.props.updateProps[k]);
8881
if (changedProps.length > 0) {
8982
const now = timestampInSeconds();
90-
// eslint-disable-next-line deprecation/deprecation
91-
this._updateSpan = this._mountSpan.startChild({
92-
data: {
93-
changedProps,
94-
'ui.component_name': this.props.name,
95-
},
96-
description: `<${this.props.name}>`,
97-
op: REACT_UPDATE_OP,
98-
origin: 'auto.ui.react.profiler',
99-
startTimestamp: now,
83+
this._updateSpan = withActiveSpan(this._mountSpan, () => {
84+
return startInactiveSpan({
85+
name: `<${this.props.name}>`,
86+
op: REACT_UPDATE_OP,
87+
origin: 'auto.ui.react.profiler',
88+
startTimestamp: now,
89+
attributes: {
90+
'ui.component_name': this.props.name,
91+
'ui.react.changed_props': changedProps,
92+
},
93+
});
10094
});
10195
}
10296
}
@@ -114,19 +108,24 @@ class Profiler extends React.Component<ProfilerProps> {
114108
// If a component is unmounted, we can say it is no longer on the screen.
115109
// This means we can finish the span representing the component render.
116110
public componentWillUnmount(): void {
111+
const endTimestamp = timestampInSeconds();
117112
const { name, includeRender = true } = this.props;
118113

119114
if (this._mountSpan && includeRender) {
120-
// If we were able to obtain the spanId of the mount activity, we should set the
121-
// next activity as a child to the component mount activity.
122-
// eslint-disable-next-line deprecation/deprecation
123-
this._mountSpan.startChild({
124-
description: `<${name}>`,
125-
endTimestamp: timestampInSeconds(),
126-
op: REACT_RENDER_OP,
127-
origin: 'auto.ui.react.profiler',
128-
startTimestamp: spanToJSON(this._mountSpan).timestamp,
129-
data: { 'ui.component_name': name },
115+
const startTimestamp = spanToJSON(this._mountSpan).timestamp;
116+
withActiveSpan(this._mountSpan, () => {
117+
const renderSpan = startInactiveSpan({
118+
name: `<${name}>`,
119+
op: REACT_RENDER_OP,
120+
origin: 'auto.ui.react.profiler',
121+
startTimestamp,
122+
attributes: { 'ui.component_name': name },
123+
});
124+
if (renderSpan) {
125+
// Have to cast to Span because the type of _mountSpan is Span | undefined
126+
// and not getting narrowed properly
127+
renderSpan.end(endTimestamp);
128+
}
130129
});
131130
}
132131
}
@@ -144,6 +143,7 @@ class Profiler extends React.Component<ProfilerProps> {
144143
* @param WrappedComponent component that is wrapped by Profiler
145144
* @param options the {@link ProfilerProps} you can pass into the Profiler
146145
*/
146+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
147147
function withProfiler<P extends Record<string, any>>(
148148
WrappedComponent: React.ComponentType<P>,
149149
// We do not want to have `updateProps` given in options, it is instead filled through the HOC.
@@ -185,18 +185,12 @@ function useProfiler(
185185
return undefined;
186186
}
187187

188-
const activeTransaction = getActiveTransaction();
189-
if (activeTransaction) {
190-
// eslint-disable-next-line deprecation/deprecation
191-
return activeTransaction.startChild({
192-
description: `<${name}>`,
193-
op: REACT_MOUNT_OP,
194-
origin: 'auto.ui.react.profiler',
195-
data: { 'ui.component_name': name },
196-
});
197-
}
198-
199-
return undefined;
188+
return startInactiveSpan({
189+
name: `<${name}>`,
190+
op: REACT_MOUNT_OP,
191+
origin: 'auto.ui.react.profiler',
192+
attributes: { 'ui.component_name': name },
193+
});
200194
});
201195

202196
React.useEffect(() => {
@@ -206,15 +200,21 @@ function useProfiler(
206200

207201
return (): void => {
208202
if (mountSpan && options.hasRenderSpan) {
209-
// eslint-disable-next-line deprecation/deprecation
210-
mountSpan.startChild({
211-
description: `<${name}>`,
212-
endTimestamp: timestampInSeconds(),
203+
const startTimestamp = spanToJSON(mountSpan).timestamp;
204+
const endTimestamp = timestampInSeconds();
205+
206+
const renderSpan = startInactiveSpan({
207+
name: `<${name}>`,
213208
op: REACT_RENDER_OP,
214209
origin: 'auto.ui.react.profiler',
215-
startTimestamp: spanToJSON(mountSpan).timestamp,
216-
data: { 'ui.component_name': name },
210+
startTimestamp,
211+
attributes: { 'ui.component_name': name },
217212
});
213+
if (renderSpan) {
214+
// Have to cast to Span because the type of _mountSpan is Span | undefined
215+
// and not getting narrowed properly
216+
renderSpan.end(endTimestamp);
217+
}
218218
}
219219
};
220220
// We only want this to run once.
@@ -223,18 +223,3 @@ function useProfiler(
223223
}
224224

225225
export { withProfiler, Profiler, useProfiler };
226-
227-
/** Grabs active transaction off scope */
228-
export function getActiveTransaction<T extends Transaction>(
229-
// eslint-disable-next-line deprecation/deprecation
230-
hub: Hub = getCurrentHub(),
231-
): T | undefined {
232-
if (hub) {
233-
// eslint-disable-next-line deprecation/deprecation
234-
const scope = hub.getScope();
235-
// eslint-disable-next-line deprecation/deprecation
236-
return scope.getTransaction() as T | undefined;
237-
}
238-
239-
return undefined;
240-
}

0 commit comments

Comments
 (0)