Skip to content

Commit 567502a

Browse files
committed
feat: Add @sentry/tracing
CHANGELOG Rename tracing to APM bring back APM fix: APM -> tracing feat(tracing): Add @sentry/tracing delete src mass rename mass move test chore: Change APM -> Tracing feat(tracing): Add IdleTransaction class ref: Delete Tracing integration feat: BrowserTracing integration some comments about what I've done so far router stuff more router changes more comments and thoughts refactor heartbeat beforeFinish Squash 1 router tracing init reset active transactions more idle transaction refactor Revert "mass move test" This reverts commit c1a11a1. Revert "mass rename" This reverts commit 8173c5d. Revert "delete src" This reverts commit 0298cd4. fix routing typings sentry-trace meta tag Add request logic smaller errors + backgroundtab performance fix stuff fix some errors get it working it finally works :) zero counter react changes Vue changes Fix all errors delete tracing again again any typings any changes more refactors Fix performance and router better
1 parent 30e710d commit 567502a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+6158
-125
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66
- [tracing] fix: Add manual `DOMStringList` typing (#2718)
7+
- [tracing] feat: `Add @sentry/tracing` (#2719)
78

89
## 5.19.0
910

package.json

Lines 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"

packages/hub/src/hub.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,8 +376,8 @@ export class Hub implements HubInterface {
376376
/**
377377
* @inheritDoc
378378
*/
379-
public startTransaction(context: TransactionContext): Transaction {
380-
return this._callExtensionMethod('startTransaction', context);
379+
public startTransaction(context: TransactionContext, idleTimeout?: number): Transaction {
380+
return this._callExtensionMethod('startTransaction', context, idleTimeout);
381381
}
382382

383383
/**

packages/integrations/src/vue.ts

Lines changed: 26 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1-
import { EventProcessor, Hub, Integration, IntegrationClass, Span } from '@sentry/types';
1+
import { EventProcessor, Hub, Integration, Scope, Span, Transaction } from '@sentry/types';
22
import { basename, getGlobalObject, logger, timestampWithMs } from '@sentry/utils';
33

4-
/**
5-
* Used to extract Tracing integration from the current client,
6-
* without the need to import `Tracing` itself from the @sentry/apm package.
7-
*/
8-
const TRACING_GETTER = ({
9-
id: 'Tracing',
10-
} as any) as IntegrationClass<Integration>;
4+
// tslint:disable-next-line: completed-docs
5+
function getActiveTransaction(hub: Hub & { getScope?(): Scope }): Transaction | undefined {
6+
if (!hub.getScope) {
7+
return undefined;
8+
}
9+
10+
const scope = hub.getScope();
11+
if (scope) {
12+
return scope.getTransaction();
13+
}
14+
15+
return undefined;
16+
}
1117

1218
/** Global Vue object limited to the methods/attributes we require */
1319
interface VueInstance {
@@ -137,7 +143,6 @@ export class Vue implements Integration {
137143
private readonly _componentsCache: { [key: string]: string } = {};
138144
private _rootSpan?: Span;
139145
private _rootSpanTimer?: ReturnType<typeof setTimeout>;
140-
private _tracingActivity?: number;
141146

142147
/**
143148
* @inheritDoc
@@ -221,27 +226,20 @@ export class Vue implements Integration {
221226
// On the first handler call (before), it'll be undefined, as `$once` will add it in the future.
222227
// However, on the second call (after), it'll be already in place.
223228
if (this._rootSpan) {
224-
this._finishRootSpan(now, getCurrentHub);
229+
this._finishRootSpan(now);
225230
} else {
226231
vm.$once(`hook:${hook}`, () => {
227232
// Create an activity on the first event call. There'll be no second call, as rootSpan will be in place,
228233
// thus new event handler won't be attached.
229234

230235
// We do this whole dance with `TRACING_GETTER` to prevent `@sentry/apm` from becoming a peerDependency.
231236
// We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods.
232-
const tracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER);
233-
if (tracingIntegration) {
234-
// tslint:disable-next-line:no-unsafe-any
235-
this._tracingActivity = (tracingIntegration as any).constructor.pushActivity('Vue Application Render');
236-
// tslint:disable-next-line:no-unsafe-any
237-
const transaction = (tracingIntegration as any).constructor.getTransaction();
238-
if (transaction) {
239-
// tslint:disable-next-line:no-unsafe-any
240-
this._rootSpan = transaction.startChild({
241-
description: 'Application Render',
242-
op: 'Vue',
243-
});
244-
}
237+
const activeTransaction = getActiveTransaction(getCurrentHub());
238+
if (activeTransaction) {
239+
this._rootSpan = activeTransaction.startChild({
240+
description: 'Application Render',
241+
op: 'Vue',
242+
});
245243
}
246244
});
247245
}
@@ -264,7 +262,7 @@ export class Vue implements Integration {
264262
// However, on the second call (after), it'll be already in place.
265263
if (span) {
266264
span.finish();
267-
this._finishRootSpan(now, getCurrentHub);
265+
this._finishRootSpan(now);
268266
} else {
269267
vm.$once(`hook:${hook}`, () => {
270268
if (this._rootSpan) {
@@ -306,23 +304,14 @@ export class Vue implements Integration {
306304
};
307305

308306
/** Finish top-level span and activity with a debounce configured using `timeout` option */
309-
private _finishRootSpan(timestamp: number, getCurrentHub: () => Hub): void {
307+
private _finishRootSpan(timestamp: number): void {
310308
if (this._rootSpanTimer) {
311309
clearTimeout(this._rootSpanTimer);
312310
}
313311

314312
this._rootSpanTimer = setTimeout(() => {
315-
if (this._tracingActivity) {
316-
// We do this whole dance with `TRACING_GETTER` to prevent `@sentry/apm` from becoming a peerDependency.
317-
// We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods.
318-
const tracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER);
319-
if (tracingIntegration) {
320-
// tslint:disable-next-line:no-unsafe-any
321-
(tracingIntegration as any).constructor.popActivity(this._tracingActivity);
322-
if (this._rootSpan) {
323-
this._rootSpan.finish(timestamp);
324-
}
325-
}
313+
if (this._rootSpan) {
314+
this._rootSpan.finish(timestamp);
326315
}
327316
}, this._options.tracingOptions.timeout);
328317
}
@@ -333,7 +322,7 @@ export class Vue implements Integration {
333322

334323
this._options.Vue.mixin({
335324
beforeCreate(this: ViewModel): void {
336-
if (getCurrentHub().getIntegration(TRACING_GETTER)) {
325+
if (getActiveTransaction(getCurrentHub())) {
337326
// `this` points to currently rendered component
338327
applyTracingHooks(this, getCurrentHub);
339328
} else {

packages/react/src/profiler.tsx

Lines changed: 31 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,19 @@
11
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';
44
import * as hoistNonReactStatic from 'hoist-non-react-statics';
55
import * as React from 'react';
66

77
export 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

7919
export type ProfilerProps = {
@@ -95,8 +35,6 @@ export type ProfilerProps = {
9535
* spans based on component lifecycles.
9636
*/
9737
class 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) {

packages/tracing/.npmignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*
2+
!/dist/**/*
3+
!/esm/**/*
4+
*.tsbuildinfo

packages/tracing/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<p align="center">
2+
<a href="https://sentry.io" target="_blank" align="center">
3+
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280">
4+
</a>
5+
<br />
6+
</p>
7+
8+
# Sentry Tracing Extensions
9+
10+
[![npm version](https://img.shields.io/npm/v/@sentry/tracing.svg)](https://www.npmjs.com/package/@sentry/tracing)
11+
[![npm dm](https://img.shields.io/npm/dm/@sentry/tracing.svg)](https://www.npmjs.com/package/@sentry/tracing)
12+
[![npm dt](https://img.shields.io/npm/dt/@sentry/tracing.svg)](https://www.npmjs.com/package/@sentry/tracing)
13+
[![typedoc](https://img.shields.io/badge/docs-typedoc-blue.svg)](http://getsentry.github.io/sentry-javascript/)
14+
15+
## Links
16+
17+
- [Official SDK Docs](https://docs.sentry.io/quickstart/)
18+
- [TypeDoc](http://getsentry.github.io/sentry-javascript/)
19+
20+
## General
21+
22+
This package contains extensions to the `@sentry/hub` to enable Sentry AM related functionality. It also provides integrations for Browser and Node that provide a good experience out of the box.

packages/tracing/apm/.npmignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*
2+
!/dist/**/*
3+
!/esm/**/*
4+
*.tsbuildinfo

packages/tracing/apm/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<p align="center">
2+
<a href="https://sentry.io" target="_blank" align="center">
3+
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280">
4+
</a>
5+
<br />
6+
</p>
7+
8+
# Sentry Tracing Extensions
9+
10+
[![npm version](https://img.shields.io/npm/v/@sentry/tracing.svg)](https://www.npmjs.com/package/@sentry/tracing)
11+
[![npm dm](https://img.shields.io/npm/dm/@sentry/tracing.svg)](https://www.npmjs.com/package/@sentry/tracing)
12+
[![npm dt](https://img.shields.io/npm/dt/@sentry/tracing.svg)](https://www.npmjs.com/package/@sentry/tracing)
13+
[![typedoc](https://img.shields.io/badge/docs-typedoc-blue.svg)](http://getsentry.github.io/sentry-javascript/)
14+
15+
## Links
16+
17+
- [Official SDK Docs](https://docs.sentry.io/quickstart/)
18+
- [TypeDoc](http://getsentry.github.io/sentry-javascript/)
19+
20+
## General
21+
22+
This package contains extensions to the `@sentry/hub` to enable Sentry AM related functionality. It also provides integrations for Browser and Node that provide a good experience out of the box.

0 commit comments

Comments
 (0)