@sentry/profiling-node 10.70.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 +8 -46
- package/build/cjs/index.js +14 -394
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.js +15 -395
- 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 +8 -20
- package/build/types/spanProfileUtils.d.ts +0 -17
- package/build/types/spanProfileUtils.d.ts.map +0 -1
- package/build/types-ts3.8/debug-build.d.ts +0 -7
- package/build/types-ts3.8/index.d.ts +0 -2
- package/build/types-ts3.8/integration.d.ts +0 -14
- package/build/types-ts3.8/nodeVersion.d.ts +0 -7
- package/build/types-ts3.8/spanProfileUtils.d.ts +0 -17
- package/build/types-ts3.8/utils.d.ts +0 -79
- package/scripts/prune-profiler-binaries.js +0 -204
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
|
|
@@ -121,9 +123,7 @@ const nextConfig = {
|
|
|
121
123
|
},
|
|
122
124
|
};
|
|
123
125
|
|
|
124
|
-
module.exports = withSentryConfig(nextConfig, {
|
|
125
|
-
/* ... */
|
|
126
|
-
});
|
|
126
|
+
module.exports = withSentryConfig(nextConfig, {/* ... */});
|
|
127
127
|
```
|
|
128
128
|
|
|
129
129
|
[webpack](https://webpack.js.org/configuration/externals/#externals)
|
|
@@ -236,46 +236,8 @@ require('esbuild').build({
|
|
|
236
236
|
|
|
237
237
|
Once you run `node esbuild.serverless.js` esbuild wil bundle and output the files to ./dist folder, but note that all of
|
|
238
238
|
the binaries will be copied. This is wasteful as you will likely only need one of these libraries to be available during
|
|
239
|
-
runtime.
|
|
240
|
-
|
|
241
|
-
> **Deprecation notice:** This script will be removed in the next major version. If you depend on it, please comment on
|
|
242
|
-
> [this issue](https://github.com/getsentry/sentry-javascript/issues/20567).
|
|
243
|
-
|
|
244
|
-
To prune the other libraries, profiling-node ships with a small utility script that helps you prune unused binaries:
|
|
245
|
-
|
|
246
|
-
```bash
|
|
247
|
-
npx --package=@sentry/profiling-node sentry-prune-profiler-binaries
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
Use `--help` to see a list of available options or `--dry-run` if you want it to log the binaries that would have been
|
|
251
|
-
deleted.
|
|
252
|
-
|
|
253
|
-
Example of only preserving a binary to run node16 on linux x64 musl.
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
npx --package=@sentry/profiling-node sentry-prune-profiler-binaries --target_dir_path=./dist --target_platform=linux --target_node=16 --target_stdlib=musl --target_arch=x64
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
Which will output something like
|
|
260
|
-
|
|
261
|
-
```
|
|
262
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-darwin-x64-108-IFGH3SUR.node (90.41 KiB)
|
|
263
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-darwin-x64-93-Q7KBVHSP.node (74.16 KiB)
|
|
264
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-arm64-glibc-108-NXSISRTB.node (52.17 KiB)
|
|
265
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-arm64-glibc-83-OEQT5HUK.node (52.08 KiB)
|
|
266
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-arm64-glibc-93-IIXXW2PN.node (52.06 KiB)
|
|
267
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-arm64-musl-108-DSILNYHA.node (48.46 KiB)
|
|
268
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-arm64-musl-83-4CNOBNC3.node (48.37 KiB)
|
|
269
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-arm64-musl-93-JA5PKNWQ.node (48.38 KiB)
|
|
270
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-x64-glibc-108-NXSISRTB.node (52.17 KiB)
|
|
271
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-x64-glibc-83-OEQT5HUK.node (52.08 KiB)
|
|
272
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-x64-glibc-93-IIXXW2PN.node (52.06 KiB)
|
|
273
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-x64-musl-108-CX7SL27U.node (51.50 KiB)
|
|
274
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-linux-x64-musl-83-YD7ZQK2E.node (51.53 KiB)
|
|
275
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-win32-x64-108-P7V3URQV.node (181.50 KiB)
|
|
276
|
-
Sentry: pruned ./dist/sentry_cpu_profiler-win32-x64-93-3PKQDSGE.node (181.50 KiB)
|
|
277
|
-
✅ Sentry: pruned 15 binaries, saved 1.06 MiB in total.
|
|
278
|
-
```
|
|
239
|
+
runtime. Since the binaries follow the `sentry_cpu_profiler-<platform>-<arch>-<stdlib>-<abi>.node` naming scheme, you can
|
|
240
|
+
delete the ones that do not match your target runtime as part of your build step to reduce the deployment size.
|
|
279
241
|
|
|
280
242
|
### Environment flags
|
|
281
243
|
|
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
|
*/
|
|
@@ -702,7 +331,7 @@ class ContinuousProfiler {
|
|
|
702
331
|
DEBUG_BUILD && core.debug.log("[Profiling] Chunk is already running, no-op.");
|
|
703
332
|
return;
|
|
704
333
|
}
|
|
705
|
-
const traceId = core.getCurrentScope().getPropagationContext().traceId
|
|
334
|
+
const traceId = core.getCurrentScope().getPropagationContext().traceId;
|
|
706
335
|
const chunk = this._initializeChunk(traceId);
|
|
707
336
|
nodeCpuProfiler.CpuProfilerBindings.startProfiling(chunk.id);
|
|
708
337
|
DEBUG_BUILD && core.debug.log(`[Profiling] starting profiling chunk: ${chunk.id}`);
|
|
@@ -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;
|