dd-trace 2.3.0 → 2.4.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.
Files changed (52) 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-aws-sdk/src/services/sns.js +2 -2
  23. package/packages/datadog-plugin-cassandra-driver/src/index.js +52 -224
  24. package/packages/datadog-plugin-cucumber/src/index.js +3 -1
  25. package/packages/datadog-plugin-http/src/client.js +111 -254
  26. package/packages/datadog-plugin-http/src/index.js +29 -3
  27. package/packages/datadog-plugin-http/src/server.js +49 -32
  28. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +5 -3
  29. package/packages/datadog-plugin-mocha/src/index.js +96 -207
  30. package/packages/datadog-plugin-mongodb-core/src/index.js +119 -3
  31. package/packages/datadog-plugin-net/src/index.js +65 -121
  32. package/packages/datadog-plugin-next/src/index.js +10 -10
  33. package/packages/datadog-plugin-pg/src/index.js +32 -69
  34. package/packages/datadog-plugin-rhea/src/index.js +59 -225
  35. package/packages/datadog-plugin-tedious/src/index.js +38 -86
  36. package/packages/dd-trace/lib/version.js +1 -1
  37. package/packages/dd-trace/src/appsec/recommended.json +137 -116
  38. package/packages/dd-trace/src/config.js +6 -0
  39. package/packages/dd-trace/src/iitm.js +5 -1
  40. package/packages/dd-trace/src/loader.js +6 -4
  41. package/packages/dd-trace/src/noop/tracer.js +4 -0
  42. package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -1
  43. package/packages/dd-trace/src/opentracing/span.js +34 -0
  44. package/packages/dd-trace/src/plugin_manager.js +4 -0
  45. package/packages/dd-trace/src/plugins/plugin.js +3 -1
  46. package/packages/dd-trace/src/plugins/util/web.js +99 -93
  47. package/packages/dd-trace/src/proxy.js +4 -0
  48. package/packages/dd-trace/src/ritm.js +60 -25
  49. package/packages/dd-trace/src/tracer.js +16 -0
  50. package/packages/datadog-plugin-mongodb-core/src/legacy.js +0 -59
  51. package/packages/datadog-plugin-mongodb-core/src/unified.js +0 -138
  52. package/packages/datadog-plugin-mongodb-core/src/util.js +0 -143
@@ -1,40 +1,30 @@
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()
27
+ this.config = normalizeClientConfig(this.config)
38
28
  const options = args.options
39
29
  const agent = options.agent || options._defaultAgent || http.globalAgent
40
30
  const protocol = options.protocol || agent.protocol || 'http:'
@@ -43,17 +33,13 @@ function patch (http, methodName, tracer, config) {
43
33
  const path = options.path ? options.path.split(/[?#]/)[0] : '/'
44
34
  const uri = `${protocol}//${host}${path}`
45
35
 
46
- let callback = args.callback
47
-
48
36
  const method = (options.method || 'GET').toUpperCase()
49
-
50
- const scope = tracer.scope()
51
- const childOf = scope.active()
52
- const span = tracer.startSpan('http.request', {
37
+ const childOf = store ? store.span : store
38
+ const span = this.tracer.startSpan('http.request', {
53
39
  childOf,
54
40
  tags: {
55
- [SPAN_KIND]: CLIENT,
56
- 'service.name': getServiceName(tracer, config, options),
41
+ 'span.kind': 'client',
42
+ 'service.name': getServiceName(this.tracer, this.config, options),
57
43
  'resource.name': method,
58
44
  'span.type': 'http',
59
45
  'http.method': method,
@@ -61,184 +47,113 @@ function patch (http, methodName, tracer, config) {
61
47
  }
62
48
  })
63
49
 
64
- if (!(hasAmazonSignature(options) || !config.propagationFilter(uri))) {
65
- tracer.inject(span, HTTP_HEADERS, options.headers)
50
+ if (!(hasAmazonSignature(options) || !this.config.propagationFilter(uri))) {
51
+ this.tracer.inject(span, HTTP_HEADERS, options.headers)
66
52
  }
67
53
 
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
54
+ analyticsSampler.sample(span, this.config.measured)
55
+ this.enter(span, store)
56
+ })
79
57
 
80
- scope.bind(res)
58
+ this.addSub('apm:http:client:request:end', this.exit.bind(this))
81
59
 
82
- res.on('end', () => finish(req, res, span, config))
60
+ this.addSub('apm:http:client:request:async-end', ({ req, res }) => {
61
+ const span = storage.getStore().span
62
+ if (res) {
63
+ span.setTag(HTTP_STATUS_CODE, res.statusCode)
83
64
 
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)
65
+ if (!this.config.validateStatus(res.statusCode)) {
66
+ span.setTag('error', 1)
91
67
  }
92
68
 
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)) {
69
+ addResponseHeaders(res, span, this.config)
70
+ } else {
107
71
  span.setTag('error', 1)
108
72
  }
109
73
 
110
- addResponseHeaders(res, span, config)
111
- } else {
112
- span.setTag('error', 1)
113
- }
114
-
115
- addRequestHeaders(req, span, config)
116
-
117
- config.hooks.request(span, req, res)
74
+ addRequestHeaders(req, span, this.config)
118
75
 
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
76
+ this.config.hooks.request(span, req, res)
77
+ span.finish()
127
78
  })
128
79
 
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
- })
140
- }
141
-
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
- })
80
+ this.addSub('apm:http:client:request:error', errorHandler)
150
81
  }
