@sentry/profiling-node 11.0.0-alpha.0 → 11.0.0-alpha.1
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.
- package/README.md +5 -3
- package/build/cjs/index.js +13 -393
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.js +14 -394
- package/build/esm/index.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/types/integration.d.ts +0 -6
- package/build/types/integration.d.ts.map +1 -1
- package/build/types/utils.d.ts +5 -39
- package/build/types/utils.d.ts.map +1 -1
- package/package.json +3 -3
- package/build/types/spanProfileUtils.d.ts +0 -17
- package/build/types/spanProfileUtils.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -32,13 +32,13 @@ Sentry.init({
|
|
|
32
32
|
dsn: 'https://7fa19397baaf433f919fbe02228d5470@o1137848.ingest.sentry.io/6625302',
|
|
33
33
|
debug: true,
|
|
34
34
|
tracesSampleRate: 1,
|
|
35
|
-
|
|
35
|
+
profileSessionSampleRate: 1,
|
|
36
|
+
profileLifecycle: 'trace',
|
|
36
37
|
integrations: [nodeProfilingIntegration()],
|
|
37
38
|
});
|
|
38
39
|
```
|
|
39
40
|
|
|
40
|
-
Sentry SDK will now
|
|
41
|
-
automatic instrumentation integration.
|
|
41
|
+
The Sentry SDK will now collect profile chunks while spans are active, including spans started by automatic instrumentation.
|
|
42
42
|
|
|
43
43
|
```javascript
|
|
44
44
|
Sentry.startSpan({ name: 'some workflow' }, () => {
|
|
@@ -46,6 +46,8 @@ Sentry.startSpan({ name: 'some workflow' }, () => {
|
|
|
46
46
|
});
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
With `profileLifecycle: 'manual'` (the default), you can start and stop the profiler by calling `Sentry.profiler.startProfiler()` and `Sentry.profiler.stopProfiler()`.
|
|
50
|
+
|
|
49
51
|
### Building the package from source
|
|
50
52
|
|
|
51
53
|
Profiling uses native modules to interop with the v8 javascript engine which means that you may be required to build it
|
package/build/cjs/index.js
CHANGED
|
@@ -3,8 +3,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
3
3
|
const core = require('@sentry/core');
|
|
4
4
|
const nodeCpuProfiler = require('@sentry/node-cpu-profiler');
|
|
5
5
|
const worker_threads = require('worker_threads');
|
|
6
|
-
const os = require('os');
|
|
7
|
-
const process$1 = require('process');
|
|
8
6
|
|
|
9
7
|
const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
|
|
10
8
|
|
|
@@ -13,14 +11,7 @@ const NODE_MAJOR = NODE_VERSION.major;
|
|
|
13
11
|
|
|
14
12
|
const PROFILER_THREAD_ID_STRING = String(worker_threads.threadId);
|
|
15
13
|
const PROFILER_THREAD_NAME = worker_threads.isMainThread ? "main" : "worker";
|
|
16
|
-
const FORMAT_VERSION = "1";
|
|
17
14
|
const CONTINUOUS_FORMAT_VERSION = "2";
|
|
18
|
-
const PLATFORM = os.platform();
|
|
19
|
-
const RELEASE = os.release();
|
|
20
|
-
const VERSION = os.version();
|
|
21
|
-
const TYPE = os.type();
|
|
22
|
-
const MODEL = os.machine();
|
|
23
|
-
const ARCH = os.arch();
|
|
24
15
|
function isRawThreadCpuProfile(profile) {
|
|
25
16
|
return !("thread_metadata" in profile);
|
|
26
17
|
}
|
|
@@ -39,70 +30,6 @@ function enrichWithThreadInformation(profile) {
|
|
|
39
30
|
}
|
|
40
31
|
};
|
|
41
32
|
}
|
|
42
|
-
function createProfilingEvent(client, profile, event) {
|
|
43
|
-
if (!isValidProfile(profile)) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
return createProfilePayload(client, profile, {
|
|
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
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
function createProfilePayload(client, cpuProfile, {
|
|
57
|
-
release,
|
|
58
|
-
environment,
|
|
59
|
-
event_id,
|
|
60
|
-
transaction,
|
|
61
|
-
start_timestamp,
|
|
62
|
-
trace_id,
|
|
63
|
-
profile_id
|
|
64
|
-
}) {
|
|
65
|
-
if (trace_id?.length !== 32) {
|
|
66
|
-
DEBUG_BUILD && core.debug.log(`[Profiling] Invalid traceId: ${trace_id} on profiled event`);
|
|
67
|
-
}
|
|
68
|
-
const enrichedThreadProfile = enrichWithThreadInformation(cpuProfile);
|
|
69
|
-
const profile = {
|
|
70
|
-
event_id: profile_id,
|
|
71
|
-
timestamp: new Date(start_timestamp).toISOString(),
|
|
72
|
-
platform: "node",
|
|
73
|
-
version: FORMAT_VERSION,
|
|
74
|
-
release,
|
|
75
|
-
environment,
|
|
76
|
-
measurements: cpuProfile.measurements,
|
|
77
|
-
runtime: {
|
|
78
|
-
name: "node",
|
|
79
|
-
version: process$1.versions.node || ""
|
|
80
|
-
},
|
|
81
|
-
os: {
|
|
82
|
-
name: PLATFORM,
|
|
83
|
-
version: RELEASE,
|
|
84
|
-
build_number: VERSION
|
|
85
|
-
},
|
|
86
|
-
device: {
|
|
87
|
-
locale: process$1.env["LC_ALL"] || process$1.env["LC_MESSAGES"] || process$1.env["LANG"] || process$1.env["LANGUAGE"] || "",
|
|
88
|
-
model: MODEL,
|
|
89
|
-
manufacturer: TYPE,
|
|
90
|
-
architecture: ARCH,
|
|
91
|
-
is_emulator: false
|
|
92
|
-
},
|
|
93
|
-
debug_meta: {
|
|
94
|
-
images: applyDebugMetadata(client, cpuProfile.resources)
|
|
95
|
-
},
|
|
96
|
-
profile: enrichedThreadProfile,
|
|
97
|
-
transaction: {
|
|
98
|
-
name: transaction,
|
|
99
|
-
id: event_id,
|
|
100
|
-
trace_id: trace_id || "",
|
|
101
|
-
active_thread_id: PROFILER_THREAD_ID_STRING
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
return profile;
|
|
105
|
-
}
|
|
106
33
|
function createProfileChunkPayload(client, cpuProfile, {
|
|
107
34
|
release,
|
|
108
35
|
environment,
|
|
@@ -147,37 +74,6 @@ function createProfilingChunkEvent(client, options, profile, sdk, identifiers) {
|
|
|
147
74
|
sdk
|
|
148
75
|
});
|
|
149
76
|
}
|
|
150
|
-
function isValidSampleRate(rate) {
|
|
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
|
-
);
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
if (rate === true || rate === false) {
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
if (rate < 0 || rate > 1) {
|
|
163
|
-
DEBUG_BUILD && core.debug.warn(`[Profiling] Invalid sample rate. Sample rate must be between 0 and 1. Got ${rate}.`);
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
function isValidProfile(profile) {
|
|
169
|
-
if (profile.samples.length <= 1) {
|
|
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");
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
if (!profile.profile_id) {
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
77
|
function isValidProfileChunk(profile) {
|
|
182
78
|
if (profile.samples.length <= 1) {
|
|
183
79
|
DEBUG_BUILD && // Log a warning if the profile has less than 2 samples so users can know why
|
|
@@ -188,34 +84,6 @@ function isValidProfileChunk(profile) {
|
|
|
188
84
|
}
|
|
189
85
|
return true;
|
|
190
86
|
}
|
|
191
|
-
function addProfilesToEnvelope(envelope, profiles) {
|
|
192
|
-
if (!profiles.length) {
|
|
193
|
-
return envelope;
|
|
194
|
-
}
|
|
195
|
-
for (const profile of profiles) {
|
|
196
|
-
envelope[1].push([{ type: "profile" }, profile]);
|
|
197
|
-
}
|
|
198
|
-
return envelope;
|
|
199
|
-
}
|
|
200
|
-
function findProfiledTransactionsFromEnvelope(envelope) {
|
|
201
|
-
const events = [];
|
|
202
|
-
core.forEachEnvelopeItem(envelope, (item, type) => {
|
|
203
|
-
if (type !== "transaction") {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
for (let j = 1; j < item.length; j++) {
|
|
207
|
-
const event = item[j];
|
|
208
|
-
if (!event) {
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
const profile_id = event.contexts?.profile?.profile_id;
|
|
212
|
-
if (event && profile_id) {
|
|
213
|
-
events.push(event);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
return events;
|
|
218
|
-
}
|
|
219
87
|
function createEventEnvelopeHeaders(sdkInfo, tunnel, dsn) {
|
|
220
88
|
return {
|
|
221
89
|
event_id: core.uuid4(),
|
|
@@ -241,94 +109,11 @@ function applyDebugMetadata(client, resource_paths) {
|
|
|
241
109
|
return core.getDebugImagesForResources(options.stackParser, resource_paths);
|
|
242
110
|
}
|
|
243
111
|
|
|
244
|
-
const MAX_PROFILE_DURATION_MS = 30 * 1e3;
|
|
245
|
-
function maybeProfileSpan(client, span, customSamplingContext) {
|
|
246
|
-
if (!worker_threads.isMainThread) {
|
|
247
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Skipping span profiling in worker thread.");
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
if (!core.spanIsSampled(span)) {
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
if (!client) {
|
|
254
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Profiling disabled, no client found.");
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const options = client.getOptions();
|
|
258
|
-
if (!options) {
|
|
259
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Profiling disabled, no options found.");
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
const profilesSampler = options.profilesSampler;
|
|
263
|
-
let profilesSampleRate = options.profilesSampleRate;
|
|
264
|
-
if (typeof profilesSampler === "function") {
|
|
265
|
-
const { description: spanName = "<unknown>", data } = core.spanToJSON(span);
|
|
266
|
-
const parentSampled = true;
|
|
267
|
-
profilesSampleRate = profilesSampler({
|
|
268
|
-
name: spanName,
|
|
269
|
-
attributes: data,
|
|
270
|
-
parentSampled,
|
|
271
|
-
...customSamplingContext
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
if (!isValidSampleRate(profilesSampleRate)) {
|
|
275
|
-
DEBUG_BUILD && core.debug.warn("[Profiling] Discarding profile because of invalid sample rate.");
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
if (!profilesSampleRate) {
|
|
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
|
-
);
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
const sampled = profilesSampleRate === true ? true : Math.random() < profilesSampleRate;
|
|
285
|
-
if (!sampled) {
|
|
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
|
-
);
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
const profile_id = core.uuid4();
|
|
294
|
-
nodeCpuProfiler.CpuProfilerBindings.startProfiling(profile_id);
|
|
295
|
-
DEBUG_BUILD && core.debug.log(`[Profiling] started profiling transaction: ${core.spanToJSON(span).description}`);
|
|
296
|
-
return profile_id;
|
|
297
|
-
}
|
|
298
|
-
function stopSpanProfile(span, profile_id) {
|
|
299
|
-
if (!profile_id) {
|
|
300
|
-
return null;
|
|
301
|
-
}
|
|
302
|
-
const profile = nodeCpuProfiler.CpuProfilerBindings.stopProfiling(profile_id, 0);
|
|
303
|
-
DEBUG_BUILD && core.debug.log(`[Profiling] stopped profiling of transaction: ${core.spanToJSON(span).description}`);
|
|
304
|
-
if (!profile) {
|
|
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
|
-
);
|
|
309
|
-
return null;
|
|
310
|
-
}
|
|
311
|
-
profile.profile_id = profile_id;
|
|
312
|
-
return profile;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
112
|
const CHUNK_INTERVAL_MS = 1e3 * 60;
|
|
316
|
-
const PROFILE_MAP = new core.LRUMap(50);
|
|
317
|
-
const PROFILE_TIMEOUTS = {};
|
|
318
|
-
function addToProfileQueue(profile_id, profile) {
|
|
319
|
-
PROFILE_MAP.set(profile_id, profile);
|
|
320
|
-
}
|
|
321
|
-
function takeFromProfileQueue(profile_id) {
|
|
322
|
-
const profile = PROFILE_MAP.get(profile_id);
|
|
323
|
-
PROFILE_MAP.remove(profile_id);
|
|
324
|
-
return profile;
|
|
325
|
-
}
|
|
326
113
|
class ContinuousProfiler {
|
|
327
114
|
constructor() {
|
|
328
115
|
this._client = void 0;
|
|
329
116
|
this._chunkData = void 0;
|
|
330
|
-
this._mode = void 0;
|
|
331
|
-
this._legacyProfilerMode = void 0;
|
|
332
117
|
this._profileLifecycle = void 0;
|
|
333
118
|
this._sampled = void 0;
|
|
334
119
|
this._sessionSamplingRate = void 0;
|
|
@@ -347,51 +132,21 @@ class ContinuousProfiler {
|
|
|
347
132
|
}
|
|
348
133
|
this._client = client;
|
|
349
134
|
const options = client.getOptions();
|
|
350
|
-
this._mode = getProfilingMode(options);
|
|
351
135
|
this._sessionSamplingRate = Math.random();
|
|
352
136
|
this._sampled = this._sessionSamplingRate < (options.profileSessionSampleRate ?? 0);
|
|
353
137
|
this._profileLifecycle = options.profileLifecycle ?? "manual";
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
case "span": {
|
|
360
|
-
this._setupAutomaticSpanProfiling();
|
|
361
|
-
break;
|
|
362
|
-
}
|
|
363
|
-
case "continuous": {
|
|
364
|
-
break;
|
|
365
|
-
}
|
|
366
|
-
default: {
|
|
367
|
-
DEBUG_BUILD && core.debug.warn(
|
|
368
|
-
`[Profiling] Unknown profiler mode: ${this._legacyProfilerMode}, profiler was not initialized`
|
|
369
|
-
);
|
|
370
|
-
break;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
138
|
+
this._setupSpanChunkInstrumentation();
|
|
139
|
+
DEBUG_BUILD && core.debug.log(`[Profiling] Profiling mode is ${this._profileLifecycle}.`);
|
|
140
|
+
switch (this._profileLifecycle) {
|
|
141
|
+
case "trace": {
|
|
142
|
+
this._startTraceLifecycleProfiling();
|
|
373
143
|
break;
|
|
374
144
|
}
|
|
375
|
-
case "
|
|
376
|
-
this._setupSpanChunkInstrumentation();
|
|
377
|
-
DEBUG_BUILD && core.debug.log(`[Profiling] Profiling mode is ${this._profileLifecycle}.`);
|
|
378
|
-
switch (this._profileLifecycle) {
|
|
379
|
-
case "trace": {
|
|
380
|
-
this._startTraceLifecycleProfiling();
|
|
381
|
-
break;
|
|
382
|
-
}
|
|
383
|
-
case "manual": {
|
|
384
|
-
break;
|
|
385
|
-
}
|
|
386
|
-
default: {
|
|
387
|
-
DEBUG_BUILD && core.debug.warn(`[Profiling] Unknown profiler mode: ${this._profileLifecycle}, profiler was not initialized`);
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
145
|
+
case "manual": {
|
|
391
146
|
break;
|
|
392
147
|
}
|
|
393
148
|
default: {
|
|
394
|
-
DEBUG_BUILD && core.debug.warn(`[Profiling] Unknown profiler mode: ${this.
|
|
149
|
+
DEBUG_BUILD && core.debug.warn(`[Profiling] Unknown profiler mode: ${this._profileLifecycle}, profiler was not initialized`);
|
|
395
150
|
break;
|
|
396
151
|
}
|
|
397
152
|
}
|
|
@@ -402,64 +157,26 @@ class ContinuousProfiler {
|
|
|
402
157
|
* @returns void
|
|
403
158
|
*/
|
|
404
159
|
start() {
|
|
405
|
-
if (this._mode === "current") {
|
|
406
|
-
this._startProfiler();
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
160
|
if (!this._client) {
|
|
410
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Failed to start,
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
if (this._mode !== "legacy") {
|
|
414
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Continuous profiling is not supported in the current mode.");
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
if (this._legacyProfilerMode === "span") {
|
|
418
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Calls to profiler.start() are not supported in span profiling mode.");
|
|
161
|
+
DEBUG_BUILD && core.debug.log("[Profiling] Failed to start, Sentry client was never attached to the profiler.");
|
|
419
162
|
return;
|
|
420
163
|
}
|
|
421
|
-
this.
|
|
422
|
-
this._setupSpanChunkInstrumentation();
|
|
423
|
-
this._restartChunkProfiling();
|
|
164
|
+
this._startProfiler();
|
|
424
165
|
}
|
|
425
166
|
/**
|
|
426
167
|
* Stops the current chunk and flushes the profile to Sentry.
|
|
427
|
-
* @returns void
|
|
428
168
|
*/
|
|
429
169
|
stop() {
|
|
430
|
-
|
|
431
|
-
this._stopProfiler();
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
if (!this._client) {
|
|
435
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Failed to stop, sentry client was never attached to the profiler.");
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
if (this._mode !== "legacy") {
|
|
439
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Continuous profiling is not supported in the current mode.");
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
if (this._legacyProfilerMode === "span") {
|
|
443
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Calls to profiler.stop() are not supported in span profiling mode.");
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
this._stopChunkProfiling();
|
|
447
|
-
this._teardownSpanChunkInstrumentation();
|
|
170
|
+
this._stopProfiler();
|
|
448
171
|
}
|
|
449
172
|
_startProfiler() {
|
|
450
|
-
if (this._mode !== "current") {
|
|
451
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Continuous profiling is not supported in the current mode.");
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
173
|
if (this._chunkData !== void 0) {
|
|
455
174
|
DEBUG_BUILD && core.debug.log("[Profiling] Profile session already running, no-op.");
|
|
456
175
|
return;
|
|
457
176
|
}
|
|
458
|
-
if (this.
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
177
|
+
if (!this._sampled) {
|
|
178
|
+
DEBUG_BUILD && core.debug.log("[Profiling] Profile session not sampled, no-op.");
|
|
179
|
+
return;
|
|
463
180
|
}
|
|
464
181
|
if (this._profileLifecycle === "trace") {
|
|
465
182
|
DEBUG_BUILD && core.debug.log(
|
|
@@ -470,10 +187,6 @@ class ContinuousProfiler {
|
|
|
470
187
|
this._startChunkProfiling();
|
|
471
188
|
}
|
|
472
189
|
_stopProfiler() {
|
|
473
|
-
if (this._mode !== "current") {
|
|
474
|
-
DEBUG_BUILD && core.debug.log("[Profiling] Continuous profiling is not supported in the current mode.");
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
190
|
if (this._profileLifecycle === "trace") {
|
|
478
191
|
DEBUG_BUILD && core.debug.log(
|
|
479
192
|
"[Profiling] You are using the trace profile lifecycle, manual calls to profiler.startProfiler() and profiler.stopProfiler() will be ignored."
|
|
@@ -514,90 +227,6 @@ class ContinuousProfiler {
|
|
|
514
227
|
activeSpanCounter--;
|
|
515
228
|
});
|
|
516
229
|
}
|
|
517
|
-
_setupAutomaticSpanProfiling() {
|
|
518
|
-
if (!this._client) {
|
|
519
|
-
DEBUG_BUILD && core.debug.log(
|
|
520
|
-
"[Profiling] Failed to setup automatic span profiling, sentry client was never attached to the profiler."
|
|
521
|
-
);
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
const spanToProfileIdMap = /* @__PURE__ */ new WeakMap();
|
|
525
|
-
this._client.on("spanStart", (span) => {
|
|
526
|
-
if (span !== core.getRootSpan(span)) {
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
const profile_id = maybeProfileSpan(this._client, span);
|
|
530
|
-
if (profile_id) {
|
|
531
|
-
const options = this._client.getOptions();
|
|
532
|
-
const maxProfileDurationMs = options._experiments?.maxProfileDurationMs || MAX_PROFILE_DURATION_MS;
|
|
533
|
-
if (PROFILE_TIMEOUTS[profile_id]) {
|
|
534
|
-
global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
|
|
535
|
-
delete PROFILE_TIMEOUTS[profile_id];
|
|
536
|
-
}
|
|
537
|
-
const timeout = global.setTimeout(() => {
|
|
538
|
-
DEBUG_BUILD && core.debug.log(
|
|
539
|
-
"[Profiling] max profile duration elapsed, stopping profiling for:",
|
|
540
|
-
core.spanToJSON(span).description
|
|
541
|
-
);
|
|
542
|
-
const profile = stopSpanProfile(span, profile_id);
|
|
543
|
-
if (profile) {
|
|
544
|
-
addToProfileQueue(profile_id, profile);
|
|
545
|
-
}
|
|
546
|
-
}, maxProfileDurationMs);
|
|
547
|
-
timeout.unref();
|
|
548
|
-
core.getIsolationScope().setContext("profile", { profile_id });
|
|
549
|
-
spanToProfileIdMap.set(span, profile_id);
|
|
550
|
-
}
|
|
551
|
-
});
|
|
552
|
-
this._client.on("spanEnd", (span) => {
|
|
553
|
-
const profile_id = spanToProfileIdMap.get(span);
|
|
554
|
-
if (profile_id) {
|
|
555
|
-
if (PROFILE_TIMEOUTS[profile_id]) {
|
|
556
|
-
global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
|
|
557
|
-
delete PROFILE_TIMEOUTS[profile_id];
|
|
558
|
-
}
|
|
559
|
-
const profile = stopSpanProfile(span, profile_id);
|
|
560
|
-
if (profile) {
|
|
561
|
-
addToProfileQueue(profile_id, profile);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
this._client.on("beforeEnvelope", (envelope) => {
|
|
566
|
-
if (!PROFILE_MAP.size) {
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);
|
|
570
|
-
if (!profiledTransactionEvents.length) {
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
const profilesToAddToEnvelope = [];
|
|
574
|
-
for (const profiledTransaction of profiledTransactionEvents) {
|
|
575
|
-
const profileContext = profiledTransaction.contexts?.profile;
|
|
576
|
-
const profile_id = profileContext?.profile_id;
|
|
577
|
-
if (!profile_id) {
|
|
578
|
-
throw new TypeError("[Profiling] cannot find profile for a transaction without a profile context");
|
|
579
|
-
}
|
|
580
|
-
if (profileContext) {
|
|
581
|
-
delete profiledTransaction.contexts?.profile;
|
|
582
|
-
}
|
|
583
|
-
const cpuProfile = takeFromProfileQueue(profile_id);
|
|
584
|
-
if (!cpuProfile) {
|
|
585
|
-
DEBUG_BUILD && core.debug.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
|
|
586
|
-
continue;
|
|
587
|
-
}
|
|
588
|
-
const profile = createProfilingEvent(this._client, cpuProfile, profiledTransaction);
|
|
589
|
-
if (!profile) return;
|
|
590
|
-
profilesToAddToEnvelope.push(profile);
|
|
591
|
-
this._client.emit("preprocessEvent", profile, {
|
|
592
|
-
event_id: profiledTransaction.event_id
|
|
593
|
-
});
|
|
594
|
-
this._client.emit("postprocessEvent", profile, {
|
|
595
|
-
event_id: profiledTransaction.event_id
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
addProfilesToEnvelope(envelope, profilesToAddToEnvelope);
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
230
|
/**
|
|
602
231
|
* Stop profiler and initializes profiling of the next chunk
|
|
603
232
|
*/
|
|
@@ -801,15 +430,6 @@ const _nodeProfilingIntegration = (() => {
|
|
|
801
430
|
}
|
|
802
431
|
};
|
|
803
432
|
});
|
|
804
|
-
function getProfilingMode(options) {
|
|
805
|
-
if ("profilesSampleRate" in options || "profilesSampler" in options) {
|
|
806
|
-
return "legacy";
|
|
807
|
-
}
|
|
808
|
-
if ("profileSessionSampleRate" in options || "profileLifecycle" in options) {
|
|
809
|
-
return "current";
|
|
810
|
-
}
|
|
811
|
-
return "legacy";
|
|
812
|
-
}
|
|
813
433
|
const nodeProfilingIntegration = core.defineIntegration(_nodeProfilingIntegration);
|
|
814
434
|
|
|
815
435
|
exports.nodeProfilingIntegration = nodeProfilingIntegration;
|