dd-trace 2.9.0 → 2.11.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 (45) hide show
  1. package/package.json +6 -6
  2. package/packages/datadog-instrumentations/index.js +5 -0
  3. package/packages/datadog-instrumentations/src/connect.js +3 -0
  4. package/packages/datadog-instrumentations/src/fastify.js +21 -15
  5. package/packages/datadog-instrumentations/src/graphql.js +354 -0
  6. package/packages/datadog-instrumentations/src/grpc/client.js +250 -0
  7. package/packages/datadog-instrumentations/src/grpc/server.js +144 -0
  8. package/packages/{datadog-plugin-grpc/src/kinds.js → datadog-instrumentations/src/grpc/types.js} +0 -0
  9. package/packages/datadog-instrumentations/src/grpc.js +4 -0
  10. package/packages/datadog-instrumentations/src/http/client.js +1 -1
  11. package/packages/datadog-instrumentations/src/http2/client.js +67 -0
  12. package/packages/datadog-instrumentations/src/http2/server.js +3 -0
  13. package/packages/datadog-instrumentations/src/http2.js +4 -0
  14. package/packages/datadog-instrumentations/src/jest.js +14 -14
  15. package/packages/datadog-instrumentations/src/koa.js +12 -3
  16. package/packages/datadog-instrumentations/src/microgateway-core.js +66 -0
  17. package/packages/datadog-instrumentations/src/mocha.js +129 -63
  18. package/packages/datadog-instrumentations/src/next.js +140 -0
  19. package/packages/datadog-instrumentations/src/router.js +3 -0
  20. package/packages/datadog-instrumentations/src/tedious.js +1 -1
  21. package/packages/datadog-plugin-graphql/src/index.js +123 -412
  22. package/packages/datadog-plugin-graphql/src/resolve.js +120 -0
  23. package/packages/datadog-plugin-grpc/src/client.js +54 -283
  24. package/packages/datadog-plugin-grpc/src/index.js +31 -3
  25. package/packages/datadog-plugin-grpc/src/server.js +50 -145
  26. package/packages/datadog-plugin-http/src/client.js +10 -7
  27. package/packages/datadog-plugin-http2/src/client.js +72 -120
  28. package/packages/datadog-plugin-http2/src/index.js +32 -3
  29. package/packages/datadog-plugin-http2/src/server.js +6 -214
  30. package/packages/datadog-plugin-jest/src/util.js +13 -1
  31. package/packages/datadog-plugin-microgateway-core/src/index.js +14 -145
  32. package/packages/datadog-plugin-mocha/src/index.js +8 -36
  33. package/packages/datadog-plugin-next/src/index.js +56 -168
  34. package/packages/datadog-plugin-router/src/index.js +7 -3
  35. package/packages/dd-trace/src/config.js +3 -2
  36. package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
  37. package/packages/dd-trace/src/opentracing/span.js +6 -3
  38. package/packages/dd-trace/src/plugin_manager.js +21 -0
  39. package/packages/dd-trace/src/plugins/plugin.js +3 -1
  40. package/packages/dd-trace/src/plugins/util/test.js +3 -0
  41. package/packages/dd-trace/src/plugins/util/web.js +0 -7
  42. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  43. package/packages/dd-trace/src/startup-log.js +1 -1
  44. package/packages/dd-trace/src/telemetry.js +1 -1
  45. package/packages/dd-trace/lib/version.js +0 -1
@@ -5,101 +5,132 @@ const testStartCh = channel('ci:mocha:test:start')
5
5
  const errorCh = channel('ci:mocha:test:error')
6
6
  const skipCh = channel('ci:mocha:test:skip')
7
7
  const testFinishCh = channel('ci:mocha:test:finish')
8
- const suiteFinishCh = channel('ci:mocha:suite:finish')
9
- const hookErrorCh = channel('ci:mocha:hook:error')
10
8
  const parameterizedTestCh = channel('ci:mocha:test:parameterize')