82
+ }
151
83
 
152
- function normalizeArgs (inputURL, inputOptions, cb) {
153
- inputURL = normalizeOptions(inputURL)
84
+ function errorHandler (err) {
85
+ const span = storage.getStore().span
86
+ span.addTags({
87
+ 'error.type': err.name,
88
+ 'error.msg': err.message,
89
+ 'error.stack': err.stack
90
+ })
91
+ }
154
92
 
155
- const [callback, inputOptionsNormalized] = normalizeCallback(inputOptions, cb, inputURL)
156
- const options = combineOptions(inputURL, inputOptionsNormalized)
157
- normalizeHeaders(options)
158
- const uri = url.format(options)
93
+ function addResponseHeaders (res, span, config) {
94
+ config.headers.forEach(key => {
95
+ const value = res.headers[key]
159
96
 
160
- return { uri, options, callback }
161
- }
162
-
163
- function normalizeCallback (inputOptions, callback, inputURL) {
164
- if (typeof inputOptions === 'function') {
165
- return [inputOptions, inputURL || {}]
166
- } else {
167
- return [callback, inputOptions]
97
+ if (value) {
98
+ span.setTag(`${HTTP_RESPONSE_HEADERS}.${key}`, value)
168
99
  }
169
- }
100
+ })
101
+ }
102
+
103
+ function addRequestHeaders (req, span, config) {
104
+ config.headers.forEach(key => {
105
+ const value = req.getHeader(key)
170
106
 
171
- function combineOptions (inputURL, inputOptions) {
172
- if (typeof inputOptions === 'object') {
173
- return Object.assign(inputURL || {}, inputOptions)
174
- } else {
175
- return inputURL
107
+ if (value) {
108
+ span.setTag(`${HTTP_REQUEST_HEADERS}.${key}`, value)
176
109
  }
177
- }
110
+ })
111
+ }
178
112
 
179
- function normalizeHeaders (options) {
180
- options.headers = options.headers || {}
181
- }
113
+ function normalizeClientConfig (config) {
114
+ const validateStatus = getStatusValidator(config)
115
+ const propagationFilter = getFilter({ blocklist: config.propagationBlocklist })
116
+ const headers = getHeaders(config)
117
+ const hooks = getHooks(config)
182
118
 
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
- }
119
+ return Object.assign({}, config, {
120
+ validateStatus,
121
+ propagationFilter,
122
+ headers,
123
+ hooks
124
+ })
125
+ }
207
126
 
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
- }
127
+ function getStatusValidator (config) {
128
+ if (typeof config.validateStatus === 'function') {
129
+ return config.validateStatus
130
+ } else if (config.hasOwnProperty('validateStatus')) {
131
+ log.error('Expected `validateStatus` to be a function.')
220
132
  }
133
+ return code => code < 400 || code >= 500
221
134
  }
222
135
 
