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,22 +1,25 @@
1
- const { promisify } = require('util')
1
+ 'use strict'
2
+
3
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
4
+ const { storage } = require('../../datadog-core')
2
5
 
3
- const { SAMPLING_RULE_DECISION } = require('../../dd-trace/src/constants')
4
- const { SAMPLING_PRIORITY, SPAN_TYPE, RESOURCE_NAME } = require('../../../ext/tags')
5
- const { AUTO_KEEP } = require('../../../ext/priority')
6
6
  const {
7
+ CI_APP_ORIGIN,
7
8
  TEST_TYPE,
8
9
  TEST_NAME,
9
10
  TEST_SUITE,
11
+ TEST_FRAMEWORK_VERSION,
10
12
  TEST_STATUS,
11
13
  TEST_PARAMETERS,
12
- TEST_FRAMEWORK_VERSION,
13
- CI_APP_ORIGIN,
14
- getTestEnvironmentMetadata,
15
- getTestParametersString,
16
14
  finishAllTraceSpans,
15
+ getTestEnvironmentMetadata,
16
+ getTestSuitePath,
17
17
  getTestParentSpan,
18
- getTestSuitePath
18
+ getTestParametersString
19
19
  } = require('../../dd-trace/src/plugins/util/test')
20
+ const { SPAN_TYPE, RESOURCE_NAME, SAMPLING_PRIORITY } = require('../../../ext/tags')
21
+ const { SAMPLING_RULE_DECISION } = require('../../dd-trace/src/constants')
22
+ const { AUTO_KEEP } = require('../../../ext/priority')
20
23
 
21
24
  const skippedTests = new WeakSet()
22
25
 
@@ -29,240 +32,126 @@ function getTestSpanMetadata (tracer, test, sourceRoot) {
29
32
 
30
33
  return {
31
34
  childOf,
32
- resource: `${testSuite}.${fullTestName}`,
35
+ [SPAN_TYPE]: 'test',
33
36
  [TEST_TYPE]: 'test',
34
37
  [TEST_NAME]: fullTestName,
35
38
  [TEST_SUITE]: testSuite,
36
39
  [SAMPLING_RULE_DECISION]: 1,
37
40
  [SAMPLING_PRIORITY]: AUTO_KEEP,
38
- [TEST_FRAMEWORK_VERSION]: tracer._version
41
+ [TEST_FRAMEWORK_VERSION]: tracer._version,
42
+ [RESOURCE_NAME]: `${testSuite}.${fullTestName}`
39
43
  }
40
44
  }
41
45
 
