dd-trace 2.3.1 → 2.4.2

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.
Files changed (53) hide show
  1. package/ci/init.js +26 -2
  2. package/index.d.ts +51 -0
  3. package/package.json +2 -2
  4. package/packages/datadog-instrumentations/index.js +10 -0
  5. package/packages/datadog-instrumentations/src/amqp10.js +70 -0
  6. package/packages/datadog-instrumentations/src/amqplib.js +58 -0
  7. package/packages/datadog-instrumentations/src/cassandra-driver.js +191 -0
  8. package/packages/datadog-instrumentations/src/cucumber.js +27 -12
  9. package/packages/datadog-instrumentations/src/helpers/hook.js +44 -0
  10. package/packages/datadog-instrumentations/src/helpers/instrument.js +31 -58
  11. package/packages/datadog-instrumentations/src/http/client.js +170 -0
  12. package/packages/datadog-instrumentations/src/http/server.js +61 -0
  13. package/packages/datadog-instrumentations/src/http.js +4 -0
  14. package/packages/datadog-instrumentations/src/mocha.js +139 -0
  15. package/packages/datadog-instrumentations/src/mongodb-core.js +179 -0
  16. package/packages/datadog-instrumentations/src/net.js +117 -0
  17. package/packages/datadog-instrumentations/src/pg.js +75 -0
  18. package/packages/datadog-instrumentations/src/rhea.js +224 -0
  19. package/packages/datadog-instrumentations/src/tedious.js +66 -0
  20. package/packages/datadog-plugin-amqp10/src/index.js +79 -122
  21. package/packages/datadog-plugin-amqplib/src/index.js +77 -142
  22. package/packages/datadog-plugin-cassandra-driver/src/index.js +52 -224
  23. package/packages/datadog-plugin-cucumber/src/index.js +3 -1
  24. package/packages/datadog-plugin-elasticsearch/src/index.js +4 -2
  25. package/packages/datadog-plugin-http/src/client.js +112 -252
  26. package/packages/datadog-plugin-http/src/index.js +29 -3
  27. package/packages/datadog-plugin-http/src/server.js +54 -32
  28. package/packages/datadog-plugin-jest/src/jest-environment.js +3 -3
  29. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +5 -3
  30. package/packages/datadog-plugin-mocha/src/index.js +96 -207
  31. package/packages/datadog-plugin-mongodb-core/src/index.js +119 -3
  32. package/packages/datadog-plugin-net/src/index.js +65 -121
  33. package/packages/datadog-plugin-next/src/index.js +10 -10
  34. package/packages/datadog-plugin-pg/src/index.js +32 -69
  35. package/packages/datadog-plugin-rhea/src/index.js +59 -225
  36. package/packages/datadog-plugin-tedious/src/index.js +38 -86
  37. package/packages/dd-trace/lib/version.js +1 -1
  38. package/packages/dd-trace/src/appsec/recommended.json +235 -315
  39. package/packages/dd-trace/src/config.js +6 -0
  40. package/packages/dd-trace/src/iitm.js +5 -1
  41. package/packages/dd-trace/src/loader.js +6 -4
  42. package/packages/dd-trace/src/noop/tracer.js +4 -0
  43. package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -1
  44. package/packages/dd-trace/src/opentracing/span.js +34 -0
  45. package/packages/dd-trace/src/plugin_manager.js +4 -0
  46. package/packages/dd-trace/src/plugins/plugin.js +3 -1
  47. package/packages/dd-trace/src/plugins/util/web.js +99 -93
  48. package/packages/dd-trace/src/proxy.js +4 -0
  49. package/packages/dd-trace/src/ritm.js +60 -25
  50. package/packages/dd-trace/src/tracer.js +16 -0
  51. package/packages/datadog-plugin-mongodb-core/src/legacy.js +0 -59
  52. package/packages/datadog-plugin-mongodb-core/src/unified.js +0 -138
  53. package/packages/datadog-plugin-mongodb-core/src/util.js +0 -143
@@ -1,40 +1,29 @@
1
1
  'use strict'
2
2
 
3
- const url = require('url')
4
- const log = require('../../dd-trace/src/log')
3
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
4
+ const { storage } = require('../../datadog-core')
5
5
  const tags = require('../../../ext/tags')
6
- const kinds = require('../../../ext/kinds')
6
+ const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
7
7
  const formats = require('../../../ext/formats')