11
9
  const testRunFinishCh = channel('ci:mocha:run:finish')
12
10
 
13
- // TODO: remove when root hooks and fixtures are implemented
14
- const patched = new WeakSet()
15
-
16
11
  function isRetry (test) {
17
12
  return test._currentRetry !== undefined && test._currentRetry !== 0
18
13
  }
19
14
 
20
- function getAllTestsInSuite (root) {
21
- const tests = []
22
- function getTests (suiteOrTest) {
23
- suiteOrTest.tests.forEach(test => {
24
- tests.push(test)
25
- })
26
- suiteOrTest.suites.forEach(suite => {
27
- getTests(suite)
28
- })
15
+ function getTestAsyncResource (test) {
16
+ if (!test.fn) {
17
+ return testToAr.get(test)
29
18
  }
30
- getTests(root)
31
- return tests
19
+ if (!test.fn.asyncResource) {
20
+ return testToAr.get(test.fn)
21
+ }
22
+ const originalFn = originalFns.get(test.fn)
23
+ return testToAr.get(originalFn)
32
24
  }
33
25
 
26
+ // TODO: remove when root hooks and fixtures are implemented
27
+ const patched = new WeakSet()
28
+
29
+ const testToAr = new WeakMap()
30
+ const originalFns = new WeakMap()
31
+
34
32
  function mochaHook (Runner) {
35
33
  if (patched.has(Runner)) return Runner
36
34
 
37
35
  patched.add(Runner)
38
36
 
39
- shimmer.wrap(Runner.prototype, 'runTest', runTest => function () {
40
- if (!testStartCh.hasSubscribers || isRetry(this.test)) {
41
- return runTest.apply(this, arguments)
37
+ shimmer.wrap(Runner.prototype, 'run', run => function () {
38
+ if (!testStartCh.hasSubscribers) {
39
+ return run.apply(this, arguments)
42
40
  }
43
41
 
44
- const asyncResource = new AsyncResource('bound-anonymous-fn')
45
- return asyncResource.runInAsyncScope(() => {
46
- testStartCh.publish(this.test)
47
-
48
- this.once('test end', AsyncResource.bind(() => {
49
- let status
42
+ this.on('test', (test) => {
43
+ if (isRetry(test)) {
44
+ return
45
+ }
46
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
47
+ testToAr.set(test.fn, asyncResource)
48
+ asyncResource.runInAsyncScope(() => {
49
+ testStartCh.publish(test)
50
+ })
51
+ })
50
52
 
51
- if (this.test.pending) {
52
- status = 'skipped'
53
- } else if (this.test.state !== 'failed' && !this.test.timedOut) {
54
- status = 'pass'
55
- } else {
56
- status = 'fail'
57
- }
53
+ this.on('test end', (test) => {
54
+ const asyncResource = getTestAsyncResource(test)
55
+ let status
56
+ if (test.pending) {
57
+ status = 'skip'
58
+ } else if (test.state !== 'failed' && !test.timedOut) {
59
+ status = 'pass'
60
+ } else {
61
+ status = 'fail'
62
+ }
58
63
 
59
- testFinishCh.publish(status)
60
- }))
64
+ // if there are afterEach to be run, we don't finish the test yet
65
+ if (!test.parent._afterEach.length) {
66
+ asyncResource.runInAsyncScope(() => {
67
+ testFinishCh.publish(status)
68
+ })
69
+ }
70
+ })
61
71
 
62
- this.once('fail', AsyncResource.bind((test, err) => {
63
- errorCh.publish(err)
64
- }))
72
+ // If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
73
+ this.on('hook end', (hook) => {
74
+ const test = hook.ctx.currentTest
75
+ if (test && hook.parent._afterEach.includes(hook)) { // only if it's an afterEach
76
+ const isLastAfterEach = hook.parent._afterEach.indexOf(hook) === hook.parent._afterEach.length - 1
77
+ if (isLastAfterEach) {
78
+ const asyncResource = getTestAsyncResource(test)
79
+ asyncResource.runInAsyncScope(() => {
80
+ testFinishCh.publish('pass')
81
+ })
82
+ }
83
+ }
84
+ })
65
85
 
66
- this.once('pending', AsyncResource.bind((test) => {
67
- skipCh.publish(test)
68
- }))
86
+ this.on('fail', (testOrHook, err) => {
87
+ let test = testOrHook
88
+ const isHook = testOrHook.type === 'hook'
89
+ if (isHook && testOrHook.ctx) {
90
+ test = testOrHook.ctx.currentTest
91
+ }
92
+ const asyncResource = getTestAsyncResource(test)
93
+ if (asyncResource) {
94
+ asyncResource.runInAsyncScope(() => {
95
+ if (isHook) {
96
+ err.message = `${testOrHook.title}: ${err.message}`
97
+ errorCh.publish(err)
98
+ // if it's a hook and it has failed, 'test end' will not be called
99
+ testFinishCh.publish('fail')
100
+ } else {
101
+ errorCh.publish(err)
102
+ }
103
+ })
104
+ }
105
+ })
69
106
 
70
- try {
71
- return runTest.apply(this, arguments)
72
- } catch (err) {
73
- errorCh.publish(err)
74
- throw err
107
+ this.on('pending', (test) => {
108
+ const asyncResource = getTestAsyncResource(test)
109
+ if (asyncResource) {
110
+ asyncResource.runInAsyncScope(() => {
111
+ skipCh.publish(test)
112
+ })
113
+ } else {
114
+ // if there is no async resource, the test has been skipped through `test.skip``
115
+ const skippedTestAsyncResource = new AsyncResource('bound-anonymous-fn')
116
+ testToAr.set(test, skippedTestAsyncResource)
117
+ skippedTestAsyncResource.runInAsyncScope(() => {
118
+ skipCh.publish(test)
119
+ })
75
120
  }
76
121
  })
122
+
123
+ return run.apply(this, arguments)
77
124
  })
78
125
 
79
126
  shimmer.wrap(Runner.prototype, 'runTests', runTests => function () {
80
- if (!suiteFinishCh.hasSubscribers) {
127
+ if (!testRunFinishCh.hasSubscribers) {
81
128
  return runTests.apply(this, arguments)
82
129
  }
83
130
  this.once('end', AsyncResource.bind(() => {
84
131
  testRunFinishCh.publish()
85
132
  }))
86
- runTests.apply(this, arguments)
87
- const suite = arguments[0]
88
- // We call `getAllTestsInSuite` with the root suite so every skipped test
89
- // should already have an associated test span.
90
- const tests = getAllTestsInSuite(suite)
91
- suiteFinishCh.publish(tests)
92
- })
93
-
94
- shimmer.wrap(Runner.prototype, 'fail', fail => function (hook, error) {
95
- if (!hookErrorCh.hasSubscribers) {
96
- return fail.apply(this, arguments)
97
- }
98
- if (error && hook.ctx && hook.ctx.currentTest) {
99
- error.message = `${hook.title}: ${error.message}`
100
- hookErrorCh.publish({ test: hook.ctx.currentTest, error })
101
- }
102
- return fail.apply(this, arguments)
133
+ return runTests.apply(this, arguments)
103
134
  })
104
135
 
105
136
  return Runner
@@ -129,6 +160,41 @@ addHook({
129
160
  file: 'lib/runner.js'
130
161
  }, mochaHook)
131
162
 
163
+ addHook({
164
+ name: 'mocha',
165
+ versions: ['>=5.2.0'],
166
+ file: 'lib/runnable.js'
167
+ }, (Runnable) => {
168
+ shimmer.wrap(Runnable.prototype, 'run', run => function () {
169
+ const isBeforeEach = this.parent._beforeEach.includes(this)
170
+ const isAfterEach = this.parent._afterEach.includes(this)
171
+
172
+ const isTestHook = isBeforeEach || isAfterEach
173
+
174
+ // we restore the original user defined function
175
+ if (this.fn.asyncResource) {
176
+ const originalFn = originalFns.get(this.fn)
177
+ this.fn = originalFn
178
+ }
179
+
180
+ if (isTestHook || this.type === 'test') {
181
+ const test = isTestHook ? this.ctx.currentTest : this
182
+ const asyncResource = getTestAsyncResource(test)
183
+
184
+ // we bind the test fn to the correct async resource
185
+ const newFn = asyncResource.bind(this.fn)
186
+
187
+ // we store the original function, not to lose it
188
+ originalFns.set(newFn, this.fn)
189
+
190
+ this.fn = newFn
191
+ }
192
+
193
+ return run.apply(this, arguments)
194
+ })
195
+ return Runnable
196
+ })
197
+
132
198
  addHook({
133
199
  name: 'mocha-each',
134
200
  versions: ['>=2.0.1']
@@ -0,0 +1,140 @@
1
+ 'use strict'
2
+
3
+ // TODO: either instrument all or none of the render functions
4
+
5
+ const { channel, addHook, AsyncResource } = require('./helpers/instrument')
6
+ const shimmer = require('../../datadog-shimmer')
7
+
8
+ const startChannel = channel('apm:next:request:start')
9
+ const finishChannel = channel('apm:next:request:finish')
10
+ const errorChannel = channel('apm:next:request:error')
11
+ const pageLoadChannel = channel('apm:next:page:load')
12
+
13
+ const requestResources = new WeakMap()
14
+
15
+ function wrapHandleRequest (handleRequest) {
16
+ return function (req, res, pathname, query) {
17
+ return instrument(req, res, () => handleRequest.apply(this, arguments))
18
+ }
19
+ }
20
+
21
+ function wrapHandleApiRequest (handleApiRequest) {
22
+ return function (req, res, pathname, query) {
23
+ return instrument(req, res, () => {
24
+ const promise = handleApiRequest.apply(this, arguments)
25
+
26
+ return promise.then(handled => {
27
+ if (!handled) return handled
28
+
29
+ const page = getPageFromPath(pathname, this.dynamicRoutes)
30
+
31
+ pageLoadChannel.publish({ page })
32
+
33
+ return handled
34
+ })
35
+ })
36
+ }
37
+ }
38
+
39
+ function wrapRenderToResponse (renderToResponse) {
40
+ return function (ctx) {
41
+ return instrument(ctx.req, ctx.res, () => renderToResponse.apply(this, arguments))
42
+ }
43
+ }
44
+
45
+ function wrapRenderErrorToResponse (renderErrorToResponse) {
46
+ return function (ctx) {
47
+ return instrument(ctx.req, ctx.res, () => renderErrorToResponse.apply(this, arguments))
48
+ }
49
+ }
50
+
51
+ function wrapRenderToHTML (renderToHTML) {
52
+ return function (req, res, pathname, query, parsedUrl) {
53
+ return instrument(req, res, () => renderToHTML.apply(this, arguments))
54
+ }
55
+ }
56
+
57
+ function wrapRenderErrorToHTML (renderErrorToHTML) {
58
+ return function (err, req, res, pathname, query) {
59
+ return instrument(req, res, () => renderErrorToHTML.apply(this, arguments))
60
+ }
61
+ }
62
+
63
+ function wrapFindPageComponents (findPageComponents) {
64
+ return function (pathname, query) {
65
+ const result = findPageComponents.apply(this, arguments)
66
+
67
+ if (result) {
68
+ pageLoadChannel.publish({ page: pathname })
69
+ }
70
+
71
+ return result
72
+ }
73
+ }
74
+
75
+ function getPageFromPath (page, dynamicRoutes = []) {
76
+ for (const dynamicRoute of dynamicRoutes) {
77
+ if (dynamicRoute.page.startsWith('/api') && dynamicRoute.match(page)) {
78
+ return dynamicRoute.page
79
+ }
80
+ }
81
+
82
+ return page
83
+ }
84
+
85
+ function instrument (req, res, handler) {
86
+ if (requestResources.has(req)) return handler()
87
+
88
+ const requestResource = new AsyncResource('bound-anonymous-fn')
89
+
90
+ requestResources.set(req, requestResource)
91
+
92
+ return requestResource.runInAsyncScope(() => {
93
+ startChannel.publish({ req, res })
94
+
95
+ try {
96
+ const promise = handler()
97
+
98
+ return promise.then(
99
+ result => finish(req, res, result),
100
+ err => finish(req, res, null, err)
101
+ )
102
+ } catch (e) {
103
+ finish(req, res, null, e)
104
+ }
105
+ })
106
+ }
107
+
108
+ function finish (req, res, result, err) {
109
+ if (err) {
110
+ errorChannel.publish(err)
111
+ }
112
+
113
+ finishChannel.publish({ req, res })
114
+
115
+ return result || err
116
+ }
117
+
118
+ addHook({ name: 'next', versions: ['>=11.1'], file: 'dist/server/next-server.js' }, nextServer => {
119
+ const Server = nextServer.default
120
+
121
+ shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
122
+ shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequest)
123
+ shimmer.wrap(Server.prototype, 'renderToResponse', wrapRenderToResponse)
124
+ shimmer.wrap(Server.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse)
125
+ shimmer.wrap(Server.prototype, 'findPageComponents', wrapFindPageComponents)
126
+
127
+ return nextServer
128
+ })
129
+
130
+ addHook({ name: 'next', versions: ['>=9.5 <11.1'], file: 'dist/next-server/server/next-server.js' }, nextServer => {
131
+ const Server = nextServer.default
132
+
133
+ shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
134
+ shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequest)
135
+ shimmer.wrap(Server.prototype, 'renderToHTML', wrapRenderToHTML)
136
+ shimmer.wrap(Server.prototype, 'renderErrorToHTML', wrapRenderErrorToHTML)
137
+ shimmer.wrap(Server.prototype, 'findPageComponents', wrapFindPageComponents)
138
+
139
+ return nextServer
140
+ })
@@ -7,6 +7,7 @@ const { addHook, channel, AsyncResource } = require('./helpers/instrument')
7
7
 
8
8
  function createWrapRouterMethod (name) {
9
9
  const enterChannel = channel(`apm:${name}:middleware:enter`)
10
+ const exitChannel = channel(`apm:${name}:middleware:exit`)
10
11
  const errorChannel = channel(`apm:${name}:middleware:error`)
11
12
  const nextChannel = channel(`apm:${name}:middleware:next`)
12
13
 
@@ -51,6 +52,7 @@ function createWrapRouterMethod (name) {
51
52
  } catch (e) {
52
53
  errorChannel.publish(e)
53
54
  nextChannel.publish({ req })
55
+ exitChannel.publish({ req })
54
56
 
55
57
  throw e
56
58
  }
@@ -93,6 +95,7 @@ function createWrapRouterMethod (name) {
93
95
  }
94
96
 
95
97
  nextChannel.publish({ req })
98
+ exitChannel.publish({ req })
96
99
 
97
100
  next.apply(null, arguments)
98
101
  }
@@ -13,7 +13,7 @@ addHook({ name: 'tedious', versions: [ '>=1.0.0' ] }, tedious => {
13
13
  const errorCh = channel('apm:tedious:request:error')
14
14
  shimmer.wrap(tedious.Connection.prototype, 'makeRequest', makeRequest => function (request) {
15
15
  if (!startCh.hasSubscribers) {
16
- return request.apply(this, arguments)
16
+ return makeRequest.apply(this, arguments)
17
17
  }
18
18
 
19
19
  const queryOrProcedure = getQueryOrProcedure(request)