dd-trace 3.12.1 → 3.15.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 (101) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/README.md +5 -5
  3. package/ci/init.js +3 -1
  4. package/index.d.ts +100 -1
  5. package/package.json +5 -4
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +86 -0
  7. package/packages/datadog-instrumentations/src/cucumber.js +74 -15
  8. package/packages/datadog-instrumentations/src/cypress.js +1 -1
  9. package/packages/datadog-instrumentations/src/fs.js +358 -0
  10. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  11. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  12. package/packages/datadog-instrumentations/src/jest.js +24 -23
  13. package/packages/datadog-instrumentations/src/ldapjs.js +12 -2
  14. package/packages/datadog-instrumentations/src/mocha.js +10 -7
  15. package/packages/datadog-instrumentations/src/mongoose.js +1 -1
  16. package/packages/datadog-instrumentations/src/mysql.js +7 -1
  17. package/packages/datadog-instrumentations/src/mysql2.js +7 -1
  18. package/packages/datadog-instrumentations/src/next.js +2 -1
  19. package/packages/datadog-instrumentations/src/playwright.js +263 -0
  20. package/packages/datadog-plugin-aws-sdk/src/base.js +12 -5
  21. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -2
  22. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +29 -24
  23. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +31 -16
  24. package/packages/datadog-plugin-cucumber/src/index.js +42 -11
  25. package/packages/datadog-plugin-cypress/src/plugin.js +129 -4
  26. package/packages/datadog-plugin-cypress/src/support.js +5 -0
  27. package/packages/datadog-plugin-fs/src/index.js +45 -0
  28. package/packages/datadog-plugin-hapi/src/index.js +5 -1
  29. package/packages/datadog-plugin-http/src/server.js +1 -1
  30. package/packages/datadog-plugin-http2/src/server.js +1 -1
  31. package/packages/datadog-plugin-jest/src/index.js +40 -70
  32. package/packages/datadog-plugin-mocha/src/index.js +44 -64
  33. package/packages/datadog-plugin-mysql/src/index.js +8 -7
  34. package/packages/datadog-plugin-playwright/src/index.js +112 -0
  35. package/packages/datadog-shimmer/src/shimmer.js +28 -11
  36. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  37. package/packages/dd-trace/src/appsec/blocking.js +35 -9
  38. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +1 -1
  39. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  40. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +60 -0
  41. package/packages/dd-trace/src/appsec/iast/iast-context.js +6 -2
  42. package/packages/dd-trace/src/appsec/iast/index.js +3 -2
  43. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +5 -2
  44. package/packages/dd-trace/src/appsec/index.js +5 -5
  45. package/packages/dd-trace/src/appsec/recommended.json +320 -184
  46. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  47. package/packages/dd-trace/src/appsec/remote_config/index.js +3 -0
  48. package/packages/dd-trace/src/appsec/reporter.js +14 -14
  49. package/packages/dd-trace/src/appsec/sdk/index.js +41 -0
  50. package/packages/dd-trace/src/appsec/sdk/noop.js +17 -0
  51. package/packages/dd-trace/src/appsec/sdk/set_user.js +30 -0
  52. package/packages/dd-trace/src/appsec/sdk/track_event.js +74 -0
  53. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +73 -0
  54. package/packages/dd-trace/src/appsec/sdk/utils.js +10 -0
  55. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +1 -5
  56. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -5
  57. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +48 -11
  58. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +7 -1
  59. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +4 -2
  60. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +5 -3
  61. package/packages/dd-trace/src/config.js +63 -7
  62. package/packages/dd-trace/src/encode/0.4.js +1 -1
  63. package/packages/dd-trace/src/encode/0.5.js +1 -1
  64. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -4
  65. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +52 -37
  66. package/packages/dd-trace/src/encode/tags-processors.js +3 -2
  67. package/packages/dd-trace/src/exporters/common/request.js +10 -3
  68. package/packages/dd-trace/src/lambda/handler.js +5 -6
  69. package/packages/dd-trace/src/log/channels.js +47 -0
  70. package/packages/dd-trace/src/log/index.js +79 -0
  71. package/packages/dd-trace/src/log/writer.js +124 -0
  72. package/packages/dd-trace/src/metrics.js +18 -0
  73. package/packages/dd-trace/src/noop/proxy.js +5 -2
  74. package/packages/dd-trace/src/opentracing/propagation/text_map.js +188 -36
  75. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +99 -0
  76. package/packages/dd-trace/src/opentracing/span.js +2 -1
  77. package/packages/dd-trace/src/opentracing/span_context.js +6 -3
  78. package/packages/dd-trace/src/plugins/ci_plugin.js +72 -12
  79. package/packages/dd-trace/src/plugins/index.js +2 -0
  80. package/packages/dd-trace/src/plugins/util/ci.js +13 -21
  81. package/packages/dd-trace/src/plugins/util/exec.js +2 -2
  82. package/packages/dd-trace/src/plugins/util/git.js +16 -1
  83. package/packages/dd-trace/src/{appsec → plugins/util}/ip_extractor.js +1 -1
  84. package/packages/dd-trace/src/plugins/util/test.js +53 -10
  85. package/packages/dd-trace/src/plugins/util/user-provided-git.js +2 -7
  86. package/packages/dd-trace/src/plugins/util/web.js +11 -0
  87. package/packages/dd-trace/src/profiler.js +3 -0
  88. package/packages/dd-trace/src/profiling/config.js +8 -3
  89. package/packages/dd-trace/src/profiling/exporters/file.js +13 -2
  90. package/packages/dd-trace/src/profiling/profiler.js +23 -6
  91. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
  92. package/packages/dd-trace/src/proxy.js +2 -0
  93. package/packages/dd-trace/src/span_processor.js +1 -1
  94. package/packages/dd-trace/src/span_sampler.js +68 -52
  95. package/packages/dd-trace/src/startup-log.js +3 -6
  96. package/packages/dd-trace/src/telemetry/index.js +23 -2
  97. package/packages/dd-trace/src/telemetry/send-data.js +4 -1
  98. package/packages/dd-trace/src/tracer.js +0 -16
  99. package/scripts/check-proposal-labels.js +71 -0
  100. package/packages/dd-trace/src/log.js +0 -143
  101. /package/packages/dd-trace/src/{appsec → plugins/util}/ip_blocklist.js +0 -0
