@sentry/profiling-node 10.53.1 → 10.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,622 +6,385 @@ const worker_threads = require('worker_threads');
6
6
  const os = require('os');
7
7
  const process$1 = require('process');
8
8
 
9
- /**
10
- * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
11
- *
12
- * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
13
- */
14
9
  const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
15
10
 
16
- const NODE_VERSION = core.parseSemver(process.versions.node) ;
11
+ const NODE_VERSION = core.parseSemver(process.versions.node);
17
12
  const NODE_MAJOR = NODE_VERSION.major;
18
13
 
19
- // We require the file because if we import it, it will be included in the bundle.
20
- // I guess tsc does not check file contents when it's imported.
21
14
  const PROFILER_THREAD_ID_STRING = String(worker_threads.threadId);
22
- const PROFILER_THREAD_NAME = worker_threads.isMainThread ? 'main' : 'worker';
23
- const FORMAT_VERSION = '1';
24
- const CONTINUOUS_FORMAT_VERSION = '2';
25
-
26
- // Machine properties (eval only once)
15
+ const PROFILER_THREAD_NAME = worker_threads.isMainThread ? "main" : "worker";
16
+ const FORMAT_VERSION = "1";
17
+ const CONTINUOUS_FORMAT_VERSION = "2";
27
18
  const PLATFORM = os.platform();
28
19
  const RELEASE = os.release();
29
20
  const VERSION = os.version();
30
21
  const TYPE = os.type();
31
22
  const MODEL = os.machine();
32
23
  const ARCH = os.arch();
33
-
34
- /**
35
- * Checks if the profile is a raw profile or a profile enriched with thread information.
36
- * @param {ThreadCpuProfile | RawThreadCpuProfile} profile
37
- * @returns {boolean}
38
- */
39
- function isRawThreadCpuProfile(
40
- profile,
41
- ) {
42
- return !('thread_metadata' in profile);
24
+ function isRawThreadCpuProfile(profile) {
25
+ return !("thread_metadata" in profile);
43
26
  }
44
-
45
- /**
46
- * Enriches the profile with threadId of the current thread.
47
- * This is done in node as we seem to not be able to get the info from C native code.
48
- *
49
- * @param {ThreadCpuProfile | RawThreadCpuProfile} profile
50
- * @returns {ThreadCpuProfile}
51
- */
52
- function enrichWithThreadInformation(
53
- profile,
54
- ) {
27
+ function enrichWithThreadInformation(profile) {
55
28
  if (!isRawThreadCpuProfile(profile)) {
56
29
  return profile;
57
30
  }
58
-
59
31
  return {
60
32
  samples: profile.samples,
61
33
  frames: profile.frames,
62
34
  stacks: profile.stacks,
63
35
  thread_metadata: {
64
36
  [PROFILER_THREAD_ID_STRING]: {
65
- name: PROFILER_THREAD_NAME,
66
- },
67
- },
68
- } ;
37
+ name: PROFILER_THREAD_NAME
38
+ }
39
+ }
40
+ };
69
41
  }
70
-
71
- /**
72
- * Creates a profiling envelope item, if the profile does not pass validation, returns null.
73
- * @param {RawThreadCpuProfile}
74
- * @param {Event}
75
- * @returns {Profile | null}
76
- */
77
42
  function createProfilingEvent(client, profile, event) {
78
43
  if (!isValidProfile(profile)) {
79
44
  return null;
80
45
  }
81
-
82
46
  return createProfilePayload(client, profile, {
83
- release: event.release ?? '',
84
- environment: event.environment ?? '',
85
- event_id: event.event_id ?? '',
86
- transaction: event.transaction ?? '',
87
- start_timestamp: event.start_timestamp ? event.start_timestamp * 1000 : Date.now(),
88
- trace_id: event.contexts?.trace?.trace_id ?? '',
89
- profile_id: profile.profile_id,
47
+ release: event.release ?? "",
48
+ environment: event.environment ?? "",
49
+ event_id: event.event_id ?? "",
50
+ transaction: event.transaction ?? "",
51
+ start_timestamp: event.start_timestamp ? event.start_timestamp * 1e3 : Date.now(),
52
+ trace_id: event.contexts?.trace?.trace_id ?? "",
53
+ profile_id: profile.profile_id
90
54
  });
91
55
  }
92
-
93
- /**
94
- * Create a profile
95
- * @param {RawThreadCpuProfile} cpuProfile
96
- * @param {options}
97
- * @returns {Profile}
98
- */
99
- function createProfilePayload(
100
- client,
101
- cpuProfile,
102
- {
103
- release,
104
- environment,
105
- event_id,
106
- transaction,
107
- start_timestamp,
108
- trace_id,
109
- profile_id,
110
- }
111
-
112
- ,
113
- ) {
114
- // Log a warning if the profile has an invalid traceId (should be uuidv4).
115
- // All profiles and transactions are rejected if this is the case and we want to
116
- // warn users that this is happening if they enable debug flag
56
+ function createProfilePayload(client, cpuProfile, {
57
+ release,
58
+ environment,
59
+ event_id,
60
+ transaction,
61
+ start_timestamp,
62
+ trace_id,
63
+ profile_id
64
+ }) {
117
65
  if (trace_id?.length !== 32) {
118
66
  DEBUG_BUILD && core.debug.log(`[Profiling] Invalid traceId: ${trace_id} on profiled event`);
119
67
  }
120
-
121
68
  const enrichedThreadProfile = enrichWithThreadInformation(cpuProfile);
122
-
123
69
  const profile = {
124
70
  event_id: profile_id,
125
71
  timestamp: new Date(start_timestamp).toISOString(),
126
- platform: 'node',
72
+ platform: "node",
127
73
  version: FORMAT_VERSION,
128
- release: release,
129
- environment: environment,
74
+ release,
75
+ environment,
130
76
  measurements: cpuProfile.measurements,
131
77
  runtime: {
132
- name: 'node',
133
- version: process$1.versions.node || '',
78
+ name: "node",
79
+ version: process$1.versions.node || ""
134
80
  },
135
81
  os: {
136
82
  name: PLATFORM,
137
83
  version: RELEASE,
138
- build_number: VERSION,
84
+ build_number: VERSION
139
85
  },
140
86
  device: {
141
- locale: process$1.env['LC_ALL'] || process$1.env['LC_MESSAGES'] || process$1.env['LANG'] || process$1.env['LANGUAGE'] || '',
87
+ locale: process$1.env["LC_ALL"] || process$1.env["LC_MESSAGES"] || process$1.env["LANG"] || process$1.env["LANGUAGE"] || "",
142
88
  model: MODEL,
143
89
  manufacturer: TYPE,
144
90
  architecture: ARCH,
145
- is_emulator: false,
91
+ is_emulator: false
146
92
  },
147
93
  debug_meta: {
148
- images: applyDebugMetadata(client, cpuProfile.resources),
94
+ images: applyDebugMetadata(client, cpuProfile.resources)
149
95
  },
150
- profile: enrichedThreadProfile ,
96
+ profile: enrichedThreadProfile,
151
97
  transaction: {
152
98
  name: transaction,
153
99
  id: event_id,
154
- trace_id: trace_id || '',
155
- active_thread_id: PROFILER_THREAD_ID_STRING,
156
- },
100
+ trace_id: trace_id || "",
101
+ active_thread_id: PROFILER_THREAD_ID_STRING
102
+ }
157
103
  };
158
-
159
104
  return profile;
160
105
  }
