dd-trace 2.4.2 → 2.6.0

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/LICENSE-3rdparty.csv +1 -0
  2. package/ci/init.js +6 -0
  3. package/ci/jest/env.js +16 -3
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +17 -1
  7. package/package.json +5 -4
  8. package/packages/datadog-instrumentations/index.js +1 -0
  9. package/packages/datadog-instrumentations/src/cypress.js +8 -0
  10. package/packages/datadog-instrumentations/src/jest.js +170 -0
  11. package/packages/datadog-plugin-aws-sdk/src/helpers.js +4 -4
  12. package/packages/datadog-plugin-aws-sdk/src/index.js +1 -1
  13. package/packages/datadog-plugin-cucumber/src/index.js +16 -16
  14. package/packages/datadog-plugin-cypress/src/index.js +10 -5
  15. package/packages/datadog-plugin-cypress/src/plugin.js +18 -17
  16. package/packages/datadog-plugin-fs/src/index.js +2 -0
  17. package/packages/datadog-plugin-http/src/server.js +0 -8
  18. package/packages/datadog-plugin-jest/src/index.js +101 -3
  19. package/packages/datadog-plugin-jest/src/util.js +1 -29
  20. package/packages/datadog-plugin-mocha/src/index.js +14 -15
  21. package/packages/dd-trace/lib/version.js +1 -1
  22. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +29 -12
  23. package/packages/dd-trace/src/appsec/index.js +7 -3
  24. package/packages/dd-trace/src/appsec/recommended.json +15 -5
  25. package/packages/dd-trace/src/appsec/reporter.js +29 -3
  26. package/packages/dd-trace/src/appsec/rule_manager.js +2 -2
  27. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +32 -0
  28. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +51 -0
  29. package/packages/dd-trace/src/config.js +33 -4
  30. package/packages/dd-trace/src/encode/0.4.js +0 -1
  31. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +193 -0
  32. package/packages/dd-trace/src/encode/tags-processors.js +116 -0
  33. package/packages/dd-trace/src/exporter.js +3 -0
  34. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  35. package/packages/dd-trace/src/exporters/agent/writer.js +7 -32
  36. package/packages/dd-trace/src/exporters/{agent → common}/docker.js +0 -0
  37. package/packages/dd-trace/src/exporters/common/request.js +83 -0
  38. package/packages/dd-trace/src/exporters/common/writer.js +36 -0
  39. package/packages/dd-trace/src/exporters/{agent/scheduler.js → scheduler.js} +0 -0
  40. package/packages/dd-trace/src/format.js +9 -5
  41. package/packages/dd-trace/src/instrumenter.js +3 -0
  42. package/packages/dd-trace/src/pkg.js +11 -6
  43. package/packages/dd-trace/src/plugins/util/test.js +79 -1
  44. package/packages/dd-trace/src/plugins/util/web.js +11 -10
  45. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  46. package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -1
  47. package/packages/dd-trace/src/proxy.js +2 -0
  48. package/packages/dd-trace/src/span_processor.js +4 -1
  49. package/packages/dd-trace/src/telemetry.js +187 -0
  50. package/scripts/install_plugin_modules.js +1 -0
  51. package/packages/datadog-plugin-jest/src/jest-environment.js +0 -272
  52. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +0 -185
  53. package/packages/dd-trace/src/exporters/agent/request.js +0 -86
@@ -1,4 +1,7 @@
1
1
  const path = require('path')
2
+ const fs = require('fs')
3
+
4
+ const ignore = require('ignore')
2
5
 
3
6
  const { getGitMetadata } = require('./git')
4
7
  const { getUserProviderGitMetadata } = require('./user-provided-git')
@@ -16,6 +19,10 @@ const {
16
19
  } = require('./tags')
17
20
  const id = require('../../id')
18
21
 
