Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 820bd77

Browse filesBrowse files
authored
Feat: Add @sentry/tracing (getsentry#2719)
* feat: Create IdleTransaction class (getsentry#2720) * feat: Add BrowserTracing integration (getsentry#2723) * feat: Add span creators to @sentry/tracing package (getsentry#2736) * ref: Convert React and Vue Tracing to use active transaction (getsentry#2741) * build: generate tracing bundles * fix: Remove circular dependency between span and transaction * ref: Add side effects true to tracing * build: Only include @sentry/browser for bundle * fix: Make sure vue and react are backwards compatible with @sentry/apm
1 parent 0f07871 commit 820bd77
Copy full SHA for 820bd77

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner
Expand file treeCollapse file tree

43 files changed

+3919
-121
lines changed
Open diff view settings
Collapse file

‎CHANGELOG.md‎

Copy file name to clipboardExpand all lines: CHANGELOG.md
+2-1Lines changed: 2 additions & 1 deletion
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## Unreleased
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
6+
- [react] feat: Export `createReduxEnhancer` to log redux actions as breadcrumbs, and attach state as an extra. (#2717)
7+
- [tracing] feat: `Add @sentry/tracing` (#2719)
68

79
## 5.19.2
810

@@ -17,7 +19,6 @@
1719
- [tracing] fix: APM CDN bundle expose startTransaction (#2726)
1820
- [browser] fix: Correctly remove all event listeners (#2725)
1921
- [tracing] fix: Add manual `DOMStringList` typing (#2718)
20-
- [react] feat: Export `createReduxEnhancer` to log redux actions as breadcrumbs, and attach state as an extra. (#2717)
2122

2223
## 5.19.0
2324

Collapse file

‎package.json‎

Copy file name to clipboardExpand all lines: package.json
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"packages/minimal",
2929
"packages/node",
3030
"packages/react",
31+
"packages/tracing",
3132
"packages/types",
3233
"packages/typescript",
3334
"packages/utils"
Collapse file

‎packages/integrations/src/vue.ts‎

Copy file name to clipboardExpand all lines: packages/integrations/src/vue.ts
+45-5Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
import { EventProcessor, Hub, Integration, IntegrationClass, Span } from '@sentry/types';
1+
import { EventProcessor, Hub, Integration, IntegrationClass, Scope, Span, Transaction } from '@sentry/types';
22
import { basename, getGlobalObject, logger, timestampWithMs } from '@sentry/utils';
33

44
/**
55
* Used to extract Tracing integration from the current client,
66
* without the need to import `Tracing` itself from the @sentry/apm package.
7+
* @deprecated as @sentry/tracing should be used over @sentry/apm.
78
*/
89
const TRACING_GETTER = ({
910
id: 'Tracing',
1011
} as any) as IntegrationClass<Integration>;
1112

13+
/**
14+
* Used to extract BrowserTracing integration from @sentry/tracing
15+
*/
16+
const BROWSER_TRACING_GETTER = ({
17+
id: 'BrowserTracing',
18+
} as any) as IntegrationClass<Integration>;
19+
1220
/** Global Vue object limited to the methods/attributes we require */
1321
interface VueInstance {
1422
config: {
@@ -229,6 +237,7 @@ export class Vue implements Integration {
229237

230238
// We do this whole dance with `TRACING_GETTER` to prevent `@sentry/apm` from becoming a peerDependency.
231239
// We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods.
240+
// tslint:disable-next-line: deprecation
232241
const tracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER);
233242
if (tracingIntegration) {
234243
// tslint:disable-next-line:no-unsafe-any
@@ -242,6 +251,15 @@ export class Vue implements Integration {
242251
op: 'Vue',
243252
});
244253
}
254+
// Use functionality from @sentry/tracing
255+
} else {
256+
const activeTransaction = getActiveTransaction(getCurrentHub());
257+
if (activeTransaction) {
258+
this._rootSpan = activeTransaction.startChild({
259+
description: 'Application Render',
260+
op: 'Vue',
261+
});
262+
}
245263
}
246264
});
247265
}
@@ -315,15 +333,18 @@ export class Vue implements Integration {
315333
if (this._tracingActivity) {
316334
// We do this whole dance with `TRACING_GETTER` to prevent `@sentry/apm` from becoming a peerDependency.
317335
// We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods.
336+
// tslint:disable-next-line: deprecation
318337
const tracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER);
319338
if (tracingIntegration) {
320339
// tslint:disable-next-line:no-unsafe-any
321340
(tracingIntegration as any).constructor.popActivity(this._tracingActivity);
322-
if (this._rootSpan) {
323-
this._rootSpan.finish(timestamp);
324-
}
325341
}
326342
}
343+
344+
// We should always finish the span, only should pop activity if using @sentry/apm
345+
if (this._rootSpan) {
346+
this._rootSpan.finish(timestamp);
347+
}
327348
}, this._options.tracingOptions.timeout);
328349
}
329350

@@ -333,7 +354,8 @@ export class Vue implements Integration {
333354

334355
this._options.Vue.mixin({
335356
beforeCreate(this: ViewModel): void {
336-
if (getCurrentHub().getIntegration(TRACING_GETTER)) {
357+
// tslint:disable-next-line: deprecation
358+
if (getCurrentHub().getIntegration(TRACING_GETTER) || getCurrentHub().getIntegration(BROWSER_TRACING_GETTER)) {
337359
// `this` points to currently rendered component
338360
applyTracingHooks(this, getCurrentHub);
339361
} else {
@@ -405,3 +427,21 @@ export class Vue implements Integration {
405427
}
406428
}
407429
}
430+
431+
// tslint:disable-next-line: completed-docs
432+
interface HubType extends Hub {
433+
// tslint:disable-next-line: completed-docs
434+
getScope?(): Scope | undefined;
435+
}
436+
437+
/** Grabs active transaction off scope */
438+
export function getActiveTransaction<T extends Transaction>(hub: HubType): T | undefined {
439+
if (hub && hub.getScope) {
440+
const scope = hub.getScope() as Scope;
441+
if (scope) {
442+
return scope.getTransaction() as T | undefined;
443+
}
444+
}
445+
446+
return undefined;
447+
}
Collapse file

‎packages/react/src/profiler.tsx‎

Copy file name to clipboardExpand all lines: packages/react/src/profiler.tsx
+58-36Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { getCurrentHub } from '@sentry/browser';
2-
import { Integration, IntegrationClass, Span } from '@sentry/types';
3-
import { logger, timestampWithMs } from '@sentry/utils';
1+
import { getCurrentHub, Hub } from '@sentry/browser';
2+
import { Integration, IntegrationClass, Span, Transaction } from '@sentry/types';
3+
import { timestampWithMs } from '@sentry/utils';
44
import * as hoistNonReactStatic from 'hoist-non-react-statics';
55
import * as React from 'react';
66

@@ -11,6 +11,7 @@ const TRACING_GETTER = ({
1111
} as any) as IntegrationClass<Integration>;
1212

1313
let globalTracingIntegration: Integration | null = null;
14+
/** @deprecated remove when @sentry/apm no longer used */
1415
const getTracingIntegration = () => {
1516
if (globalTracingIntegration) {
1617
return globalTracingIntegration;
@@ -20,21 +21,11 @@ const getTracingIntegration = () => {
2021
return globalTracingIntegration;
2122
};
2223

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-
3424
/**
3525
* pushActivity creates an new react activity.
3626
* Is a no-op if Tracing integration is not valid
3727
* @param name displayName of component that started activity
28+
* @deprecated remove when @sentry/apm no longer used
3829
*/
3930
function pushActivity(name: string, op: string): number | null {
4031
if (globalTracingIntegration === null) {
@@ -52,6 +43,7 @@ function pushActivity(name: string, op: string): number | null {
5243
* popActivity removes a React activity.
5344
* Is a no-op if Tracing integration is not valid.
5445
* @param activity id of activity that is being popped
46+
* @deprecated remove when @sentry/apm no longer used
5547
*/
5648
function popActivity(activity: number | null): void {
5749
if (activity === null || globalTracingIntegration === null) {
@@ -66,6 +58,7 @@ function popActivity(activity: number | null): void {
6658
* Obtain a span given an activity id.
6759
* Is a no-op if Tracing integration is not valid.
6860
* @param activity activity id associated with obtained span
61+
* @deprecated remove when @sentry/apm no longer used
6962
*/
7063
function getActivitySpan(activity: number | null): Span | undefined {
7164
if (activity === null || globalTracingIntegration === null) {
@@ -96,11 +89,9 @@ export type ProfilerProps = {
9689
*/
9790
class Profiler extends React.Component<ProfilerProps> {
9891
// The activity representing how long it takes to mount a component.
99-
public mountActivity: number | null = null;
92+
private _mountActivity: number | null = null;
10093
// The span of the mount activity
101-
public mountSpan: Span | undefined = undefined;
102-
// The span of the render
103-
public renderSpan: Span | undefined = undefined;
94+
private _mountSpan: Span | undefined = undefined;
10495

10596
public static defaultProps: Partial<ProfilerProps> = {
10697
disabled: false,
@@ -116,33 +107,48 @@ class Profiler extends React.Component<ProfilerProps> {
116107
return;
117108
}
118109

110+
// If they are using @sentry/apm, we need to push/pop activities
111+
// tslint:disable-next-line: deprecation
119112
if (getTracingIntegration()) {
120-
this.mountActivity = pushActivity(name, 'mount');
113+
// tslint:disable-next-line: deprecation
114+
this._mountActivity = pushActivity(name, 'mount');
121115
} else {
122-
warnAboutTracing(name);
116+
const activeTransaction = getActiveTransaction();
117+
if (activeTransaction) {
118+
this._mountSpan = activeTransaction.startChild({
119+
description: `<${name}>`,
120+
op: 'react.mount',
121+
});
122+
}
123123
}
124124
}
125125

126126
// If a component mounted, we can finish the mount activity.
127127
public componentDidMount(): void {
128-
this.mountSpan = getActivitySpan(this.mountActivity);
129-
popActivity(this.mountActivity);
130-
this.mountActivity = null;
128+
if (this._mountSpan) {
129+
this._mountSpan.finish();
130+
} else {
131+
// tslint:disable-next-line: deprecation
132+
this._mountSpan = getActivitySpan(this._mountActivity);
133+
// tslint:disable-next-line: deprecation
134+
popActivity(this._mountActivity);
135+
this._mountActivity = null;
136+
}
131137
}
132138

133139
public componentDidUpdate({ updateProps, includeUpdates = true }: ProfilerProps): void {
134140
// Only generate an update span if hasUpdateSpan is true, if there is a valid mountSpan,
135141
// and if the updateProps have changed. It is ok to not do a deep equality check here as it is expensive.
136142
// We are just trying to give baseline clues for further investigation.
137-
if (includeUpdates && this.mountSpan && updateProps !== this.props.updateProps) {
143+
if (includeUpdates && this._mountSpan && updateProps !== this.props.updateProps) {
138144
// See what props haved changed between the previous props, and the current props. This is
139145
// set as data on the span. We just store the prop keys as the values could be potenially very large.
140146
const changedProps = Object.keys(updateProps).filter(k => updateProps[k] !== this.props.updateProps[k]);
141147
if (changedProps.length > 0) {
142148
// The update span is a point in time span with 0 duration, just signifying that the component
143149
// has been updated.
144150
const now = timestampWithMs();
145-
this.mountSpan.startChild({
151+
this._mountSpan.startChild({
146152
data: {
147153
changedProps,
148154
},
@@ -160,14 +166,14 @@ class Profiler extends React.Component<ProfilerProps> {
160166
public componentWillUnmount(): void {
161167
const { name, includeRender = true } = this.props;
162168

163-
if (this.mountSpan && includeRender) {
169+
if (this._mountSpan && includeRender) {
164170
// If we were able to obtain the spanId of the mount activity, we should set the
165171
// next activity as a child to the component mount activity.
166-
this.mountSpan.startChild({
172+
this._mountSpan.startChild({
167173
description: `<${name}>`,
168174
endTimestamp: timestampWithMs(),
169175
op: `react.render`,
170-
startTimestamp: this.mountSpan.endTimestamp,
176+
startTimestamp: this._mountSpan.endTimestamp,
171177
});
172178
}
173179
}
@@ -221,22 +227,26 @@ function useProfiler(
221227
hasRenderSpan: true,
222228
},
223229
): void {
224-
const [mountActivity] = React.useState(() => {
230+
const [mountSpan] = React.useState(() => {
225231
if (options && options.disabled) {
226-
return null;
232+
return undefined;
227233
}
228234

229-
if (getTracingIntegration()) {
230-
return pushActivity(name, 'mount');
235+
const activeTransaction = getActiveTransaction();
236+
if (activeTransaction) {
237+
return activeTransaction.startChild({
238+
description: `<${name}>`,
239+
op: 'react.mount',
240+
});
231241
}
232242

233-
warnAboutTracing(name);
234-
return null;
243+
return undefined;
235244
});
236245

237246
React.useEffect(() => {
238-
const mountSpan = getActivitySpan(mountActivity);
239-
popActivity(mountActivity);
247+
if (mountSpan) {
248+
mountSpan.finish();
249+
}
240250

241251
return () => {
242252
if (mountSpan && options.hasRenderSpan) {
@@ -252,3 +262,15 @@ function useProfiler(
252262
}
253263

254264
export { withProfiler, Profiler, useProfiler };
265+
266+
/** Grabs active transaction off scope */
267+
export function getActiveTransaction<T extends Transaction>(hub: Hub = getCurrentHub()): T | undefined {
268+
if (hub) {
269+
const scope = hub.getScope();
270+
if (scope) {
271+
return scope.getTransaction() as T | undefined;
272+
}
273+
}
274+
275+
return undefined;
276+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.