161
-
162
- /**
163
- * Create a profile chunk from raw thread profile
164
- * @param {RawThreadCpuProfile} cpuProfile
165
- * @param {options}
166
- * @returns {Profile}
167
- */
168
- function createProfileChunkPayload(
169
- client,
170
- cpuProfile,
171
- {
172
- release,
173
- environment,
174
- trace_id,
175
- profiler_id,
176
- chunk_id,
177
- sdk,
178
- }
179
-
180
- ,
181
- ) {
182
- // Log a warning if the profile has an invalid traceId (should be uuidv4).
183
- // All profiles and transactions are rejected if this is the case and we want to
184
- // warn users that this is happening if they enable debug flag
106
+ function createProfileChunkPayload(client, cpuProfile, {
107
+ release,
108
+ environment,
109
+ trace_id,
110
+ profiler_id,
111
+ chunk_id,
112
+ sdk
113
+ }) {
185
114
  if (trace_id?.length !== 32) {
186
115
  DEBUG_BUILD && core.debug.log(`[Profiling] Invalid traceId: ${trace_id} on profiled event`);
187
116
  }
188
-
189
117
  const enrichedThreadProfile = enrichWithThreadInformation(cpuProfile);
190
-
191
118
  const profile = {
192
- chunk_id: chunk_id,
119
+ chunk_id,
193
120
  client_sdk: {
194
- name: sdk?.name ?? 'sentry.javascript.node',
195
- version: sdk?.version ?? '0.0.0',
121
+ name: sdk?.name ?? "sentry.javascript.node",
122
+ version: sdk?.version ?? "0.0.0"
196
123
  },
197
- profiler_id: profiler_id,
198
- platform: 'node',
124
+ profiler_id,
125
+ platform: "node",
199
126
  version: CONTINUOUS_FORMAT_VERSION,
200
- release: release,
201
- environment: environment,
127
+ release,
128
+ environment,
202
129
  measurements: cpuProfile.measurements,
203
130
  debug_meta: {
204
- images: applyDebugMetadata(client, cpuProfile.resources),
131
+ images: applyDebugMetadata(client, cpuProfile.resources)
205
132
  },
206
- profile: enrichedThreadProfile ,
133
+ profile: enrichedThreadProfile
207
134
  };
208
-
209
135
  return profile;
210
136
  }
211
-
212
- /**
213
- * Creates a profiling chunk envelope item, if the profile does not pass validation, returns null.
214
- */
215
- function createProfilingChunkEvent(
216
- client,
217
- options,
218
- profile,
219
- sdk,
220
- identifiers,
221
- ) {
137
+ function createProfilingChunkEvent(client, options, profile, sdk, identifiers) {
222
138
  if (!isValidProfileChunk(profile)) {
223
139
  return null;
224
140
  }
225
-
226
141
  return createProfileChunkPayload(client, profile, {
227
- release: options.release ?? '',
228
- environment: options.environment ?? '',
229
- trace_id: identifiers.trace_id ?? '',
142
+ release: options.release ?? "",
143
+ environment: options.environment ?? "",
144
+ trace_id: identifiers.trace_id ?? "",
230
145
  chunk_id: identifiers.chunk_id,
231
146
  profiler_id: identifiers.profiler_id,
232
- sdk,
147
+ sdk
233
148
  });
234
149
  }
235
-
236
- /**
237
- * Checks the given sample rate to make sure it is valid type and value (a boolean, or a number between 0 and 1).
238
- * @param {unknown} rate
239
- * @returns {boolean}
240
- */
241
150
  function isValidSampleRate(rate) {
242
- // we need to check NaN explicitly because it's of type 'number' and therefore wouldn't get caught by this typecheck
243
- if ((typeof rate !== 'number' && typeof rate !== 'boolean') || (typeof rate === 'number' && isNaN(rate))) {
244
- DEBUG_BUILD &&
245
- core.debug.warn(
246
- `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(
247
- rate,
248
- )} of type ${JSON.stringify(typeof rate)}.`,
249
- );
151
+ if (typeof rate !== "number" && typeof rate !== "boolean" || typeof rate === "number" && isNaN(rate)) {
152
+ DEBUG_BUILD && core.debug.warn(
153
+ `[Profiling] Invalid sample rate. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(
154
+ rate
155
+ )} of type ${JSON.stringify(typeof rate)}.`
156
+ );
250
157
  return false;
251
158
  }
252
-
253
- // Boolean sample rates are always valid
254
159
  if (rate === true || rate === false) {
255
160
  return true;
256
161
  }
257
-
258
- // in case sampleRate is a boolean, it will get automatically cast to 1 if it's true and 0 if it's false
259
162
  if (rate < 0 || rate > 1) {
260
163
  DEBUG_BUILD && core.debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);
261
164
  return false;
262
165
  }
263
166
  return true;
264
167
  }
265
-
266
- /**
267
- * Checks if the profile is valid and can be sent to Sentry.
268
- * @param {RawThreadCpuProfile} profile
269
- * @returns {boolean}
270
- */
271
168
  function isValidProfile(profile) {
272
169
  if (profile.samples.length <= 1) {
273
- DEBUG_BUILD &&
274
- // Log a warning if the profile has less than 2 samples so users can know why
275
- // they are not seeing any profiling data and we cant avoid the back and forth
276
- // of asking them to provide us with a dump of the profile data.
277
- core.debug.log('[Profiling] Discarding profile because it contains less than 2 samples');
170
+ DEBUG_BUILD && // Log a warning if the profile has less than 2 samples so users can know why
171
+ // they are not seeing any profiling data and we cant avoid the back and forth
172
+ // of asking them to provide us with a dump of the profile data.
173
+ core.debug.log("[Profiling] Discarding profile because it contains less than 2 samples");
278
174
  return false;
279
175
  }
280
-
281
176
  if (!profile.profile_id) {
282
177
  return false;
283
178
  }
284
-
285
179
  return true;
286
180
  }
287
-
288
- /**
289
- * Checks if the profile chunk is valid and can be sent to Sentry.
290
- * @param profile
291
- * @returns
292
- */
293
181
  function isValidProfileChunk(profile) {
294
182
  if (profile.samples.length <= 1) {
295
- DEBUG_BUILD &&
296
- // Log a warning if the profile has less than 2 samples so users can know why
297
- // they are not seeing any profiling data and we cant avoid the back and forth
298
- // of asking them to provide us with a dump of the profile data.
299
- core.debug.log('[Profiling] Discarding profile chunk because it contains less than 2 samples');
183
+ DEBUG_BUILD && // Log a warning if the profile has less than 2 samples so users can know why
184
+ // they are not seeing any profiling data and we cant avoid the back and forth
185
+ // of asking them to provide us with a dump of the profile data.
186
+ core.debug.log("[Profiling] Discarding profile chunk because it contains less than 2 samples");
300
187
  return false;
301
188
  }
302
-
303
189
  return true;
304
190
  }
305
-
306
- /**
307
- * Adds items to envelope if they are not already present - mutates the envelope.
308
- * @param {Envelope} envelope
309
- * @param {Profile[]} profiles
310
- * @returns {Envelope}
311
- */
312
191
  function addProfilesToEnvelope(envelope, profiles) {
313
192
  if (!profiles.length) {
314
193
  return envelope;
315
194
  }
316
-
317
195
  for (const profile of profiles) {
318
- // @ts-expect-error untyped envelope
319
- envelope[1].push([{ type: 'profile' }, profile]);
196
+ envelope[1].push([{ type: "profile" }, profile]);
320
197
  }
321
198
  return envelope;
322
199
  }
323
-
324
- /**
325
- * Finds transactions with profile_id context in the envelope
326
- * @param {Envelope} envelope
327
- * @returns {Event[]}
328
- */
329
200
  function findProfiledTransactionsFromEnvelope(envelope) {
330
201
  const events = [];
331
-
332
202
  core.forEachEnvelopeItem(envelope, (item, type) => {
333
- if (type !== 'transaction') {
203
+ if (type !== "transaction") {
334
204
  return;
335
205
  }
336
-
337
- // First item is the type, so we can skip it, everything else is an event
338
206
  for (let j = 1; j < item.length; j++) {
339
- const event = item[j] ;
340
-
207
+ const event = item[j];
341
208
  if (!event) {
342
- // Shouldn't happen, but lets be safe
343
209
  continue;
344
210
  }
345
-
346
211
  const profile_id = event.contexts?.profile?.profile_id;
347
-
348
212
  if (event && profile_id) {
349
213
  events.push(event);
350
214
  }
351
215
  }
352
216
  });
353
-
354
217
  return events;
355
218
  }
356
-
357
- /**
358
- * Creates event envelope headers for a profile chunk. This is separate from createEventEnvelopeHeaders util
359
- * as the profile chunk does not conform to the sentry event type
360
- */
361
- function createEventEnvelopeHeaders(
362
- sdkInfo,
363
- tunnel,
364
- dsn,
365
- ) {
219
+ function createEventEnvelopeHeaders(sdkInfo, tunnel, dsn) {
366
220
  return {
367
221
  event_id: core.uuid4(),
368
- sent_at: new Date().toISOString(),
369
- ...(sdkInfo && { sdk: sdkInfo }),
370
- ...(!!tunnel && dsn && { dsn: core.dsnToString(dsn) }),
222
+ sent_at: (/* @__PURE__ */ new Date()).toISOString(),
223
+ ...sdkInfo && { sdk: sdkInfo },
224
+ ...!!tunnel && dsn && { dsn: core.dsnToString(dsn) }
371
225
  };
372
226
  }
373
-
374
- /**
375
- * Creates a standalone profile_chunk envelope.
376
- */
377
- function makeProfileChunkEnvelope(
378
- platform,
379
- chunk,
380
- sdkInfo,
381
- tunnel,
382
- dsn,
383
- ) {
227
+ function makeProfileChunkEnvelope(platform, chunk, sdkInfo, tunnel, dsn) {
384
228
  const profileChunkHeader = {
385
- type: 'profile_chunk',
386
- platform,
229
+ type: "profile_chunk",
230
+ platform
387
231
  };
388
-
389
232
  return core.createEnvelope(createEventEnvelopeHeaders(sdkInfo, tunnel, dsn), [
390
- [profileChunkHeader, chunk],
233
+ [profileChunkHeader, chunk]
391
234
  ]);
392
235
  }
393
-
394
- /**
395
- * Cross reference profile collected resources with debug_ids and return a list of debug images.
396
- * @param {string[]} resource_paths
397
- * @returns {DebugImage[]}
398
- */
399
236
  function applyDebugMetadata(client, resource_paths) {
400
237
  const options = client.getOptions();
401
-
402
238
  if (!options?.stackParser) {
403
239
  return [];
404
240
  }
405
-
406
241
  return core.getDebugImagesForResources(options.stackParser, resource_paths);
407
242
  }
408
243
 
409
- const MAX_PROFILE_DURATION_MS = 30 * 1000;
410
-
411
- /**
412
- * Takes a transaction and determines if it should be profiled or not. If it should be profiled, it returns the
413
- * profile_id, otherwise returns undefined. Takes care of setting profile context on transaction as well
414
- */
415
- function maybeProfileSpan(
416
- client,
417
- span,
418
- customSamplingContext,
419
- ) {
420
- // Profiling is not supported in worker threads as the native CPU profiler's
421
- // sampling thread can race with V8's GC across isolates, causing heap corruption.
244
+ const MAX_PROFILE_DURATION_MS = 30 * 1e3;
245
+ function maybeProfileSpan(client, span, customSamplingContext) {
422
246
  if (!worker_threads.isMainThread) {
423
- DEBUG_BUILD && core.debug.log('[Profiling] Skipping span profiling in worker thread.');
247
+ DEBUG_BUILD && core.debug.log("[Profiling] Skipping span profiling in worker thread.");
424
248
  return;
425
249
  }
426
-
427
- // profilesSampleRate is multiplied with tracesSampleRate to get the final sampling rate. We dont perform
428
- // the actual multiplication to get the final rate, but we discard the profile if the span was sampled,
429
- // so anything after this block from here is based on the span sampling.
430
250
  if (!core.spanIsSampled(span)) {
431
251
  return;
432
252
  }
433
-
434
- // Client and options are required for profiling
435
253
  if (!client) {
436
- DEBUG_BUILD && core.debug.log('[Profiling] Profiling disabled, no client found.');
254
+ DEBUG_BUILD && core.debug.log("[Profiling] Profiling disabled, no client found.");
437
255
  return;
438
256
  }
439
-
440
257
  const options = client.getOptions();
441
258
  if (!options) {
442
- DEBUG_BUILD && core.debug.log('[Profiling] Profiling disabled, no options found.');
259
+ DEBUG_BUILD && core.debug.log("[Profiling] Profiling disabled, no options found.");
443
260
  return;
444
261
  }
445
-
446
262
  const profilesSampler = options.profilesSampler;
447
263
  let profilesSampleRate = options.profilesSampleRate;
448
-
449
- // Prefer sampler to sample rate if both are provided.
450
- if (typeof profilesSampler === 'function') {
451
- const { description: spanName = '<unknown>', data } = core.spanToJSON(span);
452
- // We bail out early if that is not the case
264
+ if (typeof profilesSampler === "function") {
265
+ const { description: spanName = "<unknown>", data } = core.spanToJSON(span);
453
266
  const parentSampled = true;
454
-
455
267
  profilesSampleRate = profilesSampler({
456
268
  name: spanName,
457
269
  attributes: data,
458
270
  parentSampled,
459
- ...customSamplingContext,
271
+ ...customSamplingContext
460
272
  });
461
273
  }
462
-
463
- // Since this is coming from the user (or from a function provided by the user), who knows what we might get. (The
464
- // only valid values are booleans or numbers between 0 and 1.)
465
274
  if (!isValidSampleRate(profilesSampleRate)) {
466
- DEBUG_BUILD && core.debug.warn('[Profiling] Discarding profile because of invalid sample rate.');
275
+ DEBUG_BUILD && core.debug.warn("[Profiling] Discarding profile because of invalid sample rate.");
467
276
  return;
468
277
  }
469
-
470
- // if the function returned 0 (or false), or if `profileSampleRate` is 0, it's a sign the profile should be dropped
471
278
  if (!profilesSampleRate) {
472
- DEBUG_BUILD &&
473
- core.debug.log(
474
- `[Profiling] Discarding profile because ${
475
- typeof profilesSampler === 'function'
476
- ? 'profileSampler returned 0 or false'
477
- : 'a negative sampling decision was inherited or profileSampleRate is set to 0'
478
- }`,
479
- );
279
+ DEBUG_BUILD && core.debug.log(
280
+ `[Profiling] Discarding profile because ${typeof profilesSampler === "function" ? "profileSampler returned 0 or false" : "a negative sampling decision was inherited or profileSampleRate is set to 0"}`
281
+ );
480
282
  return;
481
283
  }
482
-
483
- // Now we roll the dice. Math.random is inclusive of 0, but not of 1, so strict < is safe here. In case sampleRate is
484
- // a boolean, the < comparison will cause it to be automatically cast to 1 if it's true and 0 if it's false.
485
284
  const sampled = profilesSampleRate === true ? true : Math.random() < profilesSampleRate;
486
- // Check if we should sample this profile
487
285
  if (!sampled) {
488
- DEBUG_BUILD &&
489
- core.debug.log(
490
- `[Profiling] Discarding profile because it's not included in the random sample (sampling rate = ${Number(
491
- profilesSampleRate,
492
- )})`,
493
- );
286
+ DEBUG_BUILD && core.debug.log(
287
+ `[Profiling] Discarding profile because it's not included in the random sample (sampling rate = ${Number(
288
+ profilesSampleRate
289
+ )})`
290
+ );
494
291
  return;
495
292
  }
496
-
497
293
  const profile_id = core.uuid4();
498
294
  nodeCpuProfiler.CpuProfilerBindings.startProfiling(profile_id);
499
295
  DEBUG_BUILD && core.debug.log(`[Profiling] started profiling transaction: ${core.spanToJSON(span).description}`);
500
-
501
- // set transaction context - do this regardless if profiling fails down the line
502
- // so that we can still see the profile_id in the transaction context
503
296
  return profile_id;
504
297
  }
505
-
506
- /**
507
- * Stops the profiler for profile_id and returns the profile
508
- * @param transaction
509
- * @param profile_id
510
- * @returns
511
- */
512
298
  function stopSpanProfile(span, profile_id) {
513
- // Should not happen, but satisfy the type checker and be safe regardless.
514
299
  if (!profile_id) {
515
300
  return null;
516
301
  }
517
-
518
302
  const profile = nodeCpuProfiler.CpuProfilerBindings.stopProfiling(profile_id, 0);
519
303
  DEBUG_BUILD && core.debug.log(`[Profiling] stopped profiling of transaction: ${core.spanToJSON(span).description}`);
520
-
521
- // In case of an overlapping span, stopProfiling may return null and silently ignore the overlapping profile.
522
304
  if (!profile) {
523
- DEBUG_BUILD &&
524
- core.debug.log(
525
- `[Profiling] profiler returned null profile for: ${core.spanToJSON(span).description}`,
526
- 'this may indicate an overlapping span or a call to stopProfiling with a profile title that was never started',
527
- );
305
+ DEBUG_BUILD && core.debug.log(
306
+ `[Profiling] profiler returned null profile for: ${core.spanToJSON(span).description}`,
307
+ "this may indicate an overlapping span or a call to stopProfiling with a profile title that was never started"
308
+ );
528
309
  return null;
529
310
  }
530
-
531
- // Assign profile_id to the profile
532
311
  profile.profile_id = profile_id;
533
312
  return profile;
534
313
  }
535
314
 
536
- const CHUNK_INTERVAL_MS = 1000 * 60;
315
+ const CHUNK_INTERVAL_MS = 1e3 * 60;
537
316
  const PROFILE_MAP = new core.LRUMap(50);
538
317
  const PROFILE_TIMEOUTS = {};
539
-
540
318
  function addToProfileQueue(profile_id, profile) {
541
319
  PROFILE_MAP.set(profile_id, profile);
542
320
  }
543
-
544
321
  function takeFromProfileQueue(profile_id) {
545
322
  const profile = PROFILE_MAP.get(profile_id);
546
323
  PROFILE_MAP.remove(profile_id);
547
324
  return profile;
548
325
  }
549
-
550
- class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.call(this);ContinuousProfiler.prototype.__init2.call(this);ContinuousProfiler.prototype.__init3.call(this);ContinuousProfiler.prototype.__init4.call(this);ContinuousProfiler.prototype.__init5.call(this);ContinuousProfiler.prototype.__init6.call(this);ContinuousProfiler.prototype.__init7.call(this); }
551
-
552
- __init() {this._client = undefined;}
553
- __init2() {this._chunkData = undefined;}
554
- __init3() {this._mode = undefined;}
555
- __init4() {this._legacyProfilerMode = undefined;}
556
- __init5() {this._profileLifecycle = undefined;}
557
- __init6() {this._sampled = undefined;}
558
- __init7() {this._sessionSamplingRate = undefined;}
326
+ class ContinuousProfiler {
327
+ constructor() {
328
+ this._client = void 0;
329
+ this._chunkData = void 0;
330
+ this._mode = void 0;
331
+ this._legacyProfilerMode = void 0;
332
+ this._profileLifecycle = void 0;
333
+ this._sampled = void 0;
334
+ this._sessionSamplingRate = void 0;
335
+ }
559
336
  /**
560
337
  * Called when the profiler is attached to the client (continuous mode is enabled). If of the profiler
561
338
  * methods called before the profiler is initialized will result in a noop action with debug logs.
562
339
  * @param client
563
340
  */
564
- initialize(client) {
341
+ initialize(client) {
565
342
  if (!worker_threads.isMainThread) {
566
- DEBUG_BUILD &&
567
- core.debug.warn(
568
- '[Profiling] nodeProfilingIntegration() does not support worker threads — profiling will be disabled for this thread.',
569
- );
343
+ DEBUG_BUILD && core.debug.warn(
344
+ "[Profiling] nodeProfilingIntegration() does not support worker threads \u2014 profiling will be disabled for this thread."
345
+ );
570
346
  return;
571
347
  }
572
-
573
348
  this._client = client;
574
349
  const options = client.getOptions();
575
-
576
350
  this._mode = getProfilingMode(options);
577
351
  this._sessionSamplingRate = Math.random();
578
352
  this._sampled = this._sessionSamplingRate < (options.profileSessionSampleRate ?? 0);
579
- this._profileLifecycle = options.profileLifecycle ?? 'manual';
580
-
353
+ this._profileLifecycle = options.profileLifecycle ?? "manual";
581
354
  switch (this._mode) {
582
- case 'legacy': {
583
- this._legacyProfilerMode =
584
- 'profilesSampleRate' in options || 'profilesSampler' in options ? 'span' : 'continuous';
585
-
355
+ case "legacy": {
356
+ this._legacyProfilerMode = "profilesSampleRate" in options || "profilesSampler" in options ? "span" : "continuous";
586
357
  DEBUG_BUILD && core.debug.log(`[Profiling] Profiling mode is ${this._legacyProfilerMode}.`);
587
-
588
358
  switch (this._legacyProfilerMode) {
589
- case 'span': {
359
+ case "span": {
590
360
  this._setupAutomaticSpanProfiling();
591
361
  break;
592
362
  }
593
- case 'continuous': {
594
- // Continous mode requires manual calls to profiler.start() and profiler.stop()
363
+ case "continuous": {
595
364
  break;
596
365
  }
597
366
  default: {
598
- DEBUG_BUILD &&
599
- core.debug.warn(
600
- `[Profiling] Unknown profiler mode: ${this._legacyProfilerMode}, profiler was not initialized`,
601
- );
367
+ DEBUG_BUILD && core.debug.warn(
368
+ `[Profiling] Unknown profiler mode: ${this._legacyProfilerMode}, profiler was not initialized`
369
+ );
602
370
  break;
603
371
  }
604
372
  }
605
373
  break;
606
374
  }
607
-
608
- case 'current': {
375
+ case "current": {
609
376
  this._setupSpanChunkInstrumentation();
610
-
611
377
  DEBUG_BUILD && core.debug.log(`[Profiling] Profiling mode is ${this._profileLifecycle}.`);
612
-
613
378
  switch (this._profileLifecycle) {
614
- case 'trace': {
379
+ case "trace": {
615
380
  this._startTraceLifecycleProfiling();
616
381
  break;
617
382
  }
618
- case 'manual': {
619
- // Manual mode requires manual calls to profiler.startProfiler() and profiler.stopProfiler()
383
+ case "manual": {
620
384
  break;
621
385
  }
622
386
  default: {
623
- DEBUG_BUILD &&
624
- core.debug.warn(`[Profiling] Unknown profiler mode: ${this._profileLifecycle}, profiler was not initialized`);
387
+ DEBUG_BUILD && core.debug.warn(`[Profiling] Unknown profiler mode: ${this._profileLifecycle}, profiler was not initialized`);
625
388
  break;
626
389
  }
627
390
  }
@@ -632,350 +395,258 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
632
395
  break;
633
396
  }
634
397
  }
635
-
636
- // Attaches a listener to beforeSend which will add the threadId data to the event being sent.
637
- // This adds a constant overhead to all events being sent which could be improved to only attach
638
- // and detach the listener during a profiler session
639
- this._client.on('beforeSendEvent', this._onBeforeSendThreadContextAssignment.bind(this));
398
+ this._client.on("beforeSendEvent", this._onBeforeSendThreadContextAssignment.bind(this));
640
399
  }
641
-
642
400
  /**
643
401
  * Initializes a new profilerId session and schedules chunk profiling.
644
402
  * @returns void
645
403
  */
646
- start() {
647
- if (this._mode === 'current') {
404
+ start() {
405
+ if (this._mode === "current") {
648
406
  this._startProfiler();
649
407
  return;
650
408
  }
651
-
652
409
  if (!this._client) {
653
- DEBUG_BUILD && core.debug.log('[Profiling] Failed to start, sentry client was never attached to the profiler.');
410
+ DEBUG_BUILD && core.debug.log("[Profiling] Failed to start, sentry client was never attached to the profiler.");
654
411
  return;
655
412
  }
656
-
657
- if (this._mode !== 'legacy') {
658
- DEBUG_BUILD && core.debug.log('[Profiling] Continuous profiling is not supported in the current mode.');
413
+ if (this._mode !== "legacy") {
414
+ DEBUG_BUILD && core.debug.log("[Profiling] Continuous profiling is not supported in the current mode.");
659
415
  return;
660
416
  }
661
-
662
- if (this._legacyProfilerMode === 'span') {
663
- DEBUG_BUILD && core.debug.log('[Profiling] Calls to profiler.start() are not supported in span profiling mode.');
417
+ if (this._legacyProfilerMode === "span") {
418
+ DEBUG_BUILD && core.debug.log("[Profiling] Calls to profiler.start() are not supported in span profiling mode.");
664
419
  return;
665
420
  }
666
-
667
- // Flush any existing chunks before starting a new one.
668
-
669
421
  this._stopChunkProfiling();
670
-
671
- // Restart the profiler session
672
422
  this._setupSpanChunkInstrumentation();
673
423
  this._restartChunkProfiling();
674
424
  }
675
-
676
425
  /**
677
426
  * Stops the current chunk and flushes the profile to Sentry.
678
427
  * @returns void
679
428
  */
680
- stop() {
681
- if (this._mode === 'current') {
429
+ stop() {
430
+ if (this._mode === "current") {
682
431
  this._stopProfiler();
683
432
  return;
684
433
  }
685
-
686
434
  if (!this._client) {
687
- DEBUG_BUILD && core.debug.log('[Profiling] Failed to stop, sentry client was never attached to the profiler.');
435
+ DEBUG_BUILD && core.debug.log("[Profiling] Failed to stop, sentry client was never attached to the profiler.");
688
436
  return;
689
437
  }
690
-
691
- if (this._mode !== 'legacy') {
692
- DEBUG_BUILD && core.debug.log('[Profiling] Continuous profiling is not supported in the current mode.');
438
+ if (this._mode !== "legacy") {
439
+ DEBUG_BUILD && core.debug.log("[Profiling] Continuous profiling is not supported in the current mode.");
693
440
  return;
694
441
  }
695
-
696
- if (this._legacyProfilerMode === 'span') {
697
- DEBUG_BUILD && core.debug.log('[Profiling] Calls to profiler.stop() are not supported in span profiling mode.');
442
+ if (this._legacyProfilerMode === "span") {
443
+ DEBUG_BUILD && core.debug.log("[Profiling] Calls to profiler.stop() are not supported in span profiling mode.");
698
444
  return;
699
445
  }
700
-
701
446
  this._stopChunkProfiling();
702
447
  this._teardownSpanChunkInstrumentation();
703
448
  }
704
-
705
- _startProfiler() {
706
- if (this._mode !== 'current') {
707
- DEBUG_BUILD && core.debug.log('[Profiling] Continuous profiling is not supported in the current mode.');
449
+ _startProfiler() {
450
+ if (this._mode !== "current") {
451
+ DEBUG_BUILD && core.debug.log("[Profiling] Continuous profiling is not supported in the current mode.");
708
452
  return;
709
453
  }
710
-
711
- if (this._chunkData !== undefined) {
712
- DEBUG_BUILD && core.debug.log('[Profiling] Profile session already running, no-op.');
454
+ if (this._chunkData !== void 0) {
455
+ DEBUG_BUILD && core.debug.log("[Profiling] Profile session already running, no-op.");
713
456
  return;
714
457
  }
715
-
716
- if (this._mode === 'current') {
458
+ if (this._mode === "current") {
717
459
  if (!this._sampled) {
718
- DEBUG_BUILD && core.debug.log('[Profiling] Profile session not sampled, no-op.');
460
+ DEBUG_BUILD && core.debug.log("[Profiling] Profile session not sampled, no-op.");
719
461
  return;
720
462
  }
721
463
  }
722
-
723
- if (this._profileLifecycle === 'trace') {
724
- DEBUG_BUILD &&
725
- core.debug.log(
726
- '[Profiling] You are using the trace profile lifecycle, manual calls to profiler.startProfiler() and profiler.stopProfiler() will be ignored.',
727
- );
464
+ if (this._profileLifecycle === "trace") {
465
+ DEBUG_BUILD && core.debug.log(
466
+ "[Profiling] You are using the trace profile lifecycle, manual calls to profiler.startProfiler() and profiler.stopProfiler() will be ignored."
467
+ );
728
468
  return;
729
469
  }
730
-
731
470
  this._startChunkProfiling();
732
471
  }
733
-
734
- _stopProfiler() {
735
- if (this._mode !== 'current') {
736
- DEBUG_BUILD && core.debug.log('[Profiling] Continuous profiling is not supported in the current mode.');
472
+ _stopProfiler() {
473
+ if (this._mode !== "current") {
474
+ DEBUG_BUILD && core.debug.log("[Profiling] Continuous profiling is not supported in the current mode.");
737
475
  return;
738
476
  }
739
-
740
- if (this._profileLifecycle === 'trace') {
741
- DEBUG_BUILD &&
742
- core.debug.log(
743
- '[Profiling] You are using the trace profile lifecycle, manual calls to profiler.startProfiler() and profiler.stopProfiler() will be ignored.',
744
- );
477
+ if (this._profileLifecycle === "trace") {
478
+ DEBUG_BUILD && core.debug.log(
479
+ "[Profiling] You are using the trace profile lifecycle, manual calls to profiler.startProfiler() and profiler.stopProfiler() will be ignored."
480
+ );
745
481
  return;
746
482
  }
747
-
748
483
  if (!this._chunkData) {
749
- DEBUG_BUILD && core.debug.log('[Profiling] No profile session running, no-op.');
484
+ DEBUG_BUILD && core.debug.log("[Profiling] No profile session running, no-op.");
750
485
  return;
751
486
  }
752
-
753
487
  this._stopChunkProfiling();
754
488
  }
755
-
756
489
  /**
757
490
  * Starts trace lifecycle profiling. Profiling will remain active as long as there is an active span.
758
491
  */
759
- _startTraceLifecycleProfiling() {
492
+ _startTraceLifecycleProfiling() {
760
493
  if (!this._client) {
761
- DEBUG_BUILD &&
762
- core.debug.log(
763
- '[Profiling] Failed to start trace lifecycle profiling, sentry client was never attached to the profiler.',
764
- );
494
+ DEBUG_BUILD && core.debug.log(
495
+ "[Profiling] Failed to start trace lifecycle profiling, sentry client was never attached to the profiler."
496
+ );
765
497
  return;
766
498
  }
767
-
768
499
  let activeSpanCounter = 0;
769
- this._client.on('spanStart', _span => {
500
+ this._client.on("spanStart", (_span) => {
770
501
  if (activeSpanCounter === 0) {
771
502
  this._startChunkProfiling();
772
503
  }
773
504
  activeSpanCounter++;
774
505
  });
775
-
776
- this._client.on('spanEnd', _span => {
506
+ this._client.on("spanEnd", (_span) => {
777
507
  if (activeSpanCounter === 1) {
778
508
  this._stopChunkProfiling();
779
509
  }
780
510
  activeSpanCounter--;
781
511
  });
782
512
  }
783
-
784
- _setupAutomaticSpanProfiling() {
513
+ _setupAutomaticSpanProfiling() {
785
514
  if (!this._client) {
786
- DEBUG_BUILD &&
787
- core.debug.log(
788
- '[Profiling] Failed to setup automatic span profiling, sentry client was never attached to the profiler.',
789
- );
515
+ DEBUG_BUILD && core.debug.log(
516
+ "[Profiling] Failed to setup automatic span profiling, sentry client was never attached to the profiler."
517
+ );
790
518
  return;
791
519
  }
792
-
793
- const spanToProfileIdMap = new WeakMap();
794
-
795
- this._client.on('spanStart', span => {
520
+ const spanToProfileIdMap = /* @__PURE__ */ new WeakMap();
521
+ this._client.on("spanStart", (span) => {
796
522
  if (span !== core.getRootSpan(span)) {
797
523
  return;
798
524
  }
799
-
800
525
  const profile_id = maybeProfileSpan(this._client, span);
801
-
802
526
  if (profile_id) {
803
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
804
527
  const options = this._client.getOptions();
805
- // Not intended for external use, hence missing types, but we want to profile a couple of things at Sentry that
806
- // currently exceed the default timeout set by the SDKs.
807
528
  const maxProfileDurationMs = options._experiments?.maxProfileDurationMs || MAX_PROFILE_DURATION_MS;
808
-
809
529
  if (PROFILE_TIMEOUTS[profile_id]) {
810
530
  global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
811
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
812
531
  delete PROFILE_TIMEOUTS[profile_id];
813
532
  }
814
-
815
- // Enqueue a timeout to prevent profiles from running over max duration.
816
533
  const timeout = global.setTimeout(() => {
817
- DEBUG_BUILD &&
818
- core.debug.log(
819
- '[Profiling] max profile duration elapsed, stopping profiling for:',
820
- core.spanToJSON(span).description,
821
- );
822
-
534
+ DEBUG_BUILD && core.debug.log(
535
+ "[Profiling] max profile duration elapsed, stopping profiling for:",
536
+ core.spanToJSON(span).description
537
+ );
823
538
  const profile = stopSpanProfile(span, profile_id);
824
539
  if (profile) {
825
540
  addToProfileQueue(profile_id, profile);
826
541
  }
827
542
  }, maxProfileDurationMs);
828
-
829
- // Unref timeout so it doesn't keep the process alive.
830
543
  timeout.unref();
831
-
832
- core.getIsolationScope().setContext('profile', { profile_id });
544
+ core.getIsolationScope().setContext("profile", { profile_id });
833
545
  spanToProfileIdMap.set(span, profile_id);
834
546
  }
835
547
  });
836
-
837
- this._client.on('spanEnd', span => {
548
+ this._client.on("spanEnd", (span) => {
838
549
  const profile_id = spanToProfileIdMap.get(span);
839
-
840
550
  if (profile_id) {
841
551
  if (PROFILE_TIMEOUTS[profile_id]) {
842
552
  global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
843
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
844
553
  delete PROFILE_TIMEOUTS[profile_id];
845
554
  }
846
555
  const profile = stopSpanProfile(span, profile_id);
847
-
848
556
  if (profile) {
849
557
  addToProfileQueue(profile_id, profile);
850
558
  }
851
559
  }
852
560
  });
853
-
854
- this._client.on('beforeEnvelope', (envelope) => {
855
- // if not profiles are in queue, there is nothing to add to the envelope.
561
+ this._client.on("beforeEnvelope", (envelope) => {
856
562
  if (!PROFILE_MAP.size) {
857
563
  return;
858
564
  }
859
-
860
565
  const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);
861
566
  if (!profiledTransactionEvents.length) {
862
567
  return;
863
568
  }
864
-
865
569
  const profilesToAddToEnvelope = [];
866
-
867
570
  for (const profiledTransaction of profiledTransactionEvents) {
868
571
  const profileContext = profiledTransaction.contexts?.profile;
869
572
  const profile_id = profileContext?.profile_id;
870
-
871
573
  if (!profile_id) {
872
- throw new TypeError('[Profiling] cannot find profile for a transaction without a profile context');
574
+ throw new TypeError("[Profiling] cannot find profile for a transaction without a profile context");
873
575
  }
874
-
875
- // Remove the profile from the transaction context before sending, relay will take care of the rest.
876
576
  if (profileContext) {
877
577
  delete profiledTransaction.contexts?.profile;
878
578
  }
879
-
880
579
  const cpuProfile = takeFromProfileQueue(profile_id);
881
580
  if (!cpuProfile) {
882
581
  DEBUG_BUILD && core.debug.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
883
582
  continue;
884
583
  }
885
-
886
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
887
584
  const profile = createProfilingEvent(this._client, cpuProfile, profiledTransaction);
888
585
  if (!profile) return;
889
-
890
586
  profilesToAddToEnvelope.push(profile);
891
-
892
- // @ts-expect-error profile does not inherit from Event
893
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
894
- this._client.emit('preprocessEvent', profile, {
895
- event_id: profiledTransaction.event_id,
587
+ this._client.emit("preprocessEvent", profile, {
588
+ event_id: profiledTransaction.event_id
896
589
  });
897
-
898
- // @ts-expect-error profile does not inherit from Event
899
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
900
- this._client.emit('postprocessEvent', profile, {
901
- event_id: profiledTransaction.event_id,
590
+ this._client.emit("postprocessEvent", profile, {
591
+ event_id: profiledTransaction.event_id
902
592
  });
903
593
  }
904
-
905
594
  addProfilesToEnvelope(envelope, profilesToAddToEnvelope);
906
595
  });
907
596
  }
908
-
909
597
  /**
910
598
  * Stop profiler and initializes profiling of the next chunk
911
599
  */
912
- _restartChunkProfiling() {
600
+ _restartChunkProfiling() {
913
601
  if (!this._client) {
914
- // The client is not attached to the profiler if the user has not enabled continuous profiling.
915
- // In this case, calling start() and stop() is a noop action.The reason this exists is because
916
- // it makes the types easier to work with and avoids users having to do null checks.
917
- DEBUG_BUILD && core.debug.log('[Profiling] Profiler was never attached to the client.');
602
+ DEBUG_BUILD && core.debug.log("[Profiling] Profiler was never attached to the client.");
918
603
  return;
919
604
  }
920
-
921
605
  if (this._chunkData) {
922
- DEBUG_BUILD &&
923
- core.debug.log(
924
- `[Profiling] Chunk with chunk_id ${this._chunkData.id} is still running, current chunk will be stopped a new chunk will be started.`,
925
- );
606
+ DEBUG_BUILD && core.debug.log(
607
+ `[Profiling] Chunk with chunk_id ${this._chunkData.id} is still running, current chunk will be stopped a new chunk will be started.`
608
+ );
926
609
  this._stopChunkProfiling();
927
610
  }
928
-
929
611
  this._startChunkProfiling();
930
612
  }
931
-
932
613
  /**
933
614
  * Stops profiling of the current chunks and flushes the profile to Sentry
934
615
  */
935
- _stopChunkProfiling() {
616
+ _stopChunkProfiling() {
936
617
  if (!this._chunkData) {
937
- DEBUG_BUILD && core.debug.log('[Profiling] No chunk data found, no-op.');
618
+ DEBUG_BUILD && core.debug.log("[Profiling] No chunk data found, no-op.");
938
619
  return;
939
620
  }
940
-
941
621
  if (this._chunkData?.timer) {
942
622
  global.clearTimeout(this._chunkData.timer);
943
- this._chunkData.timer = undefined;
623
+ this._chunkData.timer = void 0;
944
624
  DEBUG_BUILD && core.debug.log(`[Profiling] Stopping profiling chunk: ${this._chunkData.id}`);
945
625
  }
946
-
947
626
  if (!this._client) {
948
- DEBUG_BUILD &&
949
- core.debug.log('[Profiling] Failed to collect profile, sentry client was never attached to the profiler.');
627
+ DEBUG_BUILD && core.debug.log("[Profiling] Failed to collect profile, sentry client was never attached to the profiler.");
950
628
  this._resetChunkData();
951
629
  return;
952
630
  }
953
-
954
631
  if (!this._chunkData?.id) {
955
- DEBUG_BUILD &&
956
- core.debug.log(`[Profiling] Failed to collect profile for: ${this._chunkData?.id}, the chunk_id is missing.`);
632
+ DEBUG_BUILD && core.debug.log(`[Profiling] Failed to collect profile for: ${this._chunkData?.id}, the chunk_id is missing.`);
957
633
  this._resetChunkData();
958
634
  return;
959
635
  }
960
-
961
636
  const profile = nodeCpuProfiler.CpuProfilerBindings.stopProfiling(this._chunkData.id, nodeCpuProfiler.ProfileFormat.CHUNK);
962
-
963
637
  if (!profile) {
964
638
  DEBUG_BUILD && core.debug.log(`[Profiling] Failed to collect profile for: ${this._chunkData.id}`);
965
639
  this._resetChunkData();
966
640
  return;
967
641
  }
968
-
969
642
  if (!this._profilerId) {
970
- DEBUG_BUILD &&
971
- core.debug.log('[Profiling] Profile chunk does not contain a valid profiler_id, this is a bug in the SDK');
643
+ DEBUG_BUILD && core.debug.log("[Profiling] Profile chunk does not contain a valid profiler_id, this is a bug in the SDK");
972
644
  this._resetChunkData();
973
645
  return;
974
646
  }
975
647
  if (profile) {
976
648
  DEBUG_BUILD && core.debug.log(`[Profiling] Sending profile chunk ${this._chunkData.id}.`);
977
649
  }
978
-
979
650
  DEBUG_BUILD && core.debug.log(`[Profiling] Profile chunk ${this._chunkData.id} sent to Sentry.`);
980
651
  const chunk = createProfilingChunkEvent(
981
652
  this._client,
@@ -985,207 +656,156 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
985
656
  {
986
657
  chunk_id: this._chunkData.id,
987
658
  trace_id: this._chunkData.startTraceID,
988
- profiler_id: this._profilerId,
989
- },
659
+ profiler_id: this._profilerId
660
+ }
990
661
  );
991
-
992
662
  if (!chunk) {
993
663
  DEBUG_BUILD && core.debug.log(`[Profiling] Failed to create profile chunk for: ${this._chunkData.id}`);
994
664
  this._resetChunkData();
995
665
  return;
996
666
  }
997
-
998
667
  this._flush(chunk);
999
- // Depending on the profile and stack sizes, stopping the profile and converting
1000
- // the format may negatively impact the performance of the application. To avoid
1001
- // blocking for too long, enqueue the next chunk start inside the next macrotask.
1002
- // clear current chunk
1003
668
  this._resetChunkData();
1004
669
  }
1005
-
1006
670
  /**
1007
671
  * Flushes the profile chunk to Sentry.
1008
672
  * @param chunk
1009
673
  */
1010
- _flush(chunk) {
674
+ _flush(chunk) {
1011
675
  if (!this._client) {
1012
- DEBUG_BUILD &&
1013
- core.debug.log('[Profiling] Failed to collect profile, sentry client was never attached to the profiler.');
676
+ DEBUG_BUILD && core.debug.log("[Profiling] Failed to collect profile, sentry client was never attached to the profiler.");
1014
677
  return;
1015
678
  }
1016
-
1017
679
  const transport = this._client.getTransport();
1018
680
  if (!transport) {
1019
- DEBUG_BUILD && core.debug.log('[Profiling] No transport available to send profile chunk.');
681
+ DEBUG_BUILD && core.debug.log("[Profiling] No transport available to send profile chunk.");
1020
682
  return;
1021
683
  }
1022
-
1023
684
  const dsn = this._client.getDsn();
1024
685
  const metadata = this._client.getSdkMetadata();
1025
686
  const tunnel = this._client.getOptions().tunnel;
1026
-
1027
- const envelope = makeProfileChunkEnvelope('node', chunk, metadata?.sdk, tunnel, dsn);
1028
- transport.send(envelope).then(null, reason => {
1029
- DEBUG_BUILD && core.debug.error('Error while sending profile chunk envelope:', reason);
687
+ const envelope = makeProfileChunkEnvelope("node", chunk, metadata?.sdk, tunnel, dsn);
688
+ transport.send(envelope).then(null, (reason) => {
689
+ DEBUG_BUILD && core.debug.error("Error while sending profile chunk envelope:", reason);
1030
690
  });
1031
691
  }
1032
-
1033
692
  /**
1034
693
  * Starts the profiler and registers the flush timer for a given chunk.
1035
694
  * @param chunk
1036
695
  */
1037
- _startChunkProfiling() {
696
+ _startChunkProfiling() {
1038
697
  if (this._chunkData) {
1039
- DEBUG_BUILD && core.debug.log('[Profiling] Chunk is already running, no-op.');
698
+ DEBUG_BUILD && core.debug.log("[Profiling] Chunk is already running, no-op.");
1040
699
  return;
1041
700
  }
1042
-
1043
- const traceId =
1044
- core.getCurrentScope().getPropagationContext().traceId || core.getIsolationScope().getPropagationContext().traceId;
701
+ const traceId = core.getCurrentScope().getPropagationContext().traceId || core.getIsolationScope().getPropagationContext().traceId;
1045
702
  const chunk = this._initializeChunk(traceId);
1046
-
1047
703
  nodeCpuProfiler.CpuProfilerBindings.startProfiling(chunk.id);
1048
704
  DEBUG_BUILD && core.debug.log(`[Profiling] starting profiling chunk: ${chunk.id}`);
1049
-
1050
705
  chunk.timer = global.setTimeout(() => {
1051
706
  DEBUG_BUILD && core.debug.log(`[Profiling] Stopping profiling chunk: ${chunk.id}`);
1052
707
  this._stopChunkProfiling();
1053
- DEBUG_BUILD && core.debug.log('[Profiling] Starting new profiling chunk.');
708
+ DEBUG_BUILD && core.debug.log("[Profiling] Starting new profiling chunk.");
1054
709
  setImmediate(this._restartChunkProfiling.bind(this));
1055
710
  }, CHUNK_INTERVAL_MS);
1056
-
1057
- // Unref timeout so it doesn't keep the process alive.
1058
711
  chunk.timer.unref();
1059
712
  }
1060
-
1061
713
  /**
1062
714
  * Attaches profiling information to spans that were started
1063
715
  * during a profiling session.
1064
716
  */
1065
- _setupSpanChunkInstrumentation() {
717
+ _setupSpanChunkInstrumentation() {
1066
718
  if (!this._client) {
1067
- DEBUG_BUILD &&
1068
- core.debug.log('[Profiling] Failed to initialize span profiling, sentry client was never attached to the profiler.');
719
+ DEBUG_BUILD && core.debug.log("[Profiling] Failed to initialize span profiling, sentry client was never attached to the profiler.");
1069
720
  return;
1070
721
  }
1071
-
1072
722
  this._profilerId = core.uuid4();
1073
- core.getGlobalScope().setContext('profile', {
1074
- profiler_id: this._profilerId,
723
+ core.getGlobalScope().setContext("profile", {
724
+ profiler_id: this._profilerId
1075
725
  });
1076
726
  }
1077
-
1078
727
  /**
1079
728
  * Assigns thread_id and thread name context to a profiled event if there is an active profiler session
1080
729
  */
1081
- _onBeforeSendThreadContextAssignment(event) {
730
+ _onBeforeSendThreadContextAssignment(event) {
1082
731
  if (!this._client || !this._profilerId) return;
1083
732
  this._assignThreadIdContext(event);
1084
733
  }
1085
-
1086
734
  /**
1087
735
  * Clear profiling information from global context when a profile is not running.
1088
736
  */
1089
- _teardownSpanChunkInstrumentation() {
1090
- this._profilerId = undefined;
737
+ _teardownSpanChunkInstrumentation() {
738
+ this._profilerId = void 0;
1091
739
  const globalScope = core.getGlobalScope();
1092
- globalScope.setContext('profile', {});
740
+ globalScope.setContext("profile", {});
1093
741
  }
1094
-
1095
742
  /**
1096
743
  * Initializes new profile chunk metadata
1097
744
  */
1098
- _initializeChunk(traceId) {
745
+ _initializeChunk(traceId) {
1099
746
  this._chunkData = {
1100
747
  id: core.uuid4(),
1101
748
  startTraceID: traceId,
1102
- timer: undefined,
749
+ timer: void 0
1103
750
  };
1104
751
  return this._chunkData;
1105
752
  }
1106
-
1107
753
  /**
1108
754
  * Assigns thread_id and thread name context to a profiled event.
1109
755
  */
1110
- _assignThreadIdContext(event) {
756
+ _assignThreadIdContext(event) {
1111
757
  if (!event?.contexts?.profile) {
1112
758
  return;
1113
759
  }
1114
-
1115
760
  if (!event.contexts) {
1116
761
  return;
1117
762
  }
1118
-
1119
- // @ts-expect-error the trace fallback value is wrong, though it should never happen
1120
- // and in case it does, we dont want to override whatever was passed initially.
1121
763
  event.contexts.trace = {
1122
- ...(event.contexts?.trace ?? {}),
764
+ ...event.contexts?.trace ?? {},
1123
765
  data: {
1124
- ...(event.contexts?.trace?.data ?? {}),
1125
- ['thread.id']: PROFILER_THREAD_ID_STRING,
1126
- ['thread.name']: PROFILER_THREAD_NAME,
1127
- },
766
+ ...event.contexts?.trace?.data ?? {},
767
+ ["thread.id"]: PROFILER_THREAD_ID_STRING,
768
+ ["thread.name"]: PROFILER_THREAD_NAME
769
+ }
1128
770
  };
1129
771
  }
1130
-
1131
772
  /**
1132
773
  * Resets the current chunk state.
1133
774
  */
1134
- _resetChunkData() {
1135
- this._chunkData = undefined;
775
+ _resetChunkData() {
776
+ this._chunkData = void 0;
1136
777
  }
1137
778
  }
1138
-
1139
- /** Exported only for tests. */
1140
779
  const _nodeProfilingIntegration = (() => {
1141
780
  if (![16, 18, 20, 22, 24, 26].includes(NODE_MAJOR)) {
1142
781
  core.consoleSandbox(() => {
1143
- // eslint-disable-next-line no-console
1144
782
  console.warn(
1145
783
  `[Sentry Profiling] You are using a Node.js version that does not have prebuilt binaries (${NODE_MAJOR}).`,
1146
- 'The @sentry/profiling-node package only has prebuilt support for the following LTS versions of Node.js: 16, 18, 20, 22, 24.',
1147
- 'To use the @sentry/profiling-node package with this version of Node.js, you will need to compile the native addon from source.',
1148
- 'See: https://github.com/getsentry/sentry-javascript/tree/develop/packages/profiling-node#building-the-package-from-source',
784
+ "The @sentry/profiling-node package only has prebuilt support for the following LTS versions of Node.js: 16, 18, 20, 22, 24.",
785
+ "To use the @sentry/profiling-node package with this version of Node.js, you will need to compile the native addon from source.",
786
+ "See: https://github.com/getsentry/sentry-javascript/tree/develop/packages/profiling-node#building-the-package-from-source"
1149
787
  );
1150
788
  });
1151
789
  }
1152
-
1153
790
  return {
1154
- name: 'ProfilingIntegration',
791
+ name: "ProfilingIntegration",
1155
792
  _profiler: new ContinuousProfiler(),
1156
793
  setup(client) {
1157
- DEBUG_BUILD && core.debug.log('[Profiling] Profiling integration setup.');
794
+ DEBUG_BUILD && core.debug.log("[Profiling] Profiling integration setup.");
1158
795
  this._profiler.initialize(client);
1159
796
  return;
1160
- },
797
+ }
1161
798
  };
1162
- }) ;
1163
-
1164
- /**
1165
- * Determines the profiling mode based on the options.
1166
- * @param options
1167
- * @returns 'legacy' if the options are using the legacy profiling API, 'current' if the options are using the current profiling API
1168
- */
799
+ });
1169
800
  function getProfilingMode(options) {
1170
- // Legacy mode takes precedence over current mode
1171
- if ('profilesSampleRate' in options || 'profilesSampler' in options) {
1172
- return 'legacy';
801
+ if ("profilesSampleRate" in options || "profilesSampler" in options) {
802
+ return "legacy";
1173
803
  }
1174
-
1175
- if ('profileSessionSampleRate' in options || 'profileLifecycle' in options) {
1176
- return 'current';
804
+ if ("profileSessionSampleRate" in options || "profileLifecycle" in options) {
805
+ return "current";
1177
806
  }
1178
-
1179
- // If neither are set, we are in the legacy continuous profiling mode
1180
- return 'legacy';
807
+ return "legacy";
1181
808
  }
1182
-
1183
- /**
1184
- * We need this integration in order to send data to Sentry. We hook into the event processor
1185
- * and inspect each event to see if it is a transaction event and if that transaction event
1186
- * contains a profile on it's metadata. If that is the case, we create a profiling event envelope
1187
- * and delete the profile from the transaction metadata.
1188
- */
1189
809
  const nodeProfilingIntegration = core.defineIntegration(_nodeProfilingIntegration);
1190
810
 
1191
811
  exports.nodeProfilingIntegration = nodeProfilingIntegration;