Skip to content

Commit ad44d5f

Browse files
committed
review changes part 1
1 parent f32e0b5 commit ad44d5f

File tree

6 files changed

+49
-67
lines changed

6 files changed

+49
-67
lines changed

dev-packages/browser-integration-tests/suites/profiling/traceLifecycleMode_multiple-chunks/test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ sentryTest(
164164
expect(envelopeItemPayload2.profile).toBeDefined();
165165
expect(envelopeItemPayload2.version).toBe('2');
166166
expect(envelopeItemPayload2.platform).toBe('javascript');
167-
expect(envelopeItemPayload2?.profile).toBeDefined();
168167

169168
// Required profile metadata (Sample Format V2)
170169
// https://develop.sentry.dev/sdk/telemetry/profiles/sample-format-v2/

dev-packages/browser-integration-tests/suites/profiling/traceLifecycleMode_overlapping-spans/subject.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,5 @@ await new Promise(r => setTimeout(r, 21));
4848

4949
firstSpan.end();
5050

51-
// Ensure envelope flush
5251
const client = Sentry.getClient();
5352
await client?.flush(5000);

packages/browser/src/profiling/integration.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ const _browserProfilingIntegration = (() => {
2828
setup(client) {
2929
const options = client.getOptions() as BrowserOptions;
3030

31-
if (options && !hasLegacyProfiling(options) && !options.profileLifecycle) {
31+
if (!hasLegacyProfiling(options) && !options.profileLifecycle) {
3232
// Set default lifecycle mode
3333
options.profileLifecycle = 'manual';
3434
}
3535

36-
if (!options || (hasLegacyProfiling(options) && !options.profilesSampleRate)) {
36+
if (hasLegacyProfiling(options) && !options.profilesSampleRate) {
3737
DEBUG_BUILD && debug.log('[Profiling] Profiling disabled, no profiling options found.');
3838
return;
3939
}

packages/browser/src/profiling/lifecycleMode/traceLifecycleProfiler.ts

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
getGlobalScope,
88
getRootSpan,
99
getSdkMetadataForEnvelopeHeader,
10-
spanToJSON,
1110
uuid4,
1211
} from '@sentry/core';
1312
import { DEBUG_BUILD } from '../../debug-build';
@@ -30,7 +29,6 @@ export class BrowserTraceLifecycleProfiler {
3029
private _client: Client | undefined;
3130
private _profiler: JSSelfProfiler | undefined;
3231
private _chunkTimer: ReturnType<typeof setTimeout> | undefined;
33-
private _activeRootSpanCount: number;
3432
// For keeping track of active root spans
3533
private _activeRootSpanIds: Set<string>;
3634
private _profilerId: string | undefined;
@@ -41,7 +39,6 @@ export class BrowserTraceLifecycleProfiler {
4139
this._client = undefined;
4240
this._profiler = undefined;
4341
this._chunkTimer = undefined;
44-
this._activeRootSpanCount = 0;
4542
this._activeRootSpanIds = new Set<string>();
4643
this._profilerId = undefined;
4744
this._isRunning = false;
@@ -58,37 +55,36 @@ export class BrowserTraceLifecycleProfiler {
5855
this._sessionSampled = sessionSampled;
5956

6057
client.on('spanStart', span => {
61-
if (span !== getRootSpan(span)) {
62-
return;
63-
}
6458
if (!this._sessionSampled) {
6559
DEBUG_BUILD && debug.log('[Profiling] Session not sampled because of negative sampling decision.');
6660
return;
6761
}
62+
if (span !== getRootSpan(span)) {
63+
return;
64+
}
6865
// Only count sampled root spans
6966
if (!span.isRecording()) {
7067
DEBUG_BUILD && debug.log('[Profiling] Discarding profile because root span was not sampled.');
7168
return;
7269
}
7370

74-
const rootSpanJSON = spanToJSON(span);
75-
const spanId = rootSpanJSON.span_id as string | undefined;
71+
const spanId = span.spanContext().spanId;
7672
if (!spanId) {
7773
return;
7874
}
7975
if (this._activeRootSpanIds.has(spanId)) {
8076
return;
8177
}
78+
8279
this._activeRootSpanIds.add(spanId);
80+
const rootSpanCount = this._activeRootSpanIds.size;
81+
82+
if (rootSpanCount === 1) {
83+
DEBUG_BUILD &&
84+
debug.log(
85+
`[Profiling] Root span with ID ${spanId} started. Will continue profiling for as long as there are active root spans (currently: ${rootSpanCount}).`,
86+
);
8387

84-
const wasZero = this._activeRootSpanCount === 0;
85-
this._activeRootSpanCount++; // Increment before eventually starting the profiler
86-
DEBUG_BUILD &&
87-
debug.log(
88-
`[Profiling] Root span ${rootSpanJSON.description} started. Active root spans:`,
89-
this._activeRootSpanCount,
90-
);
91-
if (wasZero) {
9288
this.start();
9389
}
9490
});
@@ -98,19 +94,19 @@ export class BrowserTraceLifecycleProfiler {
9894
return;
9995
}
10096

101-
const spanJSON = spanToJSON(span);
102-
const spanId = spanJSON.span_id as string | undefined;
97+
const spanId = span.spanContext().spanId;
10398
if (!spanId || !this._activeRootSpanIds.has(spanId)) {
10499
return;
105100
}
106101

107102
this._activeRootSpanIds.delete(spanId);
108-
this._activeRootSpanCount = Math.max(0, this._activeRootSpanCount - 1);
103+
const rootSpanCount = this._activeRootSpanIds.size;
104+
109105
DEBUG_BUILD &&
110106
debug.log(
111-
`[Profiling] Root span ${spanJSON.description} ended. Active root spans: ${this._activeRootSpanCount}`,
107+
`[Profiling] Root span with ID ${spanId} ended. Will continue profiling for as long as there are active root spans (currently: ${rootSpanCount}).`,
112108
);
113-
if (this._activeRootSpanCount === 0) {
109+
if (rootSpanCount === 0) {
114110
this._collectCurrentChunk().catch(() => /* no catch */ {});
115111

116112
this.stop();
@@ -121,29 +117,24 @@ export class BrowserTraceLifecycleProfiler {
121117
/**
122118
* Handle an already-active root span at integration setup time.
123119
*/
124-
public notifyRootSpanActive(span: Span): void {
120+
public notifyRootSpanActive(rootSpan: Span): void {
125121
if (!this._sessionSampled) {
126122
return;
127123
}
128-
if (span !== getRootSpan(span)) {
129-
return;
130-
}
131-
const spanId = spanToJSON(span)?.span_id;
124+
125+
const spanId = rootSpan.spanContext().spanId;
132126
if (!spanId || this._activeRootSpanIds.has(spanId)) {
133127
return;
134128
}
135129

136-
const wasZero = this._activeRootSpanCount === 0;
137-
138130
this._activeRootSpanIds.add(spanId);
139-
this._activeRootSpanCount++;
140-
DEBUG_BUILD &&
141-
debug.log(
142-
'[Profiling] Detected already active root span during setup. Active root spans:',
143-
this._activeRootSpanCount,
144-
);
145-
146-
if (wasZero) {
131+
132+
const rootSpanCount = this._activeRootSpanIds.size;
133+
134+
if (rootSpanCount === 1) {
135+
DEBUG_BUILD &&
136+
debug.log('[Profiling] Detected already active root span during setup. Active root spans now:', rootSpanCount);
137+
147138
this.start();
148139
}
149140
}

packages/browser/src/profiling/utils.ts

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ export function createProfileChunkPayload(
218218
client: Client,
219219
profilerId?: string,
220220
): ProfileChunk {
221-
if (jsSelfProfile === undefined || jsSelfProfile === null) {
221+
// only == to catch null and undefined
222+
if (jsSelfProfile == null) {
222223
throw new TypeError(
223224
`Cannot construct profiling event envelope without a valid profile. Got ${jsSelfProfile} instead.`,
224225
);
@@ -293,13 +294,13 @@ export function validateProfileChunk(chunk: ProfileChunk): { valid: boolean; rea
293294
return { valid: false, reason: 'missing profile data' };
294295
}
295296

296-
if (!Array.isArray(profile.frames) || profile.frames.length === 0) {
297+
if (!Array.isArray(profile.frames) || !profile.frames.length) {
297298
return { valid: false, reason: 'profile has no frames' };
298299
}
299-
if (!Array.isArray(profile.stacks) || profile.stacks.length === 0) {
300+
if (!Array.isArray(profile.stacks) || !profile.stacks.length) {
300301
return { valid: false, reason: 'profile has no stacks' };
301302
}
302-
if (!Array.isArray(profile.samples) || profile.samples.length === 0) {
303+
if (!Array.isArray(profile.samples) || !profile.samples.length) {
303304
return { valid: false, reason: 'profile has no samples' };
304305
}
305306

@@ -701,7 +702,7 @@ export function shouldProfileSpanLegacy(span: Span): boolean {
701702
/**
702703
* Determine if a profile should be created for the current session (lifecycle profiling mode).
703704
*/
704-
export function shouldProfileSession(options?: BrowserOptions): boolean {
705+
export function shouldProfileSession(options: BrowserOptions): boolean {
705706
// If constructor failed once, it will always fail, so we can early return.
706707
if (PROFILING_CONSTRUCTOR_FAILED) {
707708
if (DEBUG_BUILD) {
@@ -710,16 +711,12 @@ export function shouldProfileSession(options?: BrowserOptions): boolean {
710711
return false;
711712
}
712713

713-
if (!options || options.profileLifecycle !== 'trace') {
714+
if (options.profileLifecycle !== 'trace') {
714715
return false;
715716
}
716717

717718
// Session sampling: profileSessionSampleRate gates whether profiling is enabled for this session
718-
const profileSessionSampleRate: number | boolean | undefined = (
719-
options as unknown as {
720-
profileSessionSampleRate?: number | boolean;
721-
}
722-
).profileSessionSampleRate;
719+
const profileSessionSampleRate = options.profileSessionSampleRate;
723720

724721
if (!isValidSampleRate(profileSessionSampleRate)) {
725722
DEBUG_BUILD && debug.warn('[Profiling] Discarding profile because of invalid profileSessionSampleRate.');
@@ -732,15 +729,14 @@ export function shouldProfileSession(options?: BrowserOptions): boolean {
732729
return false;
733730
}
734731

735-
return profileSessionSampleRate === true ? true : Math.random() <= profileSessionSampleRate;
732+
return Math.random() <= profileSessionSampleRate;
736733
}
737734

738735
/**
739736
* Checks if legacy profiling is configured.
740737
*/
741-
export function hasLegacyProfiling(options: BrowserOptions = {} as unknown as BrowserOptions): boolean {
742-
// eslint-disable-next-line deprecation/deprecation
743-
return typeof (options as unknown as { profilesSampleRate?: number | boolean }).profilesSampleRate !== 'undefined';
738+
export function hasLegacyProfiling(options: BrowserOptions): boolean {
739+
return typeof options.profilesSampleRate !== 'undefined';
744740
}
745741

746742
/**
@@ -820,14 +816,11 @@ export function attachProfiledThreadToEvent(event: Event): void {
820816
};
821817

822818
// Attach thread info to individual spans so that spans can be associated with the profiled thread on the UI even if contexts are missing.
823-
if (Array.isArray(event.spans)) {
824-
const spans = event.spans;
825-
for (const span of spans) {
826-
span.data = {
827-
...(span.data || {}),
828-
['thread.id']: PROFILER_THREAD_ID_STRING,
829-
['thread.name']: PROFILER_THREAD_NAME,
830-
};
831-
}
832-
}
819+
event.spans?.forEach(span => {
820+
span.data = {
821+
...(span.data || {}),
822+
['thread.id']: PROFILER_THREAD_ID_STRING,
823+
['thread.name']: PROFILER_THREAD_NAME,
824+
};
825+
});
833826
}

packages/core/src/types-hoist/browseroptions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ export type BrowserClientProfilingOptions = {
2828
/**
2929
* Sets profiling session sample rate for the entire profiling session.
3030
*
31-
* A profiling session corresponds to a user session, so this rate determines what percentage of user sessions will have profiling enabled.
32-
*
31+
* A profiling session corresponds to a user session, meaning it is set once at integration initialization and
32+
* persisted until the next page reload. This rate determines what percentage of user sessions will have profiling enabled.
3333
* @default 0
3434
*/
3535
profileSessionSampleRate?: number;

0 commit comments

Comments
 (0)