223
- function getHost (options) {
224
- if (typeof options === 'string') {
225
- return url.parse(options).host
226
- }
136
+ function getFilter (config) {
137
+ config = Object.assign({}, config, {
138
+ blocklist: config.blocklist || []
139
+ })
227
140
 
228
- const hostname = options.hostname || options.host || 'localhost'
229
- const port = options.port
141
+ return urlFilter.getFilter(config)
142
+ }
230
143
 
231
- return [hostname, port].filter(val => val).join(':')
144
+ function getHeaders (config) {
145
+ if (!Array.isArray(config.headers)) return []
146
+
147
+ return config.headers
148
+ .filter(key => typeof key === 'string')
149
+ .map(key => key.toLowerCase())
232
150
  }
233
151
 
234
- function getServiceName (tracer, config, options) {
235
- if (config.splitByDomain) {
236
- return getHost(options)
237
- } else if (config.service) {
238
- return config.service
239
- }
152
+ function getHooks (config) {
153
+ const noop = () => {}
154
+ const request = (config.hooks && config.hooks.request) || noop
240
155
 
241
- return `${tracer._service}-http-client`
156
+ return { request }
242
157
  }
243
158
 
244
159
  function hasAmazonSignature (options) {
@@ -264,87 +179,29 @@ function hasAmazonSignature (options) {
264
179
  return options.path && options.path.toLowerCase().indexOf('x-amz-signature=') !== -1
265
180
  }
266
181
 
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.')
182
+ function getServiceName (tracer, config, options) {
183
+ if (config.splitByDomain) {
184
+ return getHost(options)
185
+ } else if (config.service) {
186
+ return config.service
281
187
  }
282
- return code => code < 400 || code >= 500
283
- }
284
188
 
285
- function getFilter (config) {
286
- config = Object.assign({}, config, {
287
- blocklist: config.blocklist || []
288
- })
289
-
290
- return urlFilter.getFilter(config)
189
+ return `${tracer._service}-http-client`
291
190
  }
292
191
 
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
- }
192
+ function getHost (options) {
193
+ if (typeof options === 'string') {
194
+ return url.parse(options).host
195
+ }
308
196
 
309
- function getHeaders (config) {
310
- if (!Array.isArray(config.headers)) return []
197
+ const hostname = options.hostname || options.host || 'localhost'
198
+ const port = options.port
311
199
 
312
- return config.headers
313
- .filter(key => typeof key === 'string')
314
- .map(key => key.toLowerCase())
200
+ return [hostname, port].filter(val => val).join(':')
315
201
  }
316
202
 
317
- function getHooks (config) {
318
- const noop = () => {}
319
- const request = (config.hooks && config.hooks.request) || noop
320
-
321
- return { request }
203
+ function startsWith (searchString) {
204
+ return value => String(value).startsWith(searchString)
322
205
  }
323
206
 
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
- ]
207
+ 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,64 @@
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)
18
+
19
+ this.addSub('apm:http:server:request:start', ({ req, res }) => {
20
+ const store = storage.getStore()
21
+ this.config = web.normalizeConfig(this.config)
9
22
 
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
- }
23
+ const span = web.startSpan(this.tracer, this.config, req, res, 'http.request')
17
24
 
18
- return emit.apply(this, arguments)
19
- })
25
+ if (this.config.service) {
26
+ span.setTag(SERVICE_NAME, this.config.service)
20
27
  }
21
28
 
22
- return emit.apply(this, arguments)
23
- }
24
- }
25
- }
29
+ analyticsSampler.sample(span, this.config.measured, true)
30
+ this.enter(span, store)
26
31
 
27
- function plugin (name) {
28
- return {
29
- name,
30
- patch (http, tracer, config) {
31
- if (config.server === false) return
32
+ const context = web.getContext(req)
32
33
 
33
- this.wrap(http.Server.prototype, 'emit', createWrapEmit(tracer, config))
34
- if (http.ServerResponse) { // not present on https
35
- Scope._wrapEmitter(http.ServerResponse.prototype)
34
+ if (!context.instrumented) {
35
+ context.res.writeHead = web.wrapWriteHead(context)
36
+ context.instrumented = true
36
37
  }
37
- },
38
- unpatch (http) {
39
- this.unwrap(http.Server.prototype, 'emit')
40
- }
38
+
39
+ if (incomingHttpRequestStart.hasSubscribers) {
40
+ incomingHttpRequestStart.publish({ req, res })
41
+ }
42
+ })
43
+
44
+ this.addSub('apm:http:server:request:end', () => {
45
+ this.exit()
46
+ })
47
+
48
+ this.addSub('apm:http:server:request:error', (error) => {
49
+ const span = storage.getStore().span
50
+ span.addTags({
51
+ 'error.type': error.name,
52
+ 'error.msg': error.message,
53
+ 'error.stack': error.stack
54
+ })
55
+ })
56
+
57
+ this.addSub('apm:http:server:request:async-end', ({ req }) => {
58
+ const context = web.getContext(req)
59
+ web.wrapRes(context, context.req, context.res, context.res.end)()
60
+ })
41
61
  }
42
62
  }
43
63
 
44
- module.exports = [
45
- plugin('http'),
46
- plugin('https')
47
- ]
64
+ module.exports = HttpServerPlugin
@@ -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()