This repository has been archived by the owner on Jan 30, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathah-stack-capturer.js
149 lines (134 loc) · 4.54 KB
/
ah-stack-capturer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
const asyncHooksRx = /\(async_hooks.js/
function no() { return false }
function captureStack() {
var stack = {}
Error.captureStackTrace(stack, captureStack)
return stack.stack
}
class StackCapturer {
/**
* Creates StackCapturere instance.
* Either `shouldCapture` OR `events` with optional `types` need to be supplied.
*
* @name StackCapturer
*
* @constructor
* @param {Object} $0 configures when a stack should be captured
*
* @param {Set.<string>} [$0.events=null] defines on which async hooks events
* (init|before|after|destroy) a stack should be captured
*
* @param {Set.<string>} [$0.types=null] defines for which async hook types a
* stack should be captured
*
* @param {function} [$0.shouldCapture=null] `function ((event, type, activity)`
* if supplied overrides the `shouldCapture` method entirely
*/
constructor({
events = null
, types = null
, shouldCapture = null
}) {
if (shouldCapture != null && typeof shouldCapture !== 'function') {
throw new Error('shouldCapture needs to be of type function')
}
if (shouldCapture != null) {
if (events != null || types != null) {
throw new Error('Only provide either events and types OR shouldCapture function')
}
} else if (events == null || !(events instanceof Set)) {
throw new Error('Need to supply shouldCapture of type function or events of type Set')
}
if (types != null && !(types instanceof Set)) {
throw new Error('types need to be of type Set')
}
this._events = events
this._types = types
this._shouldCapture = shouldCapture
}
/**
* Returns `true|false` indicating if a stack should be captured according to the
* options passed in the @constructor.
*
* @name stackCapturer.shouldCaptureStack
* @function
* @param {String} event the async hook event (init|before|after|destroy)
* @param {String} type the type of async resource that triggered the event
* @return {Boolean} `true` or `false` indicating if a stack should be captured
*/
shouldCaptureStack(event, type, activity) {
// support entirely overriding the shouldCaptureStack function
if (this._shouldCapture != null) return this._shouldCapture(event, type, activity)
// if it wasn't supplied use events and types to determine if we should
// capture the stack
if (!this._events.has(event)) return false
if (this._types != null && !this._types.has(type)) return false
return true
}
/**
* Captures the current stack.
*
* @name stackCapturer.captureStack
* @function
* @return {String} the current stack
*/
captureStack() {
return captureStack()
}
/**
* Processes the supplied stack by splitting the string into lines
* and removing those that are part of the async hook execution itself.
*
* This allows the user to focus only on the relevant stack.
*
* @name stackCapturer.processStack
* @function
* @param {String} stack the captured stack
* @return {Array.<String>} the processed stack
*/
processStack(stack) {
// was it already processed?
if (Array.isArray(stack)) return stack
// remove first line (Error) and then find last mention of async_hooks
// return all lines after that
const lines = stack.split('\n').slice(1).map(x => x.trim())
const len = lines.length
let i = 0
// find first occurence
while (!asyncHooksRx.test(lines[i]) && i < len) i++
// We blew past everything and didn't seem to find any async hooks
// related part of the stack. Therefore let's include all of it.
if (i === len) return lines
// read past last occurence
while (asyncHooksRx.test(lines[i]) && i < len) i++
// We found async_hooks, but nothing that happened before.
// Therefore let's just return nothing
if (i === len) return []
// don't convert back to string in case the consumer wants
// to do more with the stack lines
return lines.slice(i)
}
/**
* Creates a StackCapturer that captures ALL events for the supplied types.
*
* @name StackCapturer.forAllEvents
* @function
* @param {Set.<String>=} types types passed to the StackCapturer constructor
*/
static forAllEvents(types = null) {
return new StackCapturer({
events: new Set([ 'init', 'before', 'after', 'destroy' ])
, types
})
}
/**
* Creates a StackCapturer that captures nothing.
*
* @name StackCapturer.turnedOff
* @function
*/
static turnedOff() {
return new StackCapturer({ shouldCapture: no })
}
}
module.exports = StackCapturer