Skip to content

Commit 0a7b915

Browse files
authored
ref(vue): Clarify Vue tracing (#16487)
Adds some clarifying comments and changes variable naming to make it easier to understand and parse.
1 parent b1fd4a1 commit 0a7b915

File tree

2 files changed

+50
-44
lines changed

2 files changed

+50
-44
lines changed

packages/vue/src/tracing.ts

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ type Mixins = Parameters<Vue['mixin']>[0];
1212

1313
interface VueSentry extends ViewModel {
1414
readonly $root: VueSentry;
15-
$_sentrySpans?: {
15+
$_sentryComponentSpans?: {
1616
[key: string]: Span | undefined;
1717
};
18-
$_sentryRootSpan?: Span;
19-
$_sentryRootSpanTimer?: ReturnType<typeof setTimeout>;
18+
$_sentryRootComponentSpan?: Span;
19+
$_sentryRootComponentSpanTimer?: ReturnType<typeof setTimeout>;
2020
}
2121

2222
// Mappings from operation to corresponding lifecycle hook.
@@ -31,16 +31,16 @@ const HOOKS: { [key in Operation]: Hook[] } = {
3131
update: ['beforeUpdate', 'updated'],
3232
};
3333

34-
/** Finish top-level span and activity with a debounce configured using `timeout` option */
35-
function finishRootSpan(vm: VueSentry, timestamp: number, timeout: number): void {
36-
if (vm.$_sentryRootSpanTimer) {
37-
clearTimeout(vm.$_sentryRootSpanTimer);
34+
/** Finish top-level component span and activity with a debounce configured using `timeout` option */
35+
function finishRootComponentSpan(vm: VueSentry, timestamp: number, timeout: number): void {
36+
if (vm.$_sentryRootComponentSpanTimer) {
37+
clearTimeout(vm.$_sentryRootComponentSpanTimer);
3838
}
3939

40-
vm.$_sentryRootSpanTimer = setTimeout(() => {
41-
if (vm.$root?.$_sentryRootSpan) {
42-
vm.$root.$_sentryRootSpan.end(timestamp);
43-
vm.$root.$_sentryRootSpan = undefined;
40+
vm.$_sentryRootComponentSpanTimer = setTimeout(() => {
41+
if (vm.$root?.$_sentryRootComponentSpan) {
42+
vm.$root.$_sentryRootComponentSpan.end(timestamp);
43+
vm.$root.$_sentryRootComponentSpan = undefined;
4444
}
4545
}, timeout);
4646
}
@@ -77,11 +77,12 @@ export const createTracingMixins = (options: Partial<TracingOptions> = {}): Mixi
7777

7878
for (const internalHook of internalHooks) {
7979
mixins[internalHook] = function (this: VueSentry) {
80-
const isRoot = this.$root === this;
80+
const isRootComponent = this.$root === this;
8181

82-
if (isRoot) {
83-
this.$_sentryRootSpan =
84-
this.$_sentryRootSpan ||
82+
// 1. Root Component span creation
83+
if (isRootComponent) {
84+
this.$_sentryRootComponentSpan =
85+
this.$_sentryRootComponentSpan ||
8586
startInactiveSpan({
8687
name: 'Application Render',
8788
op: `${VUE_OP}.render`,
@@ -92,35 +93,39 @@ export const createTracingMixins = (options: Partial<TracingOptions> = {}): Mixi
9293
});
9394
}
9495

95-
// Skip components that we don't want to track to minimize the noise and give a more granular control to the user
96-
const name = formatComponentName(this, false);
96+
// 2. Component tracking filter
97+
const componentName = formatComponentName(this, false);
9798

98-
const shouldTrack = Array.isArray(options.trackComponents)
99-
? findTrackComponent(options.trackComponents, name)
100-
: options.trackComponents;
99+
const shouldTrack =
100+
isRootComponent || // We always want to track the root component
101+
(Array.isArray(options.trackComponents)
102+
? findTrackComponent(options.trackComponents, componentName)
103+
: options.trackComponents);
101104

102-
// We always want to track root component
103-
if (!isRoot && !shouldTrack) {
105+
if (!shouldTrack) {
104106
return;
105107
}
106108

107-
this.$_sentrySpans = this.$_sentrySpans || {};
109+
this.$_sentryComponentSpans = this.$_sentryComponentSpans || {};
108110

109-
// Start a new span if current hook is a 'before' hook.
110-
// Otherwise, retrieve the current span and finish it.
111-
if (internalHook == internalHooks[0]) {
112-
const activeSpan = this.$root?.$_sentryRootSpan || getActiveSpan();
111+
// 3. Span lifecycle management based on the hook type
112+
const isBeforeHook = internalHook === internalHooks[0];
113+
const activeSpan = this.$root?.$_sentryRootComponentSpan || getActiveSpan();
114+
115+
if (isBeforeHook) {
116+
// Starting a new span in the "before" hook
113117
if (activeSpan) {
114-
// Cancel old span for this hook operation in case it didn't get cleaned up. We're not actually sure if it
115-
// will ever be the case that cleanup hooks re not called, but we had users report that spans didn't get
116-
// finished so we finish the span before starting a new one, just to be sure.
117-
const oldSpan = this.$_sentrySpans[operation];
118+
// Cancel any existing span for this operation (safety measure)
119+
// We're actually not sure if it will ever be the case that cleanup hooks were not called.
120+
// However, we had users report that spans didn't get finished, so we finished the span before
121+
// starting a new one, just to be sure.
122+
const oldSpan = this.$_sentryComponentSpans[operation];
118123
if (oldSpan) {
119124
oldSpan.end();
120125
}
121126

122-
this.$_sentrySpans[operation] = startInactiveSpan({
123-
name: `Vue ${name}`,
127+
this.$_sentryComponentSpans[operation] = startInactiveSpan({
128+
name: `Vue ${componentName}`,
124129
op: `${VUE_OP}.${operation}`,
125130
attributes: {
126131
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.vue',
@@ -131,13 +136,14 @@ export const createTracingMixins = (options: Partial<TracingOptions> = {}): Mixi
131136
}
132137
} else {
133138
// The span should already be added via the first handler call (in the 'before' hook)
134-
const span = this.$_sentrySpans[operation];
139+
const span = this.$_sentryComponentSpans[operation];
135140
// The before hook did not start the tracking span, so the span was not added.
136141
// This is probably because it happened before there is an active transaction
137-
if (!span) return;
142+
if (!span) return; // Skip if no span was created in the "before" hook
138143
span.end();
139144

140-
finishRootSpan(this, timestampInSeconds(), options.timeout || 2000);
145+
// For any "after" hook, also schedule the root component span to finish
146+
finishRootComponentSpan(this, timestampInSeconds(), options.timeout || 2000);
141147
}
142148
};
143149
}

packages/vue/test/tracing/tracingMixin.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ describe('Vue Tracing Mixins', () => {
4646
mockRootInstance = {
4747
$root: null,
4848
componentName: 'RootComponent',
49-
$_sentrySpans: {},
49+
$_sentryComponentSpans: {},
5050
};
5151
mockRootInstance.$root = mockRootInstance; // Self-reference for root
5252

5353
mockVueInstance = {
5454
$root: mockRootInstance,
5555
componentName: 'TestComponent',
56-
$_sentrySpans: {},
56+
$_sentryComponentSpans: {},
5757
};
5858

5959
(getActiveSpan as any).mockReturnValue({ id: 'parent-span' });
@@ -131,7 +131,7 @@ describe('Vue Tracing Mixins', () => {
131131
// todo/fixme: This root component span is only finished if trackComponents is true --> it should probably be always finished
132132
const mixins = createTracingMixins({ trackComponents: true, timeout: 1000 });
133133
const rootMockSpan = mockSpanFactory();
134-
mockRootInstance.$_sentryRootSpan = rootMockSpan;
134+
mockRootInstance.$_sentryRootComponentSpan = rootMockSpan;
135135

136136
// Create and finish a component span
137137
mixins.beforeMount.call(mockVueInstance);
@@ -160,10 +160,10 @@ describe('Vue Tracing Mixins', () => {
160160
op: 'ui.vue.mount',
161161
}),
162162
);
163-
expect(mockVueInstance.$_sentrySpans.mount).toBeDefined();
163+
expect(mockVueInstance.$_sentryComponentSpans.mount).toBeDefined();
164164

165165
// 2. Get the span for verification
166-
const componentSpan = mockVueInstance.$_sentrySpans.mount;
166+
const componentSpan = mockVueInstance.$_sentryComponentSpans.mount;
167167

168168
// 3. End span in "after" hook
169169
mixins.mounted.call(mockVueInstance);
@@ -175,14 +175,14 @@ describe('Vue Tracing Mixins', () => {
175175

176176
// Create an existing span first
177177
const oldSpan = mockSpanFactory();
178-
mockVueInstance.$_sentrySpans.mount = oldSpan;
178+
mockVueInstance.$_sentryComponentSpans.mount = oldSpan;
179179

180180
// Create a new span for the same operation
181181
mixins.beforeMount.call(mockVueInstance);
182182

183183
// Verify old span was ended and new span was created
184184
expect(oldSpan.end).toHaveBeenCalled();
185-
expect(mockVueInstance.$_sentrySpans.mount).not.toBe(oldSpan);
185+
expect(mockVueInstance.$_sentryComponentSpans.mount).not.toBe(oldSpan);
186186
});
187187

188188
it('should gracefully handle when "after" hook is called without "before" hook', () => {
@@ -197,7 +197,7 @@ describe('Vue Tracing Mixins', () => {
197197

198198
// Remove active spans
199199
(getActiveSpan as any).mockReturnValue(null);
200-
mockRootInstance.$_sentryRootSpan = null;
200+
mockRootInstance.$_sentryRootComponentSpan = null;
201201

202202
// Try to create a span
203203
mixins.beforeMount.call(mockVueInstance);

0 commit comments

Comments
 (0)