22
+ const { SPAN_TYPE, RESOURCE_NAME, SAMPLING_PRIORITY } = require('../../../../../ext/tags')
23
+ const { SAMPLING_RULE_DECISION } = require('../../constants')
24
+ const { AUTO_KEEP } = require('../../../../../ext/priority')
25
+
19
26
  const TEST_FRAMEWORK = 'test.framework'
20
27
  const TEST_FRAMEWORK_VERSION = 'test.framework_version'
21
28
  const TEST_TYPE = 'test.type'
@@ -25,6 +32,7 @@ const TEST_STATUS = 'test.status'
25
32
  const TEST_PARAMETERS = 'test.parameters'
26
33
  const TEST_SKIP_REASON = 'test.skip_reason'
27
34
  const TEST_IS_RUM_ACTIVE = 'test.is_rum_active'
35
+ const TEST_CODE_OWNERS = 'test.codeowners'
28
36
 
29
37
  const ERROR_TYPE = 'error.type'
30
38
  const ERROR_MESSAGE = 'error.msg'
@@ -35,6 +43,7 @@ const CI_APP_ORIGIN = 'ciapp-test'
35
43
  const JEST_TEST_RUNNER = 'test.jest.test_runner'
36
44
 
37
45
  module.exports = {
46
+ TEST_CODE_OWNERS,
38
47
  TEST_FRAMEWORK,
39
48
  TEST_FRAMEWORK_VERSION,
40
49
  JEST_TEST_RUNNER,
@@ -53,7 +62,10 @@ module.exports = {
53
62
  getTestParametersString,
54
63
  finishAllTraceSpans,
55
64
  getTestParentSpan,
56
- getTestSuitePath
65
+ getTestSuitePath,
66
+ getCodeOwnersFileEntries,
67
+ getCodeOwnersForFilename,
68
+ getTestCommonTags
57
69
  }
58
70
 
59
71
  function getTestEnvironmentMetadata (testFramework, config) {
@@ -127,6 +139,20 @@ function getTestParentSpan (tracer) {
127
139
  'x-datadog-parent-id': '0000000000000000'
128
140
  })
129
141
  }
