Skip to content

Commit 65b46e7

Browse files
authored
feat: Add eventLoopBlockIntegration (#1188)
1 parent b9f5857 commit 65b46e7

File tree

16 files changed

+374
-63
lines changed

16 files changed

+374
-63
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module.exports = {
1717
'/renderer/**',
1818
'/examples/**',
1919
'/common/**',
20+
'/native/**',
2021
'/index.*',
2122
'/integrations.*',
2223
'/utility/**',

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ stats.json
1717
/main
1818
/common
1919
/utility
20+
/native
2021
/index.*
2122
/integrations.*
2223
/ipc.*

package.json

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,54 @@
1414
"default": "./esm/renderer/index.js"
1515
},
1616
"./main": {
17-
"require": "./main/index.js",
18-
"import": "./esm/main/index.js"
17+
"require": {
18+
"types": "./main/index.d.ts",
19+
"default": "./main/index.js"
20+
},
21+
"import": {
22+
"types": "./esm/main/index.d.ts",
23+
"default": "./esm/main/index.js"
24+
}
1925
},
2026
"./renderer": {
21-
"require": "./renderer/index.js",
22-
"import": "./esm/renderer/index.js"
27+
"require": {
28+
"types": "./renderer/index.d.ts",
29+
"default": "./renderer/index.js"
30+
},
31+
"import": {
32+
"types": "./esm/renderer/index.d.ts",
33+
"default": "./esm/renderer/index.js"
34+
}
2335
},
2436
"./preload": {
25-
"require": "./preload/index.js",
26-
"import": "./esm/preload/index.js"
37+
"require": {
38+
"types": "./preload/index.d.ts",
39+
"default": "./preload/index.js"
40+
},
41+
"import": {
42+
"types": "./esm/preload/index.d.ts",
43+
"default": "./esm/preload/index.js"
44+
}
2745
},
2846
"./utility": {
29-
"require": "./utility/index.js",
30-
"import": "./esm/utility/index.js"
47+
"require": {
48+
"types": "./utility/index.d.ts",
49+
"default": "./utility/index.js"
50+
},
51+
"import": {
52+
"types": "./esm/utility/index.d.ts",
53+
"default": "./esm/utility/index.js"
54+
}
55+
},
56+
"./native": {
57+
"require": {
58+
"types": "./native/index.d.ts",
59+
"default": "./native/index.js"
60+
},
61+
"import": {
62+
"types": "./esm/native/index.d.ts",
63+
"default": "./esm/native/index.js"
64+
}
3165
}
3266
},
3367
"repository": "https://github.com/getsentry/sentry-electron.git",
@@ -65,9 +99,18 @@
6599
"@sentry/node": "9.40.0",
66100
"deepmerge": "4.3.1"
67101
},
102+
"peerDependencies": {
103+
"@sentry/node-native": "9.39.0"
104+
},
105+
"peerDependenciesMeta": {
106+
"@sentry/node-native": {
107+
"optional": true
108+
}
109+
},
68110
"devDependencies": {
69111
"@rollup/plugin-node-resolve": "^15.2.3",
70112
"@rollup/plugin-typescript": "^11.1.6",
113+
"@sentry/node-native": "9.40.0",
71114
"@sentry-internal/eslint-config-sdk": "9.40.0",
72115
"@sentry-internal/typescript": "9.40.0",
73116
"@types/busboy": "^1.5.4",

rollup.config.mjs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { readFileSync } from 'node:fs';
55
import typescript from '@rollup/plugin-typescript';
66

77
const pkgJson = JSON.parse(readFileSync(resolve(process.cwd(), 'package.json')));
8-
98
const dependencies = Object.keys(pkgJson.dependencies || {});
10-
const external = [...builtinModules, /^node:/, 'electron', ...dependencies];
9+
const peerDependencies = Object.keys(pkgJson.peerDependencies || {});
10+
const external = [...builtinModules, /^node:/, 'electron', ...dependencies, ...peerDependencies];
1111

1212
const outputOptions = {
1313
sourcemap: true,
@@ -70,13 +70,17 @@ function bundlePreload(format, input, output) {
7070
};
7171
}
7272

73+
const entryPoints = [
74+
'src/index.ts',
75+
'src/main/index.ts',
76+
'src/renderer/index.ts',
77+
'src/utility/index.ts',
78+
'src/native/index.ts',
79+
];
80+
7381
export default [
74-
transpileFiles('cjs', ['src/index.ts', 'src/main/index.ts', 'src/renderer/index.ts', 'src/utility/index.ts'], '.'),
75-
transpileFiles(
76-
'esm',
77-
['src/index.ts', 'src/main/index.ts', 'src/renderer/index.ts', 'src/utility/index.ts'],
78-
'./esm',
79-
),
82+
transpileFiles('cjs', entryPoints, '.'),
83+
transpileFiles('esm', entryPoints, './esm'),
8084
bundlePreload('cjs', 'src/preload/index.ts', './preload/index.js'),
8185
bundlePreload('esm', 'src/preload/index.ts', './esm/preload/index.js'),
8286
];

scripts/update-sdk-versions.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ if (current !== latest) {
2222
}
2323
}
2424