42
- function createWrapRunTest (tracer, testEnvironmentMetadata, sourceRoot) {
43
- return function wrapRunTest (runTest) {
44
- return async function runTestWithTrace () {
45
- // `runTest` is rerun when retries are configured through `this.retries` and the test fails.
46
- // This clause prevents rewrapping `this.test.fn` when it has already been wrapped.
47
- if (this.test._currentRetry !== undefined && this.test._currentRetry !== 0) {
48
- return runTest.apply(this, arguments)
49
- }
46
+ class MochaPlugin extends Plugin {
47
+ static get name () {
48
+ return 'mocha'
49
+ }
50
50
 
51
- let specFunction = this.test.fn
52
- if (specFunction.length) {
53
- specFunction = promisify(specFunction)
54
- // otherwise you have to explicitly call done()
55
- this.test.async = 0
56
- this.test.sync = true
57
- }
51
+ constructor (...args) {
52
+ super(...args)
58
53
 
59
- const { childOf, resource, ...testSpanMetadata } = getTestSpanMetadata(tracer, this.test, sourceRoot)
54
+ this._testNameToParams = {}
55
+ this.testEnvironmentMetadata = getTestEnvironmentMetadata('mocha', this.config)
56
+ this.sourceRoot = process.cwd()
60
57
 
61
- const testParametersString = getTestParametersString(nameToParams, this.test.title)
62
- if (testParametersString) {
63
- testSpanMetadata[TEST_PARAMETERS] = testParametersString
58
+ this.addSub('ci:mocha:test:start', (test) => {
59
+ const store = storage.getStore()
60
+ const span = this.startTestSpan(test)
61
+
62
+ this.enter(span, store)
63
+ })
64
+
65
+ this.addSub('ci:mocha:test:async-end', (status) => {
66
+ // if the status is skipped the span has already been finished
67
+ if (status === 'skipped') {
68
+ return
64
69
  }
70
+ const span = storage.getStore().span
65
71
 
66
- this.test.fn = tracer.wrap(
67
- 'mocha.test',
68
- {
69
- type: 'test',
70
- childOf,
71
- resource,
72
- tags: {
73
- ...testSpanMetadata,
74
- ...testEnvironmentMetadata
75
- }
76
- },
77
- async () => {
78
- const activeSpan = tracer.scope().active()
79
- activeSpan.context()._trace.origin = CI_APP_ORIGIN
80
- let result
81
- try {
82
- const context = this.test.ctx
83
- result = await specFunction.call(context)
84
- if (context.test.state !== 'failed' && !context.test.timedOut) {
85
- activeSpan.setTag(TEST_STATUS, 'pass')
86
- } else {
87
- activeSpan.setTag(TEST_STATUS, 'fail')
88
- }
89
- } catch (error) {
90
- // this.skip has been called
91
- if (error.constructor.name === 'Pending' && !this.forbidPending) {
92
- activeSpan.setTag(TEST_STATUS, 'skip')
93
- } else {
94
- activeSpan.setTag(TEST_STATUS, 'fail')
95
- activeSpan.setTag('error', error)
96
- }
97
- throw error
98
- } finally {
99
- finishAllTraceSpans(activeSpan)
100
- }
101
- return result
102
- }
103
- )
104
- return runTest.apply(this, arguments)
105
- }
106
- }
107
- }
72
+ span.setTag(TEST_STATUS, status)
108
73
 
109
- function getAllTestsInSuite (root) {
110
- const tests = []
111
- function getTests (suiteOrTest) {
112
- suiteOrTest.tests.forEach(test => {
113
- tests.push(test)
74
+ span.finish()
75
+ finishAllTraceSpans(span)
114
76
  })
115
- suiteOrTest.suites.forEach(suite => {
116
- getTests(suite)
77
+
78
+ this.addSub('ci:mocha:test:end', () => {
79
+ this.exit()
117
80
  })
118
- }
119
- getTests(root)
120
- return tests
121
- }
122
81
 
123
- // Necessary to get the skipped tests, that do not go through runTest
124
- function createWrapRunTests (tracer, testEnvironmentMetadata, sourceRoot) {
125
- return function wrapRunTests (runTests) {
126
- return function runTestsWithTrace () {
127
- this.once('end', () => tracer._exporter._writer.flush())
128
- runTests.apply(this, arguments)
129
- const suite = arguments[0]
130
- const tests = getAllTestsInSuite(suite)
82
+ // This covers programmatically skipped tests (that do go through `runTest`)
83
+ this.addSub('ci:mocha:test:skip', () => {
84
+ const span = storage.getStore().span
85
+ span.setTag(TEST_STATUS, 'skip')
86
+ span.finish()
87
+ })
88
+
89
+ this.addSub('ci:mocha:test:error', (err) => {
90
+ if (err) {
91
+ const span = storage.getStore().span
92
+ if (err.constructor.name === 'Pending' && !this.forbidPending) {
93
+ span.setTag(TEST_STATUS, 'skip')
94
+ } else {
95
+ span.setTag(TEST_STATUS, 'fail')
96
+ span.setTag('error', err)
97
+ }
98
+ }
99
+ })
100
+
101
+ this.addSub('ci:mocha:suite:end', tests => {
131
102
  tests.forEach(test => {
132
103
  const { pending: isSkipped } = test
133
- // We call `getAllTestsInSuite` with the root suite so every skipped test
134
- // should already have an associated test span.
135
- // This function is called with every suite, so we need a way to mark
104
+ // `tests` includes every test, so we need a way to mark
136
105
  // the test as already accounted for. We do this through `skippedTests`.
137
106
  // If the test is already marked as skipped, we don't create an additional test span.
138
107
  if (!isSkipped || skippedTests.has(test)) {
139
108
  return
140
109
  }
141
110
  skippedTests.add(test)
142
- const { childOf, resource, ...testSpanMetadata } = getTestSpanMetadata(tracer, test, sourceRoot)
143
111
 
144
- const testSpan = tracer
145
- .startSpan('mocha.test', {
146
- childOf,
147
- tags: {
148
- [SPAN_TYPE]: 'test',
149
- [RESOURCE_NAME]: resource,
150
- ...testSpanMetadata,
151
- ...testEnvironmentMetadata,
152
- [TEST_STATUS]: 'skip'
153
- }
154
- })
155
- testSpan.context()._trace.origin = CI_APP_ORIGIN
112
+ const testSpan = this.startTestSpan(test)
156
113
 
114
+ testSpan.setTag(TEST_STATUS, 'skip')
157
115
  testSpan.finish()
158
116
  })
159
- }
160
- }
161
- }
117
+ })
162
118
 
163
- const nameToParams = {}
119
+ this.addSub('ci:mocha:hook:error', ({ test, error }) => {
120
+ const testSpan = this.startTestSpan(test)
121
+ testSpan.setTag(TEST_STATUS, 'fail')
122
+ testSpan.setTag('error', error)
123
+ testSpan.finish()
124
+ })
164
125
 
165
- function wrapMochaEach (mochaEach) {
166
- return function mochaEachWithTrace () {
167
- const [params] = arguments
168
- const { it, ...rest } = mochaEach.apply(this, arguments)
169
- return {
170
- it: function (name) {
171
- nameToParams[name] = params
172
- it.apply(this, arguments)
173
- },
174
- ...rest
175
- }
126
+ this.addSub('ci:mocha:test:parameterize', ({ name, params }) => {
127
+ this._testNameToParams[name] = params
128
+ })
129
+
130
+ this.addSub('ci:mocha:run:end', () => {
131
+ this.tracer._exporter._writer.flush()
132
+ })
176
133
  }
177
- }
178
134
 
179
- function createWrapFail (tracer, testEnvironmentMetadata, sourceRoot) {
180
- return function wrapFail (fail) {
181
- return function failWithTrace (hook, err) {
182
- if (hook.type !== 'hook') {
183
- /**
184
- * This clause is to cover errors that are uncaught, such as:
185
- * it('will fail', done => {
186
- * setTimeout(() => {
187
- * // will throw but will not be caught by `runTestWithTrace`
188
- * expect(true).to.equal(false)
189
- * done()
190
- * }, 100)
191
- * })
192
- */
193
- const testSpan = tracer.scope().active()
194
- if (!testSpan) {
195
- return fail.apply(this, arguments)
196
- }
197
- const {
198
- [TEST_NAME]: testName,
199
- [TEST_SUITE]: testSuite,
200
- [TEST_STATUS]: testStatus
201
- } = testSpan._spanContext._tags
135
+ startTestSpan (test) {
136
+ const { childOf, ...testSpanMetadata } = getTestSpanMetadata(this.tracer, test, this.sourceRoot)
202
137
 
203
- const isActiveSpanFailing = hook.fullTitle() === testName && hook.file.endsWith(testSuite)
138
+ const testParametersString = getTestParametersString(this._testNameToParams, test.title)
139
+ if (testParametersString) {
140
+ testSpanMetadata[TEST_PARAMETERS] = testParametersString
141
+ }
204
142
 
205
- if (isActiveSpanFailing && !testStatus) {
206
- testSpan.setTag(TEST_STATUS, 'fail')
207
- testSpan.setTag('error', err)
208
- // need to manually finish, as it will not be caught in `runTestWithTrace`
209
- testSpan.finish()
210
- }
211
- return fail.apply(this, arguments)
212
- }
213
- if (err && hook.ctx && hook.ctx.currentTest) {
214
- err.message = `${hook.title}: ${err.message}`
215
- const {
216
- childOf,
217
- resource,
143
+ const testSpan = this.tracer
144
+ .startSpan('mocha.test', {
145
+ childOf,
146
+ tags: {
147
+ ...this.testEnvironmentMetadata,
218
148
  ...testSpanMetadata
219
- } = getTestSpanMetadata(tracer, hook.ctx.currentTest, sourceRoot)
220
- const testSpan = tracer
221
- .startSpan('mocha.test', {
222
- childOf,
223
- tags: {
224
- [SPAN_TYPE]: 'test',
225
- [RESOURCE_NAME]: resource,
226
- ...testSpanMetadata,
227
- ...testEnvironmentMetadata,
228
- [TEST_STATUS]: 'fail'
229
- }
230
- })
231
- testSpan.setTag('error', err)
232
- testSpan.context()._trace.origin = CI_APP_ORIGIN
233
- testSpan.finish()
234
- }
235
- return fail.apply(this, arguments)
236
- }
149
+ }
150
+ })
151
+ testSpan.context()._trace.origin = CI_APP_ORIGIN
152
+
153
+ return testSpan
237
154
  }
238
155
  }
239
156
 
240
- module.exports = [
241
- {
242
- name: 'mocha',
243
- versions: ['>=5.2.0'],
244
- file: 'lib/runner.js',
245
- patch (Runner, tracer, config) {
246
- const testEnvironmentMetadata = getTestEnvironmentMetadata('mocha', config)
247
- const sourceRoot = process.cwd()
248
- this.wrap(Runner.prototype, 'runTests', createWrapRunTests(tracer, testEnvironmentMetadata, sourceRoot))
249
- this.wrap(Runner.prototype, 'runTest', createWrapRunTest(tracer, testEnvironmentMetadata, sourceRoot))
250
- this.wrap(Runner.prototype, 'fail', createWrapFail(tracer, testEnvironmentMetadata, sourceRoot))
251
- },
252
- unpatch (Runner) {
253
- this.unwrap(Runner.prototype, 'runTests')
254
- this.unwrap(Runner.prototype, 'runTest')
255
- this.unwrap(Runner.prototype, 'fail')
256
- }
257
- },
258
- {
259
- name: 'mocha-each',
260
- versions: ['>=2.0.1'],
261
- patch (mochaEach) {
262
- return this.wrapExport(mochaEach, wrapMochaEach(mochaEach))
263
- },
264
- unpatch (mochaEach) {
265
- this.unwrapExport(mochaEach)
266
- }
267
- }
268
- ]
157
+ module.exports = MochaPlugin
@@ -1,6 +1,122 @@
1
1
  'use strict'
2
2
 
3
- const unified = require('./unified')
4
- const legacy = require('./legacy')
3
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
4
+ const { storage } = require('../../datadog-core')
5
+ const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
5
6
 
6
- module.exports = [].concat(unified, legacy)
7
+ class MongodbCorePlugin extends Plugin {
8
+ static get name () {
9
+ return 'mongodb-core'
10
+ }
11
+
12
+ constructor (...args) {
13
+ super(...args)
14
+
15
+ this.addSub(`apm:mongodb:query:start`, ({ ns, ops, options, name }) => {
16
+ const query = getQuery(ops)
17
+ const resource = getResource(ns, query, name)
18
+ const store = storage.getStore()
19
+ const childOf = store ? store.span : store
20
+ const span = this.tracer.startSpan('mongodb.query', {
21
+ childOf,
22
+ tags: {
23
+ 'service.name': this.config.service || `${this.tracer._service}-mongodb`,
24
+ 'resource.name': resource,
25
+ 'span.type': 'mongodb',
26
+ 'span.kind': 'client',
27
+ 'db.name': ns
28
+ }
29
+ })
30
+
31
+ if (query) {
32
+ span.setTag('mongodb.query', query)
33
+ }
34
+
35
+ if (options && options.host && options.port) {
36
+ span.addTags({
37
+ 'out.host': options.host,
38
+ 'out.port': options.port
39
+ })
40
+ }
41
+
42
+ analyticsSampler.sample(span, this.config.measured)
43
+ this.enter(span, store)
44
+ })
45
+
46
+ this.addSub(`apm:mongodb:query:end`, () => {
47
+ this.exit()
48
+ })
49
+
50
+ this.addSub(`apm:mongodb:query:error`, err => {
51
+ storage.getStore().span.setTag('error', err)
52
+ })
53
+
54
+ this.addSub(`apm:mongodb:query:async-end`, () => {
55
+ storage.getStore().span.finish()
56
+ })
57
+ }
58
+ }
59
+
60
+ function getQuery (cmd) {
61
+ if (!cmd || typeof cmd !== 'object' || Array.isArray(cmd)) return
62
+ if (cmd.query) return JSON.stringify(sanitize(cmd.query))
63
+ if (cmd.filter) return JSON.stringify(sanitize(cmd.filter))
64
+ }
65
+
66
+ function getResource (ns, query, operationName) {
67
+ const parts = [operationName, ns]
68
+
69
+ if (query) {
70
+ parts.push(query)
71
+ }
72
+
73
+ return parts.join(' ')
74
+ }
75
+
76
+ function shouldHide (input) {
77
+ return !isObject(input) || Buffer.isBuffer(input) || isBSON(input)
78
+ }
79
+
80
+ function sanitize (input) {
81
+ if (shouldHide(input)) return '?'
82
+
83
+ const output = {}
84
+ const queue = [{
85
+ input,
86
+ output,
87
+ depth: 0
88
+ }]
89
+
90
+ while (queue.length) {
91
+ const {
92
+ input, output, depth
93
+ } = queue.pop()
94
+ const nextDepth = depth + 1
95
+ for (const key in input) {
96
+ if (typeof input[key] === 'function') continue
97
+
98
+ const child = input[key]
99
+ if (depth >= 20 || shouldHide(child)) {
100
+ output[key] = '?'
101
+ } else {
102
+ queue.push({
103
+ input: child,
104
+ output: output[key] = {},
105
+ depth: nextDepth
106
+ })
107
+ }
108
+ }
109
+ }
110
+
111
+ return output
112
+ }
113
+
114
+ function isObject (val) {
115
+ return typeof val === 'object' && val !== null && !(val instanceof Array)
116
+ }
117
+
118
+ function isBSON (val) {
119
+ return val && val._bsontype
120
+ }
121
+
122
+ module.exports = MongodbCorePlugin