1
1
// TODO (v8): This import can be removed once we only support Node with global URL
2
2
import { URL } from 'url' ;
3
3
import { convertIntegrationFnToClass , defineIntegration , getCurrentScope } from '@sentry/core' ;
4
- import type { Client , Contexts , Event , EventHint , Integration , IntegrationClass , IntegrationFn } from '@sentry/types' ;
4
+ import type {
5
+ Client ,
6
+ Contexts ,
7
+ Event ,
8
+ EventHint ,
9
+ Integration ,
10
+ IntegrationClass ,
11
+ IntegrationFn ,
12
+ IntegrationFnResult ,
13
+ } from '@sentry/types' ;
5
14
import { dynamicRequire , logger } from '@sentry/utils' ;
6
15
import type { Worker , WorkerOptions } from 'worker_threads' ;
7
16
import type { NodeClient } from '../../client' ;
@@ -52,23 +61,51 @@ interface InspectorApi {
52
61
53
62
const INTEGRATION_NAME = 'Anr' ;
54
63
64
+ type AnrInternal = { startWorker : ( ) => void ; stopWorker : ( ) => void } ;
65
+
55
66
const _anrIntegration = ( ( options : Partial < AnrIntegrationOptions > = { } ) => {
67
+ let worker : Promise < ( ) => void > | undefined ;
68
+ let client : NodeClient | undefined ;
69
+
56
70
return {
57
71
name : INTEGRATION_NAME ,
58
72
// TODO v8: Remove this
59
73
setupOnce ( ) { } , // eslint-disable-line @typescript-eslint/no-empty-function
60
- setup ( client : NodeClient ) {
74
+ startWorker : ( ) => {
75
+ if ( worker ) {
76
+ return ;
77
+ }
78
+
79
+ if ( client ) {
80
+ worker = _startWorker ( client , options ) ;
81
+ }
82
+ } ,
83
+ stopWorker : ( ) => {
84
+ if ( worker ) {
85
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
86
+ worker . then ( stop => {
87
+ stop ( ) ;
88
+ worker = undefined ;
89
+ } ) ;
90
+ }
91
+ } ,
92
+ setup ( initClient : NodeClient ) {
61
93
if ( NODE_VERSION . major < 16 || ( NODE_VERSION . major === 16 && NODE_VERSION . minor < 17 ) ) {
62
94
throw new Error ( 'ANR detection requires Node 16.17.0 or later' ) ;
63
95
}
64
96
65
- // setImmediate is used to ensure that all other integrations have been setup
66
- setImmediate ( ( ) => _startWorker ( client , options ) ) ;
97
+ client = initClient ;
98
+
99
+ // setImmediate is used to ensure that all other integrations have had their setup called first.
100
+ // This allows us to call into all integrations to fetch the full context
101
+ setImmediate ( ( ) => this . startWorker ( ) ) ;
67
102
} ,
68
- } ;
103
+ } as IntegrationFnResult & AnrInternal ;
69
104
} ) satisfies IntegrationFn ;
70
105
71
- export const anrIntegration = defineIntegration ( _anrIntegration ) ;
106
+ type AnrReturn = ( options ?: Partial < AnrIntegrationOptions > ) => IntegrationFnResult & AnrInternal ;
107
+
108
+ export const anrIntegration = defineIntegration ( _anrIntegration ) as AnrReturn ;
72
109
73
110
/**
74
111
* Starts a thread to detect App Not Responding (ANR) events
@@ -90,14 +127,20 @@ export type Anr = typeof Anr;
90
127
/**
91
128
* Starts the ANR worker thread
92
129
*/
93
- async function _startWorker ( client : NodeClient , _options : Partial < AnrIntegrationOptions > ) : Promise < void > {
94
- const contexts = await getContexts ( client ) ;
130
+ async function _startWorker (
131
+ client : NodeClient ,
132
+ integrationOptions : Partial < AnrIntegrationOptions > ,
133
+ ) : Promise < ( ) => void > {
95
134
const dsn = client . getDsn ( ) ;
96
135
97
136
if ( ! dsn ) {
98
- return ;
137
+ return ( ) => {
138
+ //
139
+ } ;
99
140
}
100
141
142
+ const contexts = await getContexts ( client ) ;
143
+
101
144
// These will not be accurate if sent later from the worker thread
102
145
delete contexts . app ?. app_memory ;
103
146
delete contexts . device ?. free_memory ;
@@ -116,11 +159,11 @@ async function _startWorker(client: NodeClient, _options: Partial<AnrIntegration
116
159
release : initOptions . release ,
117
160
dist : initOptions . dist ,
118
161
sdkMetadata,
119
- appRootPath : _options . appRootPath ,
120
- pollInterval : _options . pollInterval || DEFAULT_INTERVAL ,
121
- anrThreshold : _options . anrThreshold || DEFAULT_HANG_THRESHOLD ,
122
- captureStackTrace : ! ! _options . captureStackTrace ,
123
- staticTags : _options . staticTags || { } ,
162
+ appRootPath : integrationOptions . appRootPath ,
163
+ pollInterval : integrationOptions . pollInterval || DEFAULT_INTERVAL ,
164
+ anrThreshold : integrationOptions . anrThreshold || DEFAULT_HANG_THRESHOLD ,
165
+ captureStackTrace : ! ! integrationOptions . captureStackTrace ,
166
+ staticTags : integrationOptions . staticTags || { } ,
124
167
contexts,
125
168
} ;
126
169
@@ -139,6 +182,7 @@ async function _startWorker(client: NodeClient, _options: Partial<AnrIntegration
139
182
} ) ;
140
183
141
184
process . on ( 'exit' , ( ) => {
185
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
142
186
worker . terminate ( ) ;
143
187
} ) ;
144
188
@@ -176,4 +220,10 @@ async function _startWorker(client: NodeClient, _options: Partial<AnrIntegration
176
220
177
221
// Ensure this thread can't block app exit
178
222
worker . unref ( ) ;
223
+
224
+ return ( ) => {
225
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
226
+ worker . terminate ( ) ;
227
+ clearInterval ( timer ) ;
228
+ } ;
179
229
}
0 commit comments