25+
for (const dep of Object.keys(packageJson.peerDependencies)) {
26+
if (dep.match(re)) {
27+
packageJson.peerDependencies[dep] = latest;
28+
}
29+
}
30+
2531
for (const dep of Object.keys(packageJson.devDependencies)) {
2632
if (dep.match(re)) {
2733
packageJson.devDependencies[dep] = latest;

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ throw new Error(`The Sentry Electron SDK uses different code for the main and re
33
In the Electron main process you should import '@sentry/electron/main'
44
In the Electron renderer process you should import '@sentry/electron/renderer'
55
6-
https://github.com/getsentry/sentry-electron/blob/master/MIGRATION.md#initializing-the-sdk-in-v5
6+
https://docs.sentry.io/platforms/javascript/guides/electron/#configure
77
`);
88

99
export {};

src/main/integrations/anr.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ type Options = Parameters<typeof nodeAnrIntegration>[0];
88

99
/**
1010
* Starts a worker thread to detect App Not Responding (ANR) events
11+
*
12+
* @deprecated The ANR integration has been deprecated. Use `eventLoopBlockIntegration` from `@sentry/electron/native`
13+
* instead. You will need to install `@sentry/node-native` as a dependency.
1114
*/
1215
export const anrIntegration: (options: Options) => Integration = defineIntegration((options: Options = {}) => {
1316
if (ELECTRON_MAJOR_VERSION < 22) {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { defineIntegration, Integration } from '@sentry/core';
2+
// eslint-disable-next-line import/no-extraneous-dependencies
3+
import {
4+
disableBlockDetectionForCallback,
5+
eventLoopBlockIntegration as nodeEventLoopBlockIntegration,
6+
pauseEventLoopBlockDetection,
7+
restartEventLoopBlockDetection,
8+
} from '@sentry/node-native';
9+
import { app, powerMonitor } from 'electron';
10+
11+
type IntegrationInternal = { start: () => void; stop: () => void };
12+
13+
type Options = Parameters<typeof nodeEventLoopBlockIntegration>[0];
14+
15+
/**
16+
* > **Note**
17+
* >
18+
* > You should add `@sentry/node-native` to your dependencies to use this integration.
19+
*
20+
* Monitors the Node.js event loop for blocking behavior and reports blocked events to Sentry.
21+
*
22+
* Uses a background worker and native module to detect when the main thread is blocked for longer than the configured
23+
* threshold (default: 1 second). When a block is detected, it captures an event with stack traces for every thread.
24+
*
25+
* ```js
26+
* import * as Sentry from '@sentry/electron/main';
27+
* import { eventLoopBlockIntegration } from '@sentry/electron/native';
28+
*
29+
* Sentry.init({
30+
* dsn: '__YOUR_DSN__',
31+
* integrations: [
32+
* eventLoopBlockIntegration({
33+
* threshold: 500, // Report blocks longer than 500ms
34+
* }),
35+
* ],
36+
* });
37+
* ```
38+
*/
39+
export const eventLoopBlockIntegration: (options: Options) => Integration = defineIntegration(
40+
(options: Options = {}) => {
41+
if (process.type === 'renderer') {
42+
throw new Error(
43+
'Detected `eventLoopBlockIntegration` in renderer process. This integration should only be used in the Electron main process.',
44+
);
45+
}
46+
47+
const integration = nodeEventLoopBlockIntegration({
48+
...options,
49+
staticTags: {
50+
'event.environment': 'javascript',
51+
'event.origin': 'electron',
52+
'event.process': 'browser',
53+
...options.staticTags,
54+
},
55+
appRootPath: app.getAppPath(),
56+
}) as Integration & IntegrationInternal;
57+
58+
powerMonitor.on('suspend', () => {
59+
integration.stop();
60+
});
61+
62+
powerMonitor.on('lock-screen', () => {
63+
integration.stop();
64+
});
65+
66+
powerMonitor.on('resume', () => {
67+
integration.start();
68+
});
69+
70+
powerMonitor.on('unlock-screen', () => {
71+
integration.start();
72+
});
73+
74+
return integration;
75+
},
76+
);
77+
78+
export { disableBlockDetectionForCallback, pauseEventLoopBlockDetection, restartEventLoopBlockDetection };

src/native/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export {
2+
disableBlockDetectionForCallback,
3+
eventLoopBlockIntegration,
4+
pauseEventLoopBlockDetection,
5+
restartEventLoopBlockDetection,
6+
} from './event-loop-blocked-integration';

test/e2e/prepare.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export async function prepareTestFiles(
7171
logger: TestLogger,
7272
testBasePath: string,
7373
executionBasePath: string,
74+
electronVersion: string,
7475
port: number,
7576
convertFilesToEsm: boolean,
7677
): Promise<void> {
@@ -100,9 +101,11 @@ export async function prepareTestFiles(
100101
// We replace the Sentry JavaScript dependency versions to match that of @sentry/core
101102
.replace(/"@sentry\/replay": ".*"/, `"@sentry/replay": "${JS_SDK_VERSION}"`)
102103
.replace(/"@sentry\/react": ".*"/, `"@sentry/react": "${JS_SDK_VERSION}"`)
104+
.replace(/"@sentry\/node-native": ".*"/, `"@sentry/node-native": "${JS_SDK_VERSION}"`)
103105
.replace(/"@sentry\/profiling-node": ".*"/, `"@sentry/profiling-node": "${JS_SDK_VERSION}"`)
104106
.replace(/"@sentry\/integrations": ".*"/, `"@sentry/integrations": "${JS_SDK_VERSION}"`)
105-
.replace(/"@sentry\/vue": ".*"/, `"@sentry/vue": "${JS_SDK_VERSION}"`);
107+
.replace(/"@sentry\/vue": ".*"/, `"@sentry/vue": "${JS_SDK_VERSION}"`)
108+
.replace(/"electron": ".*"/, `"electron": "${electronVersion}"`);
106109
}
107110

108111
if (convertFilesToEsm) {

0 commit comments

Comments
 (0)