@@ -8,7 +8,12 @@ beforeEach(() => {
8
8
  })
9
9
  })
10
10
 
11
+ before(() => {
12
+ cy.task('dd:testSuiteStart', Cypress.mocha.getRootSuite().file)
13
+ })
14
+
11
15
  after(() => {
16
+ cy.task('dd:testSuiteFinish', Cypress.mocha.getRunner().stats)
12
17
  cy.window().then(win => {
13
18
  win.dispatchEvent(new Event('beforeunload'))
14
19
  })
@@ -0,0 +1,45 @@
1
+ 'use strict'
2
+
3
+ const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
4
+
5
+ class FsPlugin extends TracingPlugin {
6
+ static get name () { return 'fs' }
7
+ static get operation () { return 'operation' }
8
+
9
+ configure (...args) {
10
+ return super.configure(...args)
11
+ }
12
+
13
+ start ({ operation, ...params }) {
14
+ if (!this.activeSpan) return this.skip()
15
+
16
+ const lowerOp = operation.toLowerCase()
17
+ const flag = params.flag || params.flags || (params.options && (params.options.flag || params.options.flags))
18
+ const defaultFlag = ((lowerOp.includes('open') || lowerOp.includes('read')) && 'r') ||
19
+ (lowerOp.includes('write') && 'w') ||
20
+ (lowerOp.includes('append') && 'a')
21
+ const fd = params.fd || (typeof params.file === 'number' && params.file)
22
+ const path = params.path || params.prefix || params.filename || (typeof params.file === 'string' && params.file)
23
+ const uid = typeof params.uid === 'number' && params.uid.toString()
24
+ const gid = typeof params.gid === 'number' && params.gid.toString()
25
+ const mode = typeof params.mode === 'number' ? params.mode.toString(8) : params.mode
26
+
27
+ this.startSpan('fs.operation', {
28
+ service: this.config.service,
29
+ resource: operation,
30
+ kind: 'internal',
31
+ meta: {
32
+ 'file.descriptor': (typeof fd === 'object' || typeof fd === 'number') ? fd.toString() : '',
33
+ 'file.dest': params.dest || params.newPath || (params.target && params.path),
34
+ 'file.flag': String(flag || defaultFlag || ''),
35
+ 'file.gid': gid || '',
36
+ 'file.mode': mode,
37
+ 'file.path': path || '',
38
+ 'file.src': params.src || params.oldPath || params.existingPath || params.target,
39
+ 'file.uid': uid || ''
40
+ }
41
+ })
42
+ }
43
+ }
44
+
45
+ module.exports = FsPlugin
@@ -26,7 +26,11 @@ class HapiPlugin extends RouterPlugin {
26
26
  web.setRoute(req, route)
27
27
  })
28
28
 
29
- this.addSub(`apm:hapi:request:error`, this.addError)
29
+ this.addSub('apm:hapi:request:error', error => {
30
+ if (!error || !error.isBoom || !this.config.validateStatus(error.output.statusCode)) {
31
+ this.addError(error)
32
+ }
33
+ })
30
34
 
31
35
  this.addSub('apm:hapi:extension:enter', ({ req }) => {
32
36
  this.enter(this._requestSpans.get(req))
@@ -23,7 +23,7 @@ class HttpServerPlugin extends Plugin {
23
23
  span.setTag(COMPONENT, this.constructor.name)
24
24
 
25
25
  this._parentStore = store
26
- this.enter(span, { ...store, req })
26
+ this.enter(span, { ...store, req, res })
27
27
 
28
28
  const context = web.getContext(req)
29
29
 
@@ -22,7 +22,7 @@ class Http2ServerPlugin extends Plugin {
22
22
 
23
23
  span.setTag(COMPONENT, this.constructor.name)
24
24
 
25
- this.enter(span, { ...store, req })
25
+ this.enter(span, { ...store, req, res })
26
26
 
27
27
  const context = web.getContext(req)
28
28
 
@@ -6,18 +6,12 @@ const {
6
6
  JEST_TEST_RUNNER,
7
7
  finishAllTraceSpans,
8
8
  getTestEnvironmentMetadata,
9
- getTestParentSpan,
10
- getTestSessionCommonTags,
11
9
  getTestSuiteCommonTags,
10
+ addIntelligentTestRunnerSpanTags,
12
11
  TEST_PARAMETERS,
13
12
  getCodeOwnersFileEntries,
14
- TEST_SESSION_ID,
15
- TEST_SUITE_ID,
16
13
  TEST_COMMAND,
17
- TEST_ITR_TESTS_SKIPPED,
18
- TEST_SESSION_CODE_COVERAGE_ENABLED,
19
- TEST_SESSION_ITR_SKIPPING_ENABLED,
20
- TEST_CODE_COVERAGE_LINES_TOTAL
14
+ TEST_FRAMEWORK_VERSION
21
15
  } = require('../../dd-trace/src/plugins/util/test')
22
16
  const { COMPONENT } = require('../../dd-trace/src/constants')
23
17
 
@@ -49,22 +43,6 @@ class JestPlugin extends CiPlugin {
49
43
  this.testEnvironmentMetadata = getTestEnvironmentMetadata('jest', this.config)
50
44
  this.codeOwnersEntries = getCodeOwnersFileEntries()
51
45
 
52
- this.addSub('ci:jest:session:start', (command) => {
53
- const store = storage.getStore()
54
- const childOf = getTestParentSpan(this.tracer)
55
- const testSessionSpanMetadata = getTestSessionCommonTags(command, this.tracer._version)
56
-
57
- const testSessionSpan = this.tracer.startSpan('jest.test_session', {
58
- childOf,
59
- tags: {
60
- [COMPONENT]: this.constructor.name,
61
- ...this.testEnvironmentMetadata,
62
- ...testSessionSpanMetadata
63
- }
64
- })
65
- this.enter(testSessionSpan, store)
66
- })
67
-
68
46
  this.addSub('ci:jest:session:finish', ({
69
47
  status,
70
48
  isSuitesSkipped,
@@ -72,18 +50,18 @@ class JestPlugin extends CiPlugin {
72
50
  isCodeCoverageEnabled,
73
51
  testCodeCoverageLinesTotal
74
52
  }) => {
75
- const testSessionSpan = storage.getStore().span
76
-
77
- testSessionSpan.setTag(TEST_STATUS, status)
78
- testSessionSpan.setTag(TEST_ITR_TESTS_SKIPPED, isSuitesSkipped ? 'true' : 'false')
79
- testSessionSpan.setTag(TEST_SESSION_ITR_SKIPPING_ENABLED, isSuitesSkippingEnabled ? 'true' : 'false')
80
- testSessionSpan.setTag(TEST_SESSION_CODE_COVERAGE_ENABLED, isCodeCoverageEnabled ? 'true' : 'false')
81
-
82
- if (testCodeCoverageLinesTotal !== undefined) {
83
- testSessionSpan.setTag(TEST_CODE_COVERAGE_LINES_TOTAL, testCodeCoverageLinesTotal)
84
- }
85
- testSessionSpan.finish()
86
- finishAllTraceSpans(testSessionSpan)
53
+ this.testSessionSpan.setTag(TEST_STATUS, status)
54
+ this.testModuleSpan.setTag(TEST_STATUS, status)
55
+
56
+ addIntelligentTestRunnerSpanTags(
57
+ this.testSessionSpan,
58
+ this.testModuleSpan,
59
+ { isSuitesSkipped, isSuitesSkippingEnabled, isCodeCoverageEnabled, testCodeCoverageLinesTotal }
60
+ )
61
+
62
+ this.testModuleSpan.finish()
63
+ this.testSessionSpan.finish()
64
+ finishAllTraceSpans(this.testSessionSpan)
87
65
  this.tracer._exporter.flush()
88
66
  })
89
67
 
@@ -91,26 +69,28 @@ class JestPlugin extends CiPlugin {
91
69
  // This subscriber changes the configuration objects from jest to inject the trace id
92
70
  // of the test session to the processes that run the test suites.
93
71
  this.addSub('ci:jest:session:configuration', configs => {
94
- const testSessionSpan = storage.getStore().span
95
72
  configs.forEach(config => {
96
- config._ddTestSessionId = testSessionSpan.context()._traceId.toString(10)
97
- config._ddTestCommand = testSessionSpan.context()._tags[TEST_COMMAND]
73
+ config._ddTestSessionId = this.testSessionSpan.context().toTraceId()
74
+ config._ddTestModuleId = this.testModuleSpan.context().toSpanId()
75
+ config._ddTestCommand = this.testSessionSpan.context()._tags[TEST_COMMAND]
98
76
  })
99
77
  })
100
78
 
101
- this.addSub('ci:jest:test-suite:start', ({ testSuite, testEnvironmentOptions }) => {
102
- const { _ddTestSessionId: testSessionId, _ddTestCommand: testCommand } = testEnvironmentOptions
103
-
104
- const store = storage.getStore()
79
+ this.addSub('ci:jest:test-suite:start', ({ testSuite, testEnvironmentOptions, frameworkVersion }) => {
80
+ const {
81
+ _ddTestSessionId: testSessionId,
82
+ _ddTestCommand: testCommand,
83
+ _ddTestModuleId: testModuleId
84
+ } = testEnvironmentOptions
105
85
 
106
86
  const testSessionSpanContext = this.tracer.extract('text_map', {
107
87
  'x-datadog-trace-id': testSessionId,
108
- 'x-datadog-parent-id': '0000000000000000'
88
+ 'x-datadog-parent-id': testModuleId
109
89
  })
110
90
 
111
- const testSuiteMetadata = getTestSuiteCommonTags(testCommand, this.tracer._version, testSuite)
91
+ const testSuiteMetadata = getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite)
112
92
 
113
- const testSuiteSpan = this.tracer.startSpan('jest.test_suite', {
93
+ this.testSuiteSpan = this.tracer.startSpan('jest.test_suite', {
114
94
  childOf: testSessionSpanContext,
115
95
  tags: {
116
96
  [COMPONENT]: this.constructor.name,
@@ -118,19 +98,17 @@ class JestPlugin extends CiPlugin {
118
98
  ...testSuiteMetadata
119
99
  }
120
100
  })
121
- this.enter(testSuiteSpan, store)
122
101
  })
123
102
 
124
103
  this.addSub('ci:jest:test-suite:finish', ({ status, errorMessage }) => {
125
- const testSuiteSpan = storage.getStore().span
126
- testSuiteSpan.setTag(TEST_STATUS, status)
104
+ this.testSuiteSpan.setTag(TEST_STATUS, status)
127
105
  if (errorMessage) {
128
- testSuiteSpan.setTag('error', new Error(errorMessage))
106
+ this.testSuiteSpan.setTag('error', new Error(errorMessage))
129
107
  }
130
- testSuiteSpan.finish()
108
+ this.testSuiteSpan.finish()
131
109
  // Suites potentially run in a different process than the session,
132
110
  // so calling finishAllTraceSpans on the session span is not enough
133
- finishAllTraceSpans(testSuiteSpan)
111
+ finishAllTraceSpans(this.testSuiteSpan)
134
112
  })
135
113
 
136
114
  /**
@@ -139,8 +117,7 @@ class JestPlugin extends CiPlugin {
139
117
  * fetching the ITR config.
140
118
  */
141
119
  this.addSub('ci:jest:test-suite:code-coverage', (coverageFiles) => {
142
- const testSuiteSpan = storage.getStore().span
143
- this.tracer._exporter.exportCoverage({ span: testSuiteSpan, coverageFiles })
120
+ this.tracer._exporter.exportCoverage({ span: this.testSuiteSpan, coverageFiles })
144
121
  })
145
122
 
146
123
  this.addSub('ci:jest:test:start', (test) => {
@@ -159,9 +136,12 @@ class JestPlugin extends CiPlugin {
159
136
 
160
137
  this.addSub('ci:jest:test:err', (error) => {
161
138
  if (error) {
162
- const span = storage.getStore().span
163
- span.setTag(TEST_STATUS, 'fail')
164
- span.setTag('error', error)
139
+ const store = storage.getStore()
140
+ if (store && store.span) {
141
+ const span = store.span
142
+ span.setTag(TEST_STATUS, 'fail')
143
+ span.setTag('error', error)
144
+ }
165
145
  }
166
146
  })
167
147
 
@@ -173,25 +153,15 @@ class JestPlugin extends CiPlugin {
173
153
  }
174
154
 
175
155
  startTestSpan (test) {
176
- const suiteTags = {}
177
- const store = storage.getStore()
178
- const testSuiteSpan = store ? store.span : undefined
179
- if (testSuiteSpan) {
180
- const testSuiteId = testSuiteSpan.context()._spanId.toString(10)
181
- suiteTags[TEST_SUITE_ID] = testSuiteId
182
- suiteTags[TEST_SESSION_ID] = testSuiteSpan.context()._traceId.toString(10)
183
- suiteTags[TEST_COMMAND] = testSuiteSpan.context()._tags[TEST_COMMAND]
184
- }
185
-
186
- const { suite, name, runner, testParameters } = test
156
+ const { suite, name, runner, testParameters, frameworkVersion } = test
187
157
 
188
158
  const extraTags = {
189
159
  [JEST_TEST_RUNNER]: runner,
190
160
  [TEST_PARAMETERS]: testParameters,
191
- ...suiteTags
161
+ [TEST_FRAMEWORK_VERSION]: frameworkVersion
192
162
  }
193
163
 
194
- return super.startTestSpan(name, suite, extraTags)
164
+ return super.startTestSpan(name, suite, this.testSuiteSpan, extraTags)
195
165
  }
196
166
  }
197
167
 
@@ -8,16 +8,9 @@ const {
8
8
  TEST_PARAMETERS,
9
9
  finishAllTraceSpans,
10
10
  getTestSuitePath,
11
- getTestParentSpan,
12
11
  getTestParametersString,
13
- getTestSessionCommonTags,
14
12
  getTestSuiteCommonTags,
15
- TEST_SUITE_ID,
16
- TEST_SESSION_ID,
17
- TEST_COMMAND,
18
- TEST_ITR_TESTS_SKIPPED,
19
- TEST_SESSION_CODE_COVERAGE_ENABLED,
20
- TEST_SESSION_ITR_SKIPPING_ENABLED
13
+ addIntelligentTestRunnerSpanTags
21
14
  } = require('../../dd-trace/src/plugins/util/test')
22
15
  const { COMPONENT } = require('../../dd-trace/src/constants')
23
16
 
@@ -48,30 +41,15 @@ class MochaPlugin extends CiPlugin {
48
41
  })
49
42
  })
50
43
 
51
- this.addSub('ci:mocha:session:start', (command) => {
52
- const childOf = getTestParentSpan(this.tracer)
53
- const testSessionSpanMetadata = getTestSessionCommonTags(command, this.tracer._version)
54
-
55
- this.command = command
56
- this.testSessionSpan = this.tracer.startSpan('mocha.test_session', {
57
- childOf,
58
- tags: {
59
- [COMPONENT]: this.constructor.name,
60
- ...this.testEnvironmentMetadata,
61
- ...testSessionSpanMetadata
62
- }
63
- })
64
- })
65
-
66
44
  this.addSub('ci:mocha:test-suite:start', (suite) => {
67
45
  const store = storage.getStore()
68
46
  const testSuiteMetadata = getTestSuiteCommonTags(
69
47
  this.command,
70
- this.tracer._version,
48
+ this.frameworkVersion,
71
49
  getTestSuitePath(suite.file, this.sourceRoot)
72
50
  )
73
51
  const testSuiteSpan = this.tracer.startSpan('mocha.test_suite', {
74
- childOf: this.testSessionSpan,
52
+ childOf: this.testModuleSpan,
75
53
  tags: {
76
54
  [COMPONENT]: this.constructor.name,
77
55
  ...this.testEnvironmentMetadata,
@@ -83,18 +61,24 @@ class MochaPlugin extends CiPlugin {
83
61
  })
84
62
 
85
63
  this.addSub('ci:mocha:test-suite:finish', (status) => {
86
- const span = storage.getStore().span
87
- // the test status of the suite may have been set in ci:mocha:test-suite:error already
88
- if (!span.context()._tags[TEST_STATUS]) {
89
- span.setTag(TEST_STATUS, status)
64
+ const store = storage.getStore()
65
+ if (store && store.span) {
66
+ const span = storage.getStore().span
67
+ // the test status of the suite may have been set in ci:mocha:test-suite:error already
68
+ if (!span.context()._tags[TEST_STATUS]) {
69
+ span.setTag(TEST_STATUS, status)
70
+ }
71
+ span.finish()
90
72
  }
91
- span.finish()
92
73
  })
93
74
 
94
75
  this.addSub('ci:mocha:test-suite:error', (err) => {
95
- const span = storage.getStore().span
96
- span.setTag('error', err)
97
- span.setTag(TEST_STATUS, 'fail')
76
+ const store = storage.getStore()
77
+ if (store && store.span) {
78
+ const span = storage.getStore().span
79
+ span.setTag('error', err)
80
+ span.setTag(TEST_STATUS, 'fail')
81
+ }
98
82
  })
99
83
 
100
84
  this.addSub('ci:mocha:test:start', (test) => {
@@ -105,12 +89,16 @@ class MochaPlugin extends CiPlugin {
105
89
  })
106
90
 
107
91
  this.addSub('ci:mocha:test:finish', (status) => {
108
- const span = storage.getStore().span
92
+ const store = storage.getStore()
109
93
 
110
- span.setTag(TEST_STATUS, status)
94
+ if (store && store.span) {
95
+ const span = storage.getStore().span
96
+
97
+ span.setTag(TEST_STATUS, status)
111
98
 
112
- span.finish()
113
- finishAllTraceSpans(span)
99
+ span.finish()
100
+ finishAllTraceSpans(span)
101
+ }
114
102
  })
115
103
 
116
104
  this.addSub('ci:mocha:test:skip', (test) => {
@@ -124,8 +112,9 @@ class MochaPlugin extends CiPlugin {
124
112
  })
125
113
 
126
114
  this.addSub('ci:mocha:test:error', (err) => {
127
- if (err) {
128
- const span = storage.getStore().span
115
+ const store = storage.getStore()
116
+ if (err && store && store.span) {
117
+ const span = store.span
129
118
  if (err.constructor.name === 'Pending' && !this.forbidPending) {
130
119
  span.setTag(TEST_STATUS, 'skip')
131
120
  } else {
@@ -143,10 +132,15 @@ class MochaPlugin extends CiPlugin {
143
132
  if (this.testSessionSpan) {
144
133
  const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.itrConfig || {}
145
134
  this.testSessionSpan.setTag(TEST_STATUS, status)
146
- this.testSessionSpan.setTag(TEST_ITR_TESTS_SKIPPED, isSuitesSkipped ? 'true' : 'false')
147
- this.testSessionSpan.setTag(TEST_SESSION_ITR_SKIPPING_ENABLED, isSuitesSkippingEnabled ? 'true' : 'false')
148
- this.testSessionSpan.setTag(TEST_SESSION_CODE_COVERAGE_ENABLED, isCodeCoverageEnabled ? 'true' : 'false')
135
+ this.testModuleSpan.setTag(TEST_STATUS, status)
149
136
 
137
+ addIntelligentTestRunnerSpanTags(
138
+ this.testSessionSpan,
139
+ this.testModuleSpan,
140
+ { isSuitesSkipped, isSuitesSkippingEnabled, isCodeCoverageEnabled }
141
+ )
142
+
143
+ this.testModuleSpan.finish()
150
144
  this.testSessionSpan.finish()
151
145
  finishAllTraceSpans(this.testSessionSpan)
152
146
  }
@@ -156,33 +150,19 @@ class MochaPlugin extends CiPlugin {
156
150
  }
157
151
 
158
152
  startTestSpan (test) {
159
- const testSuiteTags = {}
160
- const testSuiteSpan = this._testSuites.get(test.parent.file)
161
- if (testSuiteSpan) {
162
- const testSuiteId = testSuiteSpan.context()._spanId.toString(10)
163
- testSuiteTags[TEST_SUITE_ID] = testSuiteId
164
- }
153
+ const testName = test.fullTitle()
154
+ const { file: testSuiteAbsolutePath, title } = test
165
155
 
166
- if (this.testSessionSpan) {
167
- const testSessionId = this.testSessionSpan.context()._traceId.toString(10)
168
- testSuiteTags[TEST_SESSION_ID] = testSessionId
169
- testSuiteTags[TEST_COMMAND] = this.command
170
- }
171
-
172
- const { file: testSuiteAbsolutePath } = test
173
- const fullTestName = test.fullTitle()
174
- const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
175
-
176
- const extraTags = {
177
- ...testSuiteTags
178
- }
179
-
180
- const testParametersString = getTestParametersString(this._testNameToParams, test.title)
156
+ const extraTags = {}
157
+ const testParametersString = getTestParametersString(this._testNameToParams, title)
181
158
  if (testParametersString) {
182
159
  extraTags[TEST_PARAMETERS] = testParametersString
183
160
  }
184
161
 
185
- return super.startTestSpan(fullTestName, testSuite, extraTags)
162
+ const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
163
+ const testSuiteSpan = this._testSuites.get(testSuiteAbsolutePath)
164
+
165
+ return super.startTestSpan(testName, testSuite, testSuiteSpan, extraTags)
186
166
  }
187
167
  }
188
168
 
@@ -6,22 +6,23 @@ class MySQLPlugin extends DatabasePlugin {
6
6
  static get name () { return 'mysql' }
7
7
  static get system () { return 'mysql' }
8
8
 
9
- start ({ sql, conf: dbConfig }) {
10
- const service = getServiceName(this.config, dbConfig)
9
+ start (payload) {
10
+ const service = getServiceName(this.config, payload.conf)
11
11
 
12
12
  this.startSpan(`${this.system}.query`, {
13
13
  service,
14
- resource: sql,
14
+ resource: payload.sql,
15
15
  type: 'sql',
16
16
  kind: 'client',
17
17
  meta: {
18
18
  'db.type': this.system,
19
- 'db.user': dbConfig.user,
20
- 'db.name': dbConfig.database,
21
- 'out.host': dbConfig.host,
22
- 'out.port': dbConfig.port
19
+ 'db.user': payload.conf.user,
20
+ 'db.name': payload.conf.database,
21
+ 'out.host': payload.conf.host,
22
+ 'out.port': payload.conf.port
23
23
  }
24
24
  })
25
+ payload.sql = this.injectDbmQuery(payload.sql, service)
25
26
  }
26
27
  }
27
28
 
@@ -0,0 +1,112 @@
1
+ 'use strict'
2
+
3
+ const { storage } = require('../../datadog-core')
4
+ const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
5
+
6
+ const {
7
+ TEST_STATUS,
8
+ finishAllTraceSpans,
9
+ getTestSuitePath,
10
+ getTestSuiteCommonTags
11
+ } = require('../../dd-trace/src/plugins/util/test')
12
+ const { RESOURCE_NAME } = require('../../../ext/tags')
13
+ const { COMPONENT } = require('../../dd-trace/src/constants')
14
+
15
+ class PlaywrightPlugin extends CiPlugin {
16
+ static get name () {
17
+ return 'playwright'
18
+ }
19
+
20
+ constructor (...args) {
21
+ super(...args)
22
+
23
+ this._testSuites = new Map()
24
+
25
+ this.addSub('ci:playwright:session:finish', ({ status, onDone }) => {
26
+ this.testModuleSpan.setTag(TEST_STATUS, status)
27
+ this.testSessionSpan.setTag(TEST_STATUS, status)
28
+
29
+ this.testModuleSpan.finish()
30
+ this.testSessionSpan.finish()
31
+ finishAllTraceSpans(this.testSessionSpan)
32
+ this.tracer._exporter.flush(onDone)
33
+ })
34
+
35
+ this.addSub('ci:playwright:test-suite:start', (testSuiteAbsolutePath) => {
36
+ const store = storage.getStore()
37
+ const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
38
+
39
+ const testSuiteMetadata = getTestSuiteCommonTags(
40
+ this.command,
41
+ this.frameworkVersion,
42
+ testSuite
43
+ )
44
+
45
+ const testSuiteSpan = this.tracer.startSpan('playwright.test_suite', {
46
+ childOf: this.testModuleSpan,
47
+ tags: {
48
+ [COMPONENT]: this.constructor.name,
49
+ ...this.testEnvironmentMetadata,
50
+ ...testSuiteMetadata
51
+ }
52
+ })
53
+ this.enter(testSuiteSpan, store)
54
+
55
+ this._testSuites.set(testSuite, testSuiteSpan)
56
+ })
57
+
58
+ this.addSub('ci:playwright:test-suite:finish', (status) => {
59
+ const store = storage.getStore()
60
+ const span = store && store.span
61
+ if (!span) return
62
+ span.setTag(TEST_STATUS, status)
63
+ span.finish()
64
+ })
65
+
66
+ this.addSub('ci:playwright:test:start', ({ testName, testSuiteAbsolutePath }) => {
67
+ const store = storage.getStore()
68
+ const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
69
+ const span = this.startTestSpan(testName, testSuite)
70
+
71
+ this.enter(span, store)
72
+ })
73
+ this.addSub('ci:playwright:test:finish', ({ testStatus, steps, error }) => {
74
+ const store = storage.getStore()
75
+ const span = store && store.span
76
+ if (!span) return
77
+
78
+ span.setTag(TEST_STATUS, testStatus)
79
+
80
+ if (error) {
81
+ span.setTag('error', error)
82
+ }
83
+
84
+ steps.forEach(step => {
85
+ const stepStartTime = step.startTime.getTime()
86
+ const stepSpan = this.tracer.startSpan('playwright.step', {
87
+ childOf: span,
88
+ startTime: stepStartTime,
89
+ tags: {
90
+ [COMPONENT]: this.constructor.name,
91
+ 'playwright.step': step.title,
92
+ [RESOURCE_NAME]: step.title
93
+ }
94
+ })
95
+ if (step.error) {
96
+ stepSpan.setTag('error', step.error)
97
+ }
98
+ stepSpan.finish(stepStartTime + step.duration)
99
+ })
100
+
101
+ span.finish()
102
+ finishAllTraceSpans(span)
103
+ })
104
+ }
105
+
106
+ startTestSpan (testName, testSuite) {
107
+ const testSuiteSpan = this._testSuites.get(testSuite)
108
+ return super.startTestSpan(testName, testSuite, testSuiteSpan)
109
+ }
110
+ }
111
+
112
+ module.exports = PlaywrightPlugin
@@ -10,7 +10,11 @@ function copyProperties (original, wrapped) {
10
10
  const keys = Reflect.ownKeys(props)
11
11
 
12
12
  for (const key of keys) {
13
- Object.defineProperty(wrapped, key, props[key])
13
+ try {
14
+ Object.defineProperty(wrapped, key, props[key])
15
+ } catch (e) {
16
+ // TODO: figure out how to handle this without a try/catch
17
+ }
14
18
  }
15
19
  }
16
20
 
@@ -33,28 +37,41 @@ function wrapFn (original, delegate) {
33
37
 
34
38
  function wrapMethod (target, name, wrapper) {
35
39
  assertMethod(target, name)
36
- assertNotClass(target[name]) // TODO: support constructors of native classes
37
40
  assertFunction(wrapper)
38
41
 
39
42
  const original = target[name]
40
43
  const wrapped = wrapper(original)
41
44
  const descriptor = Object.getOwnPropertyDescriptor(target, name)
42
45
 
46
+ const attributes = {
47
+ configurable: true,
48
+ ...descriptor
49
+ }
50
+
51
+ copyProperties(original, wrapped)
52
+
43
53
  if (descriptor) {
44
54
  unwrappers.set(wrapped, () => Object.defineProperty(target, name, descriptor))
55
+
56
+ if (descriptor.get || descriptor.set) {
57
+ attributes.get = () => wrapped
58
+ } else {
59
+ attributes.value = wrapped
60
+ }
61
+
62
+ // TODO: create a single object for multiple wrapped methods
63
+ if (descriptor.configurable === false) {
64
+ return Object.create(target, {
65
+ [name]: attributes
66
+ })
67
+ }
45
68
  } else { // no descriptor means original was on the prototype
46
69
  unwrappers.set(wrapped, () => delete target[name])
70
+ attributes.value = wrapped
71
+ attributes.writable = true
47
72
  }
48
73
 
49
- Object.defineProperty(target, name, {
50
- configurable: true,
51
- writable: true,
52
- enumerable: false,
53
- ...descriptor,
54
- value: wrapped
55
- })
56
-
57
- copyProperties(original, wrapped)
74
+ Object.defineProperty(target, name, attributes)
58
75
 
59
76
  return target
60
77
  }
@@ -16,5 +16,7 @@ module.exports = {
16
16
  HTTP_INCOMING_REMOTE_IP: 'server.request.client_ip',
17
17
  HTTP_INCOMING_REMOTE_PORT: 'server.request.client_port',
18
18
 
19
- HTTP_CLIENT_IP: 'http.client_ip'
19
+ HTTP_CLIENT_IP: 'http.client_ip',
20
+
21
+ USER_ID: 'usr.id'
20
22
  }