142
+
143
+ function getTestCommonTags (name, suite, version) {
144
+ return {
145
+ [SPAN_TYPE]: 'test',
146
+ [TEST_TYPE]: 'test',
147
+ [SAMPLING_RULE_DECISION]: 1,
148
+ [SAMPLING_PRIORITY]: AUTO_KEEP,
149
+ [TEST_NAME]: name,
150
+ [TEST_SUITE]: suite,
151
+ [RESOURCE_NAME]: `${suite}.${name}`,
152
+ [TEST_FRAMEWORK_VERSION]: version
153
+ }
154
+ }
155
+
130
156
  /**
131
157
  * We want to make sure that test suites are reported the same way for
132
158
  * every OS, so we replace `path.sep` by `/`
@@ -140,3 +166,55 @@ function getTestSuitePath (testSuiteAbsolutePath, sourceRoot) {
140
166
 
141
167
  return testSuitePath.replace(path.sep, '/')
142
168
  }
169
+
170
+ const POSSIBLE_CODEOWNERS_LOCATIONS = [
171
+ 'CODEOWNERS',
172
+ '.github/CODEOWNERS',
173
+ 'docs/CODEOWNERS',
174
+ '.gitlab/CODEOWNERS'
175
+ ]
176
+
177
+ function getCodeOwnersFileEntries (rootDir = process.cwd()) {
178
+ let codeOwnersContent
179
+
180
+ POSSIBLE_CODEOWNERS_LOCATIONS.forEach(location => {
181
+ try {
182
+ codeOwnersContent = fs.readFileSync(`${rootDir}/${location}`).toString()
183
+ } catch (e) {
184
+ // retry with next path
185
+ }
186
+ })
187
+ if (!codeOwnersContent) {
188
+ return null
189
+ }
190
+
191
+ const entries = []
192
+ const lines = codeOwnersContent.split('\n')
193
+
194
+ for (const line of lines) {
195
+ const [content] = line.split('#')
196
+ const trimmed = content.trim()
197
+ if (trimmed === '') continue
198
+ const [pattern, ...owners] = trimmed.split(/\s+/)
199
+ entries.push({ pattern, owners })
200
+ }
201
+ // Reverse because rules defined last take precedence
202
+ return entries.reverse()
203
+ }
204
+
205
+ function getCodeOwnersForFilename (filename, entries) {
206
+ if (!entries) {
207
+ return null
208
+ }
209
+ for (const entry of entries) {
210
+ try {
211
+ const isResponsible = ignore().add(entry.pattern).ignores(filename)
212
+ if (isResponsible) {
213
+ return JSON.stringify(entry.owners)
214
+ }
215
+ } catch (e) {
216
+ return null
217
+ }
218
+ }
219
+ return null
220
+ }
@@ -69,6 +69,17 @@ const web = {
69
69
  context.span = span
70
70
  context.res = res
71
71
 
72
+ if (!config.filter(req.url)) {
73
+ span.setTag(MANUAL_DROP, true)
74
+ span.context()._trace.isRecording = false
75
+ }
76
+
77
+ if (config.service) {
78
+ span.setTag(SERVICE_NAME, config.service)
79
+ }
80
+
81
+ analyticsSampler.sample(span, config.measured, true)
82
+
72
83
  return span
73
84
  },
74
85
  wrap (req) {
@@ -83,16 +94,6 @@ const web = {
83
94
  instrument (tracer, config, req, res, name, callback) {
84
95
  const span = this.startSpan(tracer, config, req, res, name)
85
96
 
86
- if (!config.filter(req.url)) {
87
- span.setTag(MANUAL_DROP, true)
88
- }
89
-
90
- if (config.service) {
91
- span.setTag(SERVICE_NAME, config.service)
92
- }
93
-
94
- analyticsSampler.sample(span, config.measured, true)
95
-
96
97
  this.wrap(req)
97
98
 
98
99
  return callback && tracer.scope().activate(span, () => callback(span))
@@ -5,7 +5,7 @@ const { request } = require('http')
5
5
  const FormData = require('form-data')
6
6
 
7
7
  // TODO: avoid using dd-trace internals. Make this a separate module?
8
- const docker = require('../../exporters/agent/docker')
8
+ const docker = require('../../exporters/common/docker')
9
9
  const version = require('../../../lib/version')
10
10
 
11
11
  const containerId = docker.id()
@@ -3,7 +3,7 @@
3
3
  class NativeCpuProfiler {
4
4
  constructor (options = {}) {
5
5
  this.type = 'wall'
6
- this._samplingInterval = options.samplingInterval || 10 * 1000
6
+ this._samplingInterval = options.samplingInterval || 1e6 / 99 // 99hz
7
7
  this._mapper = undefined
8
8
  this._pprof = undefined
9
9
  }
@@ -10,6 +10,7 @@ const metrics = require('./metrics')
10
10
  const log = require('./log')
11
11
  const { isFalse } = require('./util')
12
12
  const { setStartupLogInstrumenter } = require('./startup-log')
13
+ const telemetry = require('./telemetry')
13
14
 
14
15
  const noop = new NoopTracer()
15
16
 
@@ -63,6 +64,7 @@ class Tracer extends BaseTracer {
63
64
  this._instrumenter.enable(config)
64
65
  this._pluginManager.configure(config)
65
66
  setStartupLogInstrumenter(this._instrumenter)
67
+ telemetry.start(config, this._instrumenter, this._pluginManager)
66
68
  }
67
69
  } catch (e) {
68
70
  log.error(e)
@@ -32,7 +32,10 @@ class SpanProcessor {
32
32
  }
33
33
  }
34
34
 
35
- this._exporter.export(formatted)
35
+ if (formatted.length !== 0 && trace.isRecording !== false) {
36
+ this._exporter.export(formatted)
37
+ }
38
+
36
39
  this._erase(trace, active)
37
40
  }
38
41
  }
@@ -0,0 +1,187 @@
1
+ 'use strict'
2
+
3
+ const tracerVersion = require('../lib/version')
4
+ const pkg = require('./pkg')
5
+ const containerId = require('./exporters/common/docker').id()
6
+ const requirePackageJson = require('./require-package-json')
7
+ const path = require('path')
8
+ const os = require('os')
9
+ const request = require('./exporters/common/request')
10
+
11
+ let config
12
+ let instrumenter
13
+ let pluginManager
14
+
15
+ let seqId = 0
16
+ let application
17
+ let host
18
+ let interval
19
+ const sentIntegrations = new Set()
20
+
21
+ function getIntegrations () {
22
+ const newIntegrations = []
23
+ for (const plugin of instrumenter._instrumented.keys()) {
24
+ if (sentIntegrations.has(plugin.name)) {
25
+ continue
26
+ }
27
+ newIntegrations.push({
28
+ name: plugin.name,
29
+ enabled: true,
30
+ auto_enabled: true
31
+ })
32
+ sentIntegrations.add(plugin.name)
33
+ }
34
+ for (const pluginName in pluginManager._pluginsByName) {
35
+ if (sentIntegrations.has(pluginName)) {
36
+ continue
37
+ }
38
+ newIntegrations.push({
39
+ name: pluginName,
40
+ enabled: pluginManager._pluginsByName[pluginName]._enabled,
41
+ auto_enabled: true
42
+ })
43
+ sentIntegrations.add(pluginName)
44
+ }
45
+ return newIntegrations
46
+ }
47
+
48
+ function getDependencies () {
49
+ const deps = []
50
+ const { dependencies } = pkg
51
+ if (!dependencies) {
52
+ return deps
53
+ }
54
+ const rootDir = pkg.findRoot()
55
+ for (const [name, version] of Object.entries(dependencies)) {
56
+ const dep = { name }
57
+ try {
58
+ dep.version = requirePackageJson(
59
+ path.join(rootDir, 'node_modules', name.replace('/', path.sep))
60
+ ).version
61
+ } catch (e) {
62
+ dep.version = version
63
+ }
64
+ deps.push(dep)
65
+ }
66
+ return deps
67
+ }
68
+
69
+ function flatten (input, result = [], prefix = [], traversedObjects = null) {
70
+ traversedObjects = traversedObjects || new WeakSet()
71
+ if (traversedObjects.has(input)) {
72
+ return
73
+ }
74
+ traversedObjects.add(input)
75
+ for (const [key, value] of Object.entries(input)) {
76
+ if (typeof value === 'object' && value !== null) {
77
+ flatten(value, result, [...prefix, key], traversedObjects)
78
+ } else {
79
+ result.push({ name: [...prefix, key].join('.'), value })
80
+ }
81
+ }
82
+ return result
83
+ }
84
+
85
+ function appStarted () {
86
+ return {
87
+ integrations: getIntegrations(),
88
+ dependencies: getDependencies(),
89
+ configuration: flatten(config),
90
+ additional_payload: []
91
+ }
92
+ }
93
+
94
+ function onBeforeExit () {
95
+ process.removeListener('beforeExit', onBeforeExit)
96
+ sendData('app-closing')
97
+ }
98
+
99
+ function createAppObject () {
100
+ return {
101
+ service_name: config.service,
102
+ env: config.env,
103
+ service_version: config.version,
104
+ tracer_version: tracerVersion,
105
+ language_name: 'nodejs',
106
+ language_version: process.versions.node
107
+ }
108
+ }
109
+
110
+ function createHostObject () {
111
+ return {
112
+ hostname: os.hostname(), // TODO is this enough?
113
+ container_id: containerId
114
+ }
115
+ }
116
+
117
+ function sendData (reqType, payload = {}) {
118
+ const {
119
+ hostname,
120
+ port
121
+ } = config
122
+ const options = {
123
+ hostname,
124
+ port,
125
+ method: 'POST',
126
+ path: '/telemetry/proxy/api/v2/apmtelemetry',
127
+ headers: {
128
+ 'content-type': 'application/json',
129
+ 'dd-telemetry-api-version': 'v1',
130
+ 'dd-telemetry-request-type': reqType
131
+ }
132
+ }
133
+ const data = JSON.stringify({
134
+ api_version: 'v1',
135
+ request_type: reqType,
136
+ tracer_time: Math.floor(Date.now() / 1000),
137
+ runtime_id: config.tags['runtime-id'],
138
+ seq_id: ++seqId,
139
+ payload,
140
+ application,
141
+ host
142
+ })
143
+
144
+ request(data, options, true, () => {
145
+ // ignore errors
146
+ })
147
+ }
148
+
149
+ function start (aConfig, theInstrumenter, thePluginManager) {
150
+ if (!aConfig.telemetryEnabled) {
151
+ return
152
+ }
153
+ config = aConfig
154
+ instrumenter = theInstrumenter
155
+ pluginManager = thePluginManager
156
+ application = createAppObject()
157
+ host = createHostObject()
158
+ sendData('app-started', appStarted())
159
+ interval = setInterval(() => sendData('app-heartbeat'), 60000)
160
+ interval.unref()
161
+ process.on('beforeExit', onBeforeExit)
162
+ }
163
+
164
+ function stop () {
165
+ if (!config) {
166
+ return
167
+ }
168
+ clearInterval(interval)
169
+ process.removeListener('beforeExit', onBeforeExit)
170
+ }
171
+
172
+ function updateIntegrations () {
173
+ if (!config || !config.telemetryEnabled) {
174
+ return
175
+ }
176
+ const integrations = getIntegrations()
177
+ if (integrations.length === 0) {
178
+ return
179
+ }
180
+ sendData('app-integrations-change', { integrations })
181
+ }
182
+
183
+ module.exports = {
184
+ start,
185
+ stop,
186
+ updateIntegrations
187
+ }
@@ -186,6 +186,7 @@ const requirePackageJson = require('${requirePackageJsonPath}')
186
186
 
187
187
  module.exports = {
188
188
  get (id) { return require(id || '${name}') },
189
+ getPath (id) { return require.resolve(id || '${name}' ) },
189
190
  version () { return requirePackageJson('${name}', module).version }
190
191
  }
191
192
  `
@@ -1,272 +0,0 @@
1
- const { promisify } = require('util')
2
-
3
- const { RESOURCE_NAME } = require('../../../ext/tags')
4
- const {
5
- TEST_NAME,
6
- TEST_SUITE,
7
- TEST_STATUS,
8
- TEST_FRAMEWORK_VERSION,
9
- JEST_TEST_RUNNER,
10
- ERROR_MESSAGE,
11
- ERROR_TYPE,
12
- TEST_PARAMETERS,
13
- CI_APP_ORIGIN,
14
- getTestEnvironmentMetadata,
15
- getTestParametersString,
16
- finishAllTraceSpans,
17
- getTestSuitePath
18
- } = require('../../dd-trace/src/plugins/util/test')
19
- const {
20
- getFormattedJestTestParameters,
21
- getTestSpanTags,
22
- setSuppressedErrors
23
- } = require('./util')
24
-
25
- const originals = new WeakMap()
26
-
27
- function getVmContext (environment) {
28
- if (typeof environment.getVmContext === 'function') {
29
- return environment.getVmContext()
30
- }
31
- return null
32
- }
33
-
34
- function wrapEnvironment (BaseEnvironment) {
35
- return class DatadogJestEnvironment extends BaseEnvironment {
36
- constructor (config, context) {
37
- super(config, context)
38
- this.testSuite = getTestSuitePath(context.testPath, config.rootDir)
39
- this.testSpansByTestName = {}
40
- this.originalTestFnByTestName = {}
41
- }
42
- }
43
- }
44
-
45
- function createWrapTeardown (tracer, instrumenter) {
46
- return function wrapTeardown (teardown) {
47
- return async function teardownWithTrace () {
48
- instrumenter.unwrap(this.global.test, 'each')
49
- nameToParams = {}
50
- // for jest-jasmine2
51
- if (this.global.jasmine) {
52
- instrumenter.unwrap(this.global.jasmine.Spec.prototype, 'onException')
53
- instrumenter.unwrap(this.global, 'it')
54
- instrumenter.unwrap(this.global, 'fit')
55
- instrumenter.unwrap(this.global, 'xit')
56
- }
57
-
58
- instrumenter.unwrap(this.global.test, 'each')
59
-
60
- return teardown.apply(this, arguments).finally(() => {
61
- return new Promise(resolve => tracer._exporter._writer.flush(resolve))
62
- })
63
- }
64
- }
65
- }
66
-
67
- let nameToParams = {}
68
-
69
- const isTimeout = (event) => {
70
- return event.error &&
71
- typeof event.error === 'string' &&
72
- event.error.startsWith('Exceeded timeout')
73
- }
74
-
75
- function createHandleTestEvent (tracer, testEnvironmentMetadata, instrumenter) {
76
- return async function handleTestEventWithTrace (event) {
77
- if (event.name === 'test_retry') {
78
- let testName = event.test && event.test.name
79
- const context = getVmContext(this)
80
- if (context) {
81
- const { currentTestName } = context.expect.getState()
82
- testName = currentTestName
83
- }
84
- // If it's a retry, we restore the original test function so that it is not wrapped again
85
- if (this.originalTestFnByTestName[testName]) {
86
- event.test.fn = this.originalTestFnByTestName[testName]
87
- }
88
- return
89
- }
90
- if (event.name === 'test_fn_failure') {
91
- if (!isTimeout(event)) {
92
- return
93
- }
94
- const context = getVmContext(this)
95
- if (context) {
96
- const { currentTestName } = context.expect.getState()
97
- const testSpan = this.testSpansByTestName[`${currentTestName}_${event.test.invocations}`]
98
- if (testSpan) {
99
- testSpan.setTag(ERROR_TYPE, 'Timeout')
100
- testSpan.setTag(ERROR_MESSAGE, event.error)
101
- testSpan.setTag(TEST_STATUS, 'fail')
102
- }
103
- }
104
- return
105
- }
106
- if (event.name === 'setup') {
107
- instrumenter.wrap(this.global.test, 'each', function (original) {
108
- return function () {
109
- const testParameters = getFormattedJestTestParameters(arguments)
110
- const eachBind = original.apply(this, arguments)
111
- return function () {
112
- const [testName] = arguments
113
- nameToParams[testName] = testParameters
114
- return eachBind.apply(this, arguments)
115
- }
116
- }
117
- })
118
- return
119
- }
120
-
121
- if (event.name !== 'test_skip' &&
122
- event.name !== 'test_todo' &&
123
- event.name !== 'test_start' &&
124
- event.name !== 'hook_failure') {
125
- return
126
- }
127
- // for hook_failure events the test entry might not be defined, because the hook
128
- // is not necessarily associated to a test:
129
- if (!event.test) {
130
- return
131
- }
132
-
133
- const { childOf, commonSpanTags } = getTestSpanTags(tracer, testEnvironmentMetadata)
134
-
135
- let testName = event.test.name
136
- const context = getVmContext(this)
137
-
138
- if (context) {
139
- const { currentTestName } = context.expect.getState()
140
- testName = currentTestName
141
- }
142
- const spanTags = {
143
- ...commonSpanTags,
144
- [TEST_NAME]: testName,
145
- [TEST_SUITE]: this.testSuite,
146
- [TEST_FRAMEWORK_VERSION]: tracer._version,
147
- [JEST_TEST_RUNNER]: 'jest-circus'
148
- }
149
-
150
- const testParametersString = getTestParametersString(nameToParams, event.test.name)
151
- if (testParametersString) {
152
- spanTags[TEST_PARAMETERS] = testParametersString
153
- }
154
-
155
- const resource = `${this.testSuite}.${testName}`
156
- if (event.name === 'test_skip' || event.name === 'test_todo') {
157
- const testSpan = tracer.startSpan(
158
- 'jest.test',
159
- {
160
- childOf,
161
- tags: {
162
- ...spanTags,
163
- [RESOURCE_NAME]: resource,
164
- [TEST_STATUS]: 'skip'
165
- }
166
- }
167
- )
168
- testSpan.context()._trace.origin = CI_APP_ORIGIN
169
- testSpan.finish()
170
- return
171
- }
172
- if (event.name === 'hook_failure') {
173
- const testSpan = tracer.startSpan(
174
- 'jest.test',
175
- {
176
- childOf,
177
- tags: {
178
- ...spanTags,
179
- [RESOURCE_NAME]: resource,
180
- [TEST_STATUS]: 'fail'
181
- }
182
- }
183
- )
184
- testSpan.context()._trace.origin = CI_APP_ORIGIN
185
- if (event.test.errors && event.test.errors.length) {
186
- const error = new Error(event.test.errors[0][0])
187
- error.stack = event.test.errors[0][1].stack
188
- testSpan.setTag('error', error)
189
- }
190
- testSpan.finish()
191
- return
192
- }
193
- // event.name === test_start at this point
194
- const environment = this
195
- environment.originalTestFnByTestName[testName] = event.test.fn
196
-
197
- let specFunction = event.test.fn
198
- if (specFunction.length) {
199
- specFunction = promisify(specFunction)
200
- }
201
- event.test.fn = tracer.wrap(
202
- 'jest.test',
203
- {
204
- type: 'test',
205
- childOf,
206
- resource,
207
- tags: spanTags
208
- },
209
- async () => {
210
- let result
211
- const testSpan = tracer.scope().active()
212
- environment.testSpansByTestName[`${testName}_${event.test.invocations}`] = testSpan
213
- testSpan.context()._trace.origin = CI_APP_ORIGIN
214
- try {
215
- result = await specFunction()
216
- // it may have been set already if the test timed out
217
- let suppressedErrors = []
218
- const context = getVmContext(environment)
219
- if (context) {
220
- suppressedErrors = context.expect.getState().suppressedErrors
221
- }
222
- setSuppressedErrors(suppressedErrors, testSpan)
223
- if (!testSpan._spanContext._tags[TEST_STATUS]) {
224
- testSpan.setTag(TEST_STATUS, 'pass')
225
- }
226
- } catch (error) {
227
- testSpan.setTag(TEST_STATUS, 'fail')
228
- testSpan.setTag('error', error)
229
- throw error
230
- } finally {
231
- finishAllTraceSpans(testSpan)
232
- }
233
- return result
234
- }
235
- )
236
- }
237
- }
238
-
239
- function patch (Environment, tracer, config) {
240
- const testEnvironmentMetadata = getTestEnvironmentMetadata('jest', config)
241
- const proto = Environment.prototype
242
-
243
- this.wrap(proto, 'teardown', createWrapTeardown(tracer, this))
244
-
245
- const newHandleTestEvent = createHandleTestEvent(tracer, testEnvironmentMetadata, this)
246
- originals.set(newHandleTestEvent, proto.handleTestEvent)
247
- proto.handleTestEvent = newHandleTestEvent
248
-
249
- return wrapEnvironment(Environment)
250
- }
251
-
252
- function unpatch (Environment) {
253
- const proto = Environment.prototype
254
-
255
- this.unwrap(Environment.prototype, 'teardown')
256
- proto.handleTestEvent = originals.get(proto.handleTestEvent)
257
- }
258
-
259
- module.exports = [
260
- {
261
- name: 'jest-environment-node',
262
- versions: ['>=24.8.0'],
263
- patch,
264
- unpatch
265
- },
266
- {
267
- name: 'jest-environment-jsdom',
268
- versions: ['>=24.8.0'],
269
- patch,
270
- unpatch
271
- }
272
- ]