8
+ const HTTP_HEADERS = formats.HTTP_HEADERS
8
9
  const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
9
- const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
10
- const { storage } = require('../../datadog-core')
10
+ const log = require('../../dd-trace/src/log')
11
+ const url = require('url')
11
12
 
12
- const HTTP_HEADERS = formats.HTTP_HEADERS
13
13
  const HTTP_STATUS_CODE = tags.HTTP_STATUS_CODE
14
14
  const HTTP_REQUEST_HEADERS = tags.HTTP_REQUEST_HEADERS
15
15
  const HTTP_RESPONSE_HEADERS = tags.HTTP_RESPONSE_HEADERS
16
- const SPAN_KIND = tags.SPAN_KIND
17
- const CLIENT = kinds.CLIENT
18
-
19
- function patch (http, methodName, tracer, config) {
20
- config = normalizeConfig(tracer, config)
21
- this.wrap(http, methodName, fn => makeRequestTrace(fn))
22
16
 
23
- function makeRequestTrace (request) {
24
- return function requestTrace () {
25
- const store = storage.getStore()
26
-
27
- if (store && store.noop) return request.apply(this, arguments)
28
-
29
- let args
17
+ class HttpClientPlugin extends Plugin {
18
+ static get name () {
19
+ return 'http'
20
+ }
30
21
 
31
- try {
32
- args = normalizeArgs.apply(null, arguments)
33
- } catch (e) {
34
- log.error(e)
35
- return request.apply(this, arguments)
36
- }
22
+ constructor (...args) {
23
+ super(...args)
37
24
 
25
+ this.addSub('apm:http:client:request:start', ({ args, http }) => {
26
+ const store = storage.getStore()
38
27
  const options = args.options
39
28
  const agent = options.agent || options._defaultAgent || http.globalAgent
40
29
  const protocol = options.protocol || agent.protocol || 'http:'
@@ -43,17 +32,13 @@ function patch (http, methodName, tracer, config) {
43
32
  const path = options.path ? options.path.split(/[?#]/)[0] : '/'
44
33
  const uri = `${protocol}//${host}${path}`
45
34
 
46
- let callback = args.callback
47
-
48
35
  const method = (options.method || 'GET').toUpperCase()
49
-
50
- const scope = tracer.scope()
51
- const childOf = scope.active()
52
- const span = tracer.startSpan('http.request', {
36
+ const childOf = store ? store.span : store
37
+ const span = this.tracer.startSpan('http.request', {
53
38
  childOf,
54
39
  tags: {
55
- [SPAN_KIND]: CLIENT,
56
- 'service.name': getServiceName(tracer, config, options),
40
+ 'span.kind': 'client',
41
+ 'service.name': getServiceName(this.tracer, this.config, options),
57
42
  'resource.name': method,
58
43
  'span.type': 'http',
59
44
  'http.method': method,
@@ -61,184 +46,117 @@ function patch (http, methodName, tracer, config) {
61
46
  }
62
47
  })
63
48
 
64
- if (!(hasAmazonSignature(options) || !config.propagationFilter(uri))) {
65
- tracer.inject(span, HTTP_HEADERS, options.headers)
49
+ if (!(hasAmazonSignature(options) || !this.config.propagationFilter(uri))) {
50
+ this.tracer.inject(span, HTTP_HEADERS, options.headers)
66
51
  }
67
52
 
68
- analyticsSampler.sample(span, config.measured)
69
-
70
- callback = scope.bind(callback, childOf)
71
-
72
- const req = scope.bind(request, span).call(this, options, callback)
73
- const emit = req.emit
74
-
75
- req.emit = function (eventName, arg) {
76
- switch (eventName) {
77
- case 'response': {
78
- const res = arg
53
+ analyticsSampler.sample(span, this.config.measured)
54
+ this.enter(span, store)
55
+ })
79
56
 
80
- scope.bind(res)
57
+ this.addSub('apm:http:client:request:end', this.exit.bind(this))
81
58
 
82
- res.on('end', () => finish(req, res, span, config))
59
+ this.addSub('apm:http:client:request:async-end', ({ req, res }) => {
60
+ const span = storage.getStore().span
61
+ if (res) {
62
+ span.setTag(HTTP_STATUS_CODE, res.statusCode)
83
63
 
84
- break
85
- }
86
- case 'error':
87
- addError(span, arg)
88
- case 'abort': // eslint-disable-line no-fallthrough
89
- case 'timeout': // eslint-disable-line no-fallthrough
90
- finish(req, null, span, config)
64
+ if (!this.config.validateStatus(res.statusCode)) {
65
+ span.setTag('error', 1)
91
66
  }
92
67
 
93
- return emit.apply(this, arguments)
94
- }
95
-
96
- scope.bind(req)
97
-
98
- return req
99
- }
100
- }
101
-
102
- function finish (req, res, span, config) {
103
- if (res) {
104
- span.setTag(HTTP_STATUS_CODE, res.statusCode)
105
-
106
- if (!config.validateStatus(res.statusCode)) {
68
+ addResponseHeaders(res, span, this.config)
69
+ } else {
107
70
  span.setTag('error', 1)
108
71
  }
109
72
 
110
- addResponseHeaders(res, span, config)
111
- } else {
112
- span.setTag('error', 1)
113
- }
114
-
115
- addRequestHeaders(req, span, config)
73
+ addRequestHeaders(req, span, this.config)
116
74
 
117
- config.hooks.request(span, req, res)
118
-
119
- span.finish()
120
- }
121
-
122
- function addError (span, error) {
123
- span.addTags({
124
- 'error.type': error.name,
125
- 'error.msg': error.message,
126
- 'error.stack': error.stack
75
+ this.config.hooks.request(span, req, res)
76
+ span.finish()
127
77
  })
128
78
 
129
- return error
130
- }
131
-
132
- function addRequestHeaders (req, span, config) {
133
- config.headers.forEach(key => {
134
- const value = req.getHeader(key)
135
-
136
- if (value) {
137
- span.setTag(`${HTTP_REQUEST_HEADERS}.${key}`, value)
138
- }
139
- })
79
+ this.addSub('apm:http:client:request:error', errorHandler)
140
80
  }
141
81
 
142
- function addResponseHeaders (res, span, config) {
143
- config.headers.forEach(key => {
144
- const value = res.headers[key]
145
-
146
- if (value) {
147
- span.setTag(`${HTTP_RESPONSE_HEADERS}.${key}`, value)
148
- }
149
- })
82
+ configure (config) {
83
+ return super.configure(normalizeClientConfig(config))
150
84
  }
85
+ }
151
86
 
152
- function normalizeArgs (inputURL, inputOptions, cb) {
153
- inputURL = normalizeOptions(inputURL)
154
-
155
- const [callback, inputOptionsNormalized] = normalizeCallback(inputOptions, cb, inputURL)
156
- const options = combineOptions(inputURL, inputOptionsNormalized)
157
- normalizeHeaders(options)
158
- const uri = url.format(options)
87
+ function errorHandler (err) {
88
+ const span = storage.getStore().span
89
+ span.addTags({
90
+ 'error.type': err.name,
91
+ 'error.msg': err.message,
92
+ 'error.stack': err.stack
93
+ })
94
+ }
159
95
 
160
- return { uri, options, callback }
161
- }
96
+ function addResponseHeaders (res, span, config) {
97
+ config.headers.forEach(key => {
98
+ const value = res.headers[key]
162
99
 
163
- function normalizeCallback (inputOptions, callback, inputURL) {
164
- if (typeof inputOptions === 'function') {
165
- return [inputOptions, inputURL || {}]
166
- } else {
167
- return [callback, inputOptions]
100
+ if (value) {
101
+ span.setTag(`${HTTP_RESPONSE_HEADERS}.${key}`, value)
168
102
  }
169
- }
103
+ })
104
+ }
170
105
 
171
- function combineOptions (inputURL, inputOptions) {
172
- if (typeof inputOptions === 'object') {
173
- return Object.assign(inputURL || {}, inputOptions)
174
- } else {
175
- return inputURL
106
+ function addRequestHeaders (req, span, config) {
107
+ config.headers.forEach(key => {
108
+ const value = req.getHeader(key)
109
+
110
+ if (value) {
111
+ span.setTag(`${HTTP_REQUEST_HEADERS}.${key}`, value)
176
112
  }
177
- }
113
+ })
114
+ }
178
115
 
179
- function normalizeHeaders (options) {
180
- options.headers = options.headers || {}
181
- }
116
+ function normalizeClientConfig (config) {
117
+ const validateStatus = getStatusValidator(config)
118
+ const propagationFilter = getFilter({ blocklist: config.propagationBlocklist })
119
+ const headers = getHeaders(config)
120
+ const hooks = getHooks(config)
182
121
 
183
- // https://github.com/nodejs/node/blob/7e911d8b03a838e5ac6bb06c5b313533e89673ef/lib/internal/url.js#L1271
184
- function urlToOptions (url) {
185
- const agent = url.agent || http.globalAgent
186
- const options = {
187
- protocol: url.protocol || agent.protocol,
188
- hostname: typeof url.hostname === 'string' && url.hostname.startsWith('[')
189
- ? url.hostname.slice(1, -1)
190
- : url.hostname ||
191
- url.host ||
192
- 'localhost',
193
- hash: url.hash,
194
- search: url.search,
195
- pathname: url.pathname,
196
- path: `${url.pathname || ''}${url.search || ''}`,
197
- href: url.href
198
- }
199
- if (url.port !== '') {
200
- options.port = Number(url.port)
201
- }
202
- if (url.username || url.password) {
203
- options.auth = `${url.username}:${url.password}`
204
- }
205
- return options
206
- }
122
+ return Object.assign({}, config, {
123
+ validateStatus,
124
+ propagationFilter,
125
+ headers,
126
+ hooks
127
+ })
128
+ }
207
129
 
208
- function normalizeOptions (inputURL) {
209
- if (typeof inputURL === 'string') {
210
- try {
211
- return urlToOptions(new url.URL(inputURL))
212
- } catch (e) {
213
- return url.parse(inputURL)
214
- }
215
- } else if (inputURL instanceof url.URL) {
216
- return urlToOptions(inputURL)
217
- } else {
218
- return inputURL
219
- }
130
+ function getStatusValidator (config) {
131
+ if (typeof config.validateStatus === 'function') {
132
+ return config.validateStatus
133
+ } else if (config.hasOwnProperty('validateStatus')) {
134
+ log.error('Expected `validateStatus` to be a function.')
220
135
  }
136
+ return code => code < 400 || code >= 500
221
137
  }
222
138
 
223
- function getHost (options) {
224
- if (typeof options === 'string') {
225
- return url.parse(options).host
226
- }
139
+ function getFilter (config) {
140
+ config = Object.assign({}, config, {
141
+ blocklist: config.blocklist || []
142
+ })
227
143
 
228
- const hostname = options.hostname || options.host || 'localhost'
229
- const port = options.port
144
+ return urlFilter.getFilter(config)
145
+ }
230
146
 
231
- return [hostname, port].filter(val => val).join(':')
147
+ function getHeaders (config) {
148
+ if (!Array.isArray(config.headers)) return []
149
+
150
+ return config.headers
151
+ .filter(key => typeof key === 'string')
152
+ .map(key => key.toLowerCase())
232
153
  }
233
154
 
234
- function getServiceName (tracer, config, options) {
235
- if (config.splitByDomain) {
236
- return getHost(options)
237
- } else if (config.service) {
238
- return config.service
239
- }
155
+ function getHooks (config) {
156
+ const noop = () => {}
157
+ const request = (config.hooks && config.hooks.request) || noop
240
158
 
241
- return `${tracer._service}-http-client`
159
+ return { request }
242
160
  }
243
161
 
244
162
  function hasAmazonSignature (options) {
@@ -264,87 +182,29 @@ function hasAmazonSignature (options) {
264
182
  return options.path && options.path.toLowerCase().indexOf('x-amz-signature=') !== -1
265
183
  }
266
184
 
267
- function startsWith (searchString) {
268
- return value => String(value).startsWith(searchString)
269
- }
270
-
271
- function unpatch (http) {
272
- this.unwrap(http, 'request')
273
- this.unwrap(http, 'get')
274
- }
275
-
276
- function getStatusValidator (config) {
277
- if (typeof config.validateStatus === 'function') {
278
- return config.validateStatus
279
- } else if (config.hasOwnProperty('validateStatus')) {
280
- log.error('Expected `validateStatus` to be a function.')
185
+ function getServiceName (tracer, config, options) {
186
+ if (config.splitByDomain) {
187
+ return getHost(options)
188
+ } else if (config.service) {
189
+ return config.service
281
190
  }
282
- return code => code < 400 || code >= 500
283
- }
284
-
285
- function getFilter (config) {
286
- config = Object.assign({}, config, {
287
- blocklist: config.blocklist || []
288
- })
289
191
 
290
- return urlFilter.getFilter(config)
192
+ return `${tracer._service}-http-client`
291
193
  }
292
194
 
293
- function normalizeConfig (tracer, config) {
294
- config = config.client || config
295
-
296
- const validateStatus = getStatusValidator(config)
297
- const propagationFilter = getFilter({ blocklist: config.propagationBlocklist })
298
- const headers = getHeaders(config)
299
- const hooks = getHooks(config)
300
-
301
- return Object.assign({}, config, {
302
- validateStatus,
303
- propagationFilter,
304
- headers,
305
- hooks
306
- })
307
- }
195
+ function getHost (options) {
196
+ if (typeof options === 'string') {
197
+ return url.parse(options).host
198
+ }
308
199
 
309
- function getHeaders (config) {
310
- if (!Array.isArray(config.headers)) return []
200
+ const hostname = options.hostname || options.host || 'localhost'
201
+ const port = options.port
311
202
 
312
- return config.headers
313
- .filter(key => typeof key === 'string')
314
- .map(key => key.toLowerCase())
203
+ return [hostname, port].filter(val => val).join(':')
315
204
  }
316
205
 
317
- function getHooks (config) {
318
- const noop = () => {}
319
- const request = (config.hooks && config.hooks.request) || noop
320
-
321
- return { request }
206
+ function startsWith (searchString) {
207
+ return value => String(value).startsWith(searchString)
322
208
  }
323
209
 
324
- module.exports = [
325
- {
326
- name: 'http',
327
- patch: function (http, tracer, config) {
328
- if (config.client === false) return
329
-
330
- patch.call(this, http, 'request', tracer, config)
331
- /**
332
- * References internal to modules, such as `http(s).get` calling
333
- * `http(s).request`, do not use externally patched versions, which is
334
- * why we need to also patch `get` here separately.
335
- */
336
- patch.call(this, http, 'get', tracer, config)
337
- },
338
- unpatch
339
- },
340
- {
341
- name: 'https',
342
- patch: function (http, tracer, config) {
343
- if (config.client === false) return
344
-
345
- patch.call(this, http, 'request', tracer, config)
346
- patch.call(this, http, 'get', tracer, config)
347
- },
348
- unpatch
349
- }
350
- ]
210
+ module.exports = HttpClientPlugin
@@ -1,6 +1,32 @@
1
1
  'use strict'
2
2
 
3
- const client = require('./client')
4
- const server = require('./server')
3
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
4
+ const HttpServerPlugin = require('./server')
5
+ const HttpClientPlugin = require('./client')
5
6
 
6
- module.exports = [].concat(client, server)
7
+ class HttpPlugin extends Plugin {
8
+ static get name () {
9
+ return 'http'
10
+ }
11
+ constructor (...args) {
12
+ super(...args)
13
+ this.server = new HttpServerPlugin(...args)
14
+ this.client = new HttpClientPlugin(...args)
15
+ }
16
+ configure (config) {
17
+ const clientConfig = config.client === false ? false : {
18
+ ...config,
19
+ ...config.client
20
+ }
21
+
22
+ const serverConfig = config.server === false ? false : {
23
+ ...config,
24
+ ...config.server
25
+ }
26
+
27
+ this.server.configure(serverConfig)
28
+ this.client.configure(clientConfig)
29
+ }
30
+ }
31
+
32
+ module.exports = HttpPlugin
@@ -1,47 +1,69 @@
1
1
  'use strict'
2
2
 
3
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
4
+ const { storage } = require('../../datadog-core')
3
5
  const web = require('../../dd-trace/src/plugins/util/web')
4
- const Scope = require('../../dd-trace/src/scope')
5
6
  const { incomingHttpRequestStart } = require('../../dd-trace/src/appsec/gateway/channels')
7
+ const tags = require('../../../ext/tags')
8
+ const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
9
+ const SERVICE_NAME = tags.SERVICE_NAME
6
10
 
7
- function createWrapEmit (tracer, config) {
8
- config = web.normalizeConfig(config)
11
+ class HttpServerPlugin extends Plugin {
12
+ static get name () {
13
+ return 'http'
14
+ }
15
+
16
+ constructor (...args) {
17
+ super(...args)
9
18
 
10
- return function wrapEmit (emit) {
11
- return function emitWithTrace (eventName, req, res) {
12
- if (eventName === 'request') {
13
- return web.instrument(tracer, config, req, res, 'http.request', () => {
14
- if (incomingHttpRequestStart.hasSubscribers) {
15
- incomingHttpRequestStart.publish({ req, res })
16
- }
19
+ this.addSub('apm:http:server:request:start', ({ req, res }) => {
20
+ const store = storage.getStore()
21
+ const span = web.startSpan(this.tracer, this.config, req, res, 'http.request')
17
22
 
18
- return emit.apply(this, arguments)
19
- })
23
+ if (this.config.service) {
24
+ span.setTag(SERVICE_NAME, this.config.service)
20
25
  }
21
26
 
22
- return emit.apply(this, arguments)
23
- }
24
- }
25
- }
27
+ analyticsSampler.sample(span, this.config.measured, true)
28
+ this.enter(span, store)
26
29
 
27
- function plugin (name) {
28
- return {
29
- name,
30
- patch (http, tracer, config) {
31
- if (config.server === false) return
30
+ const context = web.getContext(req)
32
31
 
33
- this.wrap(http.Server.prototype, 'emit', createWrapEmit(tracer, config))
34
- if (http.ServerResponse) { // not present on https
35
- Scope._wrapEmitter(http.ServerResponse.prototype)
32
+ if (!context.instrumented) {
33
+ context.res.writeHead = web.wrapWriteHead(context)
34
+ context.instrumented = true
36
35
  }
37
- },
38
- unpatch (http) {
39
- this.unwrap(http.Server.prototype, 'emit')
40
- }
36
+
37
+ if (incomingHttpRequestStart.hasSubscribers) {
38
+ incomingHttpRequestStart.publish({ req, res })
39
+ }
40
+ })
41
+
42
+ this.addSub('apm:http:server:request:end', () => {
43
+ this.exit()
44
+ })
45
+
46
+ this.addSub('apm:http:server:request:error', (error) => {
47
+ const span = storage.getStore().span
48
+ span.addTags({
49
+ 'error.type': error.name,
50
+ 'error.msg': error.message,
51
+ 'error.stack': error.stack
52
+ })
53
+ })
54
+
55
+ this.addSub('apm:http:server:request:async-end', ({ req }) => {
56
+ const context = web.getContext(req)
57
+
58
+ if (!context) return // Not created by a http.Server instance.
59
+
60
+ web.wrapRes(context, context.req, context.res, context.res.end)()
61
+ })
62
+ }
63
+
64
+ configure (config) {
65
+ return super.configure(web.normalizeConfig(config))
41
66
  }
42
67
  }
43
68
 
44
- module.exports = [
45
- plugin('http'),
46
- plugin('https')
47
- ]
69
+ module.exports = HttpServerPlugin
@@ -56,10 +56,10 @@ function createWrapTeardown (tracer, instrumenter) {
56
56
  }
57
57
 
58
58
  instrumenter.unwrap(this.global.test, 'each')
59
- await new Promise((resolve) => {
60
- tracer._exporter._writer.flush(resolve)
59
+
60
+ return teardown.apply(this, arguments).finally(() => {
61
+ return new Promise(resolve => tracer._exporter._writer.flush(resolve))
61
62
  })
62
- return teardown.apply(this, arguments)
63
63
  }
64
64
  }
65
65
  }
@@ -97,9 +97,11 @@ function createWrapOnException (tracer, globalInput) {
97
97
  if (isActiveSpanFailing && !testStatus) {
98
98
  activeTestSpan.setTag(TEST_STATUS, 'fail')
99
99
  // If we don't do this, jest will show this file on its error message
100
- const stackFrames = err.stack.split('\n')
101
- const filteredStackFrames = stackFrames.filter(frame => !frame.includes(__dirname)).join('\n')
102
- err.stack = filteredStackFrames
100
+ if (err.stack) {
101
+ const stackFrames = err.stack.split('\n')
102
+ const filteredStackFrames = stackFrames.filter(frame => !frame.includes(__dirname)).join('\n')
103
+ err.stack = filteredStackFrames
104
+ }
103
105
  activeTestSpan.setTag('error', err)
104
106
  // need to manually finish, as it will not be caught in `itWithTrace`
105
107
  activeTestSpan.finish()