dd-trace 5.0.0-pre-069b62a → 5.0.0-pre-e2df7ec

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 (76) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +5 -0
  3. package/package.json +4 -3
  4. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  5. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  6. package/packages/datadog-instrumentations/src/child-process.js +4 -5
  7. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  8. package/packages/datadog-instrumentations/src/dns.js +2 -1
  9. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  10. package/packages/datadog-instrumentations/src/helpers/hooks.js +9 -2
  11. package/packages/datadog-instrumentations/src/helpers/instrument.js +8 -3
  12. package/packages/datadog-instrumentations/src/helpers/register.js +18 -2
  13. package/packages/datadog-instrumentations/src/http/client.js +4 -16
  14. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  15. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  16. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  17. package/packages/datadog-instrumentations/src/jest.js +1 -1
  18. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  19. package/packages/datadog-instrumentations/src/net.js +10 -2
  20. package/packages/datadog-instrumentations/src/next.js +15 -5
  21. package/packages/datadog-instrumentations/src/restify.js +1 -1
  22. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  23. package/packages/datadog-plugin-cucumber/src/index.js +34 -2
  24. package/packages/datadog-plugin-cypress/src/plugin.js +60 -8
  25. package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
  26. package/packages/datadog-plugin-http/src/client.js +1 -1
  27. package/packages/datadog-plugin-jest/src/index.js +38 -4
  28. package/packages/datadog-plugin-kafkajs/src/consumer.js +51 -0
  29. package/packages/datadog-plugin-kafkajs/src/producer.js +55 -0
  30. package/packages/datadog-plugin-mocha/src/index.js +32 -1
  31. package/packages/datadog-plugin-next/src/index.js +32 -6
  32. package/packages/datadog-plugin-playwright/src/index.js +17 -1
  33. package/packages/dd-trace/src/appsec/activation.js +29 -0
  34. package/packages/dd-trace/src/appsec/addresses.js +1 -0
  35. package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
  36. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  37. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  38. package/packages/dd-trace/src/appsec/channels.js +4 -1
  39. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  40. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +16 -7
  41. package/packages/dd-trace/src/appsec/index.js +29 -40
  42. package/packages/dd-trace/src/appsec/recommended.json +1395 -2
  43. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
  44. package/packages/dd-trace/src/appsec/remote_config/index.js +40 -15
  45. package/packages/dd-trace/src/appsec/reporter.js +2 -4
  46. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  47. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +25 -13
  48. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  49. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  50. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +36 -4
  51. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +18 -1
  52. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +26 -1
  53. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  54. package/packages/dd-trace/src/config.js +110 -56
  55. package/packages/dd-trace/src/datastreams/processor.js +107 -12
  56. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +14 -1
  57. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  58. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  59. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  60. package/packages/dd-trace/src/opentracing/span.js +2 -0
  61. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  62. package/packages/dd-trace/src/plugins/ci_plugin.js +44 -8
  63. package/packages/dd-trace/src/plugins/index.js +5 -0
  64. package/packages/dd-trace/src/plugins/util/exec.js +23 -2
  65. package/packages/dd-trace/src/plugins/util/git.js +94 -19
  66. package/packages/dd-trace/src/plugins/util/user-provided-git.js +3 -2
  67. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -0
  68. package/packages/dd-trace/src/profiling/profiler.js +7 -6
  69. package/packages/dd-trace/src/profiling/profilers/events.js +19 -51
  70. package/packages/dd-trace/src/profiling/profilers/shared.js +34 -4
  71. package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
  72. package/packages/dd-trace/src/profiling/profilers/wall.js +17 -12
  73. package/packages/dd-trace/src/proxy.js +14 -0
  74. package/packages/dd-trace/src/spanleak.js +98 -0
  75. package/packages/dd-trace/src/telemetry/index.js +43 -5
  76. package/packages/dd-trace/src/tracer.js +4 -0
@@ -30,6 +30,7 @@ require,opentracing,MIT,Copyright 2016 Resonance Labs Inc
30
30
  require,path-to-regexp,MIT,Copyright 2014 Blake Embrey
31
31
  require,pprof-format,MIT,Copyright 2022 Stephen Belanger
32
32
  require,protobufjs,BSD-3-Clause,Copyright 2016 Daniel Wirtz
33
+ require,tlhunter-sorted-set,MIT,Copyright (c) 2023 Datadog Inc.
33
34
  require,retry,MIT,Copyright 2011 Tim Koschützki Felix Geisendörfer
34
35
  require,semver,ISC,Copyright Isaac Z. Schlueter and Contributors
35
36
  dev,@types/node,MIT,Copyright Authors
package/index.d.ts CHANGED
@@ -567,6 +567,11 @@ export declare interface TracerOptions {
567
567
  */
568
568
  blockedTemplateJson?: string,
569
569
 
570
+ /**
571
+ * Specifies a path to a custom blocking template json file for graphql requests
572
+ */
573
+ blockedTemplateGraphql?: string,
574
+
570
575
  /**
571
576
  * Controls the automated user event tracking configuration
572
577
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.0.0-pre-069b62a",
3
+ "version": "5.0.0-pre-e2df7ec",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -68,8 +68,8 @@
68
68
  "node": ">=16"
69
69
  },
70
70
  "dependencies": {
71
- "@datadog/native-appsec": "5.0.0",
72
- "@datadog/native-iast-rewriter": "2.2.1",
71
+ "@datadog/native-appsec": "6.0.0",
72
+ "@datadog/native-iast-rewriter": "2.2.2",
73
73
  "@datadog/native-iast-taint-tracking": "1.6.4",
74
74
  "@datadog/native-metrics": "^2.0.0",
75
75
  "@datadog/pprof": "4.1.0",
@@ -99,6 +99,7 @@
99
99
  "path-to-regexp": "^0.1.2",
100
100
  "pprof-format": "^2.0.7",
101
101
  "protobufjs": "^7.2.5",
102
+ "tlhunter-sorted-set": "^0.1.0",
102
103
  "retry": "^0.13.1",
103
104
  "semver": "^7.5.4"
104
105
  },
@@ -0,0 +1,41 @@
1
+ 'use strict'
2
+
3
+ const { AbortController } = require('node-abort-controller')
4
+ const { addHook } = require('./helpers/instrument')
5
+ const shimmer = require('../../datadog-shimmer')
6
+ const dc = require('dc-polyfill')
7
+
8
+ const requestChannel = dc.tracingChannel('datadog:apollo-server-core:request')
9
+
10
+ addHook({ name: 'apollo-server-core', file: 'dist/runHttpQuery.js', versions: ['>3.0.0'] }, runHttpQueryModule => {
11
+ const HttpQueryError = runHttpQueryModule.HttpQueryError
12
+
13
+ shimmer.wrap(runHttpQueryModule, 'runHttpQuery', function wrapRunHttpQuery (originalRunHttpQuery) {
14
+ return async function runHttpQuery () {
15
+ if (!requestChannel.start.hasSubscribers) {
16
+ return originalRunHttpQuery.apply(this, arguments)
17
+ }
18
+
19
+ const abortController = new AbortController()
20
+ const abortData = {}
21
+
22
+ const runHttpQueryResult = requestChannel.tracePromise(
23
+ originalRunHttpQuery,
24
+ { abortController, abortData },
25
+ this,
26
+ ...arguments)
27
+
28
+ const abortPromise = new Promise((resolve, reject) => {
29
+ abortController.signal.addEventListener('abort', (event) => {
30
+ // runHttpQuery callbacks are writing the response on resolve/reject.
31
+ // We should return blocking data in the apollo-server-core HttpQueryError object
32
+ reject(new HttpQueryError(abortData.statusCode, abortData.message, true, abortData.headers))
33
+ }, { once: true })
34
+ })
35
+
36
+ return Promise.race([runHttpQueryResult, abortPromise])
37
+ }
38
+ })
39
+
40
+ return runHttpQueryModule
41
+ })
@@ -0,0 +1,83 @@
1
+ 'use strict'
2
+
3
+ const { AbortController } = require('node-abort-controller')
4
+ const dc = require('dc-polyfill')
5
+
6
+ const { addHook } = require('./helpers/instrument')
7
+ const shimmer = require('../../datadog-shimmer')
8
+
9
+ const graphqlMiddlewareChannel = dc.tracingChannel('datadog:apollo:middleware')
10
+
11
+ const requestChannel = dc.tracingChannel('datadog:apollo:request')
12
+
13
+ let HeaderMap
14
+
15
+ function wrapExecuteHTTPGraphQLRequest (originalExecuteHTTPGraphQLRequest) {
16
+ return async function executeHTTPGraphQLRequest () {
17
+ if (!HeaderMap || !requestChannel.start.hasSubscribers) {
18
+ return originalExecuteHTTPGraphQLRequest.apply(this, arguments)
19
+ }
20
+
21
+ const abortController = new AbortController()
22
+ const abortData = {}
23
+
24
+ const graphqlResponseData = requestChannel.tracePromise(
25
+ originalExecuteHTTPGraphQLRequest,
26
+ { abortController, abortData },
27
+ this,
28
+ ...arguments)
29
+
30
+ const abortPromise = new Promise((resolve, reject) => {
31
+ abortController.signal.addEventListener('abort', (event) => {
32
+ // This method is expected to return response data
33
+ // with headers, status and body
34
+ const headers = new HeaderMap()
35
+ Object.keys(abortData.headers).forEach(key => {
36
+ headers.set(key, abortData.headers[key])
37
+ })
38
+
39
+ resolve({
40
+ headers: headers,
41
+ status: abortData.statusCode,
42
+ body: {
43
+ kind: 'complete',
44
+ string: abortData.message
45
+ }
46
+ })
47
+ }, { once: true })
48
+ })
49
+
50
+ return Promise.race([abortPromise, graphqlResponseData])
51
+ }
52
+ }
53
+
54
+ function apolloExpress4Hook (express4) {
55
+ shimmer.wrap(express4, 'expressMiddleware', function wrapExpressMiddleware (originalExpressMiddleware) {
56
+ return function expressMiddleware (server, options) {
57
+ const originalMiddleware = originalExpressMiddleware.apply(this, arguments)
58
+
59
+ return shimmer.wrap(originalMiddleware, function (req, res, next) {
60
+ if (!graphqlMiddlewareChannel.start.hasSubscribers) {
61
+ return originalMiddleware.apply(this, arguments)
62
+ }
63
+
64
+ return graphqlMiddlewareChannel.traceSync(originalMiddleware, { req }, this, ...arguments)
65
+ })
66
+ }
67
+ })
68
+ return express4
69
+ }
70
+
71
+ function apolloHeaderMapHook (headerMap) {
72
+ HeaderMap = headerMap.HeaderMap
73
+ return headerMap
74
+ }
75
+
76
+ function apolloServerHook (apolloServer) {
77
+ shimmer.wrap(apolloServer.ApolloServer.prototype, 'executeHTTPGraphQLRequest', wrapExecuteHTTPGraphQLRequest)
78
+ return apolloServer
79
+ }
80
+
81
+ addHook({ name: '@apollo/server', file: 'dist/cjs/ApolloServer.js', versions: ['>=4.0.0'] }, apolloServerHook)
82
+ addHook({ name: '@apollo/server', file: 'dist/cjs/express4/index.js', versions: ['>=4.0.0'] }, apolloExpress4Hook)
83
+ addHook({ name: '@apollo/server', file: 'dist/cjs/utils/HeaderMap.js', versions: ['>=4.0.0'] }, apolloHeaderMapHook)
@@ -9,11 +9,10 @@ const shimmer = require('../../datadog-shimmer')
9
9
  const childProcessChannel = channel('datadog:child_process:execution:start')
10
10
  const execMethods = ['exec', 'execFile', 'fork', 'spawn', 'execFileSync', 'execSync', 'spawnSync']
11
11
  const names = ['child_process', 'node:child_process']
12
- names.forEach(name => {
13
- addHook({ name }, childProcess => {
14
- shimmer.massWrap(childProcess, execMethods, wrapChildProcessMethod())
15
- return childProcess
16
- })
12
+
13
+ addHook({ name: names }, childProcess => {
14
+ shimmer.massWrap(childProcess, execMethods, wrapChildProcessMethod())
15
+ return childProcess
17
16
  })
18
17
 
19
18
  function wrapChildProcessMethod () {
@@ -11,8 +11,9 @@ const cryptoCipherCh = channel('datadog:crypto:cipher:start')
11
11
 
12
12
  const hashMethods = ['createHash', 'createHmac', 'createSign', 'createVerify', 'sign', 'verify']
13
13
  const cipherMethods = ['createCipheriv', 'createDecipheriv']
14
+ const names = ['crypto', 'node:crypto']
14
15
 
15
- addHook({ name: 'crypto' }, crypto => {
16
+ addHook({ name: names }, crypto => {
16
17
  shimmer.massWrap(crypto, hashMethods, wrapCryptoMethod(cryptoHashCh))
17
18
  shimmer.massWrap(crypto, cipherMethods, wrapCryptoMethod(cryptoCipherCh))
18
19
  return crypto
@@ -18,8 +18,9 @@ const rrtypes = {
18
18
  }
19
19
 
20
20
  const rrtypeMap = new WeakMap()
21
+ const names = ['dns', 'node:dns']
21
22
 
22
- addHook({ name: 'dns' }, dns => {
23
+ addHook({ name: names }, dns => {
23
24
  dns.lookup = wrap('apm:dns:lookup', dns.lookup, 2)
24
25
  dns.lookupService = wrap('apm:dns:lookup_service', dns.lookupService, 3)
25
26
  dns.resolve = wrap('apm:dns:resolve', dns.resolve, 2)
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { AbortController } = require('node-abort-controller')
4
+
3
5
  const {
4
6
  addHook,
5
7
  channel,
@@ -37,6 +39,13 @@ const validateStartCh = channel('apm:graphql:validate:start')
37
39
  const validateFinishCh = channel('apm:graphql:validate:finish')
38
40
  const validateErrorCh = channel('apm:graphql:validate:error')
39
41
 
42
+ class AbortError extends Error {
43
+ constructor (message) {
44
+ super(message)
45
+ this.name = 'AbortError'
46
+ }
47
+ }
48
+
40
49
  function getOperation (document, operationName) {
41
50
  if (!document || !Array.isArray(document.definitions)) {
42
51
  return
@@ -175,11 +184,11 @@ function wrapExecute (execute) {
175
184
  docSource: documentSources.get(document)
176
185
  })
177
186
 
178
- const context = { source, asyncResource, fields: {} }
187
+ const context = { source, asyncResource, fields: {}, abortController: new AbortController() }
179
188
 
180
189
  contexts.set(contextValue, context)
181
190
 
182
- return callInAsyncScope(exe, asyncResource, this, arguments, (err, res) => {
191
+ return callInAsyncScope(exe, asyncResource, this, arguments, context.abortController, (err, res) => {
183
192
  if (finishResolveCh.hasSubscribers) finishResolvers(context)
184
193
 
185
194
  const error = err || (res && res.errors && res.errors[0])
@@ -207,7 +216,7 @@ function wrapResolve (resolve) {
207
216
 
208
217
  const field = assertField(context, info, args)
209
218
 
210
- return callInAsyncScope(resolve, field.asyncResource, this, arguments, (err) => {
219
+ return callInAsyncScope(resolve, field.asyncResource, this, arguments, context.abortController, (err) => {
211
220
  updateFieldCh.publish({ field, info, err })
212
221
  })
213
222
  }
@@ -217,10 +226,15 @@ function wrapResolve (resolve) {
217
226
  return resolveAsync
218
227
  }
219
228
 
220
- function callInAsyncScope (fn, aR, thisArg, args, cb) {
229
+ function callInAsyncScope (fn, aR, thisArg, args, abortController, cb) {
221
230
  cb = cb || (() => {})
222
231
 
223
232
  return aR.runInAsyncScope(() => {
233
+ if (abortController?.signal.aborted) {
234
+ cb(null, null)
235
+ throw new AbortError('Aborted')
236
+ }
237
+
224
238
  try {
225
239
  const result = fn.apply(thisArg, args)
226
240
  if (result && typeof result.then === 'function') {
@@ -1,6 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  module.exports = {
4
+ '@apollo/server': () => require('../apollo-server'),
5
+ 'apollo-server-core': () => require('../apollo-server-core'),
4
6
  '@aws-sdk/smithy-client': () => require('../aws-sdk'),
5
7
  '@cucumber/cucumber': () => require('../cucumber'),
6
8
  '@playwright/test': () => require('../playwright'),
@@ -29,7 +31,6 @@ module.exports = {
29
31
  'bunyan': () => require('../bunyan'),
30
32
  'cassandra-driver': () => require('../cassandra-driver'),
31
33
  'child_process': () => require('../child-process'),
32
- 'node:child_process': () => require('../child-process'),
33
34
  'connect': () => require('../connect'),
34
35
  'cookie': () => require('../cookie'),
35
36
  'cookie-parser': () => require('../cookie-parser'),
@@ -43,7 +44,6 @@ module.exports = {
43
44
  'fastify': () => require('../fastify'),
44
45
  'find-my-way': () => require('../find-my-way'),
45
46
  'fs': () => require('../fs'),
46
- 'node:fs': () => require('../fs'),
47
47
  'generic-pool': () => require('../generic-pool'),
48
48
  'graphql': () => require('../graphql'),
49
49
  'grpc': () => require('../grpc'),
@@ -77,6 +77,13 @@ module.exports = {
77
77
  'mysql2': () => require('../mysql2'),
78
78
  'net': () => require('../net'),
79
79
  'next': () => require('../next'),
80
+ 'node:child_process': () => require('../child-process'),
81
+ 'node:crypto': () => require('../crypto'),
82
+ 'node:dns': () => require('../dns'),
83
+ 'node:http': () => require('../http'),
84
+ 'node:http2': () => require('../http2'),
85
+ 'node:https': () => require('../http'),
86
+ 'node:net': () => require('../net'),
80
87
  'oracledb': () => require('../oracledb'),
81
88
  'openai': () => require('../openai'),
82
89
  'paperplane': () => require('../paperplane'),
@@ -21,11 +21,16 @@ exports.channel = function (name) {
21
21
  * @param Function hook
22
22
  */
23
23
  exports.addHook = function addHook ({ name, versions, file }, hook) {
24
- if (!instrumentations[name]) {
25
- instrumentations[name] = []
24
+ if (typeof name === 'string') {
25
+ name = [name]
26
26
  }
27
27
 
28
- instrumentations[name].push({ name, versions, file, hook })
28
+ for (const val of name) {
29
+ if (!instrumentations[val]) {
30
+ instrumentations[val] = []
31
+ }
32
+ instrumentations[val].push({ name: val, versions, file, hook })
33
+ }
29
34
  }
30
35
 
31
36
  // AsyncResource.bind exists and binds `this` properly only from 17.8.0 and up.
@@ -24,6 +24,7 @@ if (!disabledInstrumentations.has('fetch')) {
24
24
  require('../fetch')
25
25
  }
26
26
 
27
+ const HOOK_SYMBOL = Symbol('hookExportsMap')
27
28
  // TODO: make this more efficient
28
29
 
29
30
  for (const packageName of names) {
@@ -42,14 +43,29 @@ for (const packageName of names) {
42
43
  for (const { name, file, versions, hook } of instrumentations[packageName]) {
43
44
  const fullFilename = filename(name, file)
44
45
 
46
+ // Create a WeakMap associated with the hook function so that patches on the same moduleExport only happens once
47
+ // for example by instrumenting both dns and node:dns double the spans would be created
48
+ // since they both patch the same moduleExport, this WeakMap is used to mitigate that
49
+ if (!hook[HOOK_SYMBOL]) {
50
+ hook[HOOK_SYMBOL] = new WeakMap()
51
+ }
52
+
45
53
  if (moduleName === fullFilename) {
46
54
  const version = moduleVersion || getVersion(moduleBaseDir)
47
55
 
48
56
  if (matchVersion(version, versions)) {
57
+ // Check if the hook already has a set moduleExport
58
+ if (hook[HOOK_SYMBOL].has(moduleExports)) {
59
+ return moduleExports
60
+ }
61
+
49
62
  try {
50
63
  loadChannel.publish({ name, version, file })
51
-
52
- moduleExports = hook(moduleExports, version)
64
+ // Send the name and version of the module back to the callback because now addHook
65
+ // takes in an array of names so by passing the name the callback will know which module name is being used
66
+ moduleExports = hook(moduleExports, version, name)
67
+ // Set the moduleExports in the hooks weakmap
68
+ hook[HOOK_SYMBOL].set(moduleExports, name)
53
69
  } catch (e) {
54
70
  log.error(e)
55
71
  }
@@ -14,9 +14,9 @@ const endChannel = channel('apm:http:client:request:end')
14
14
  const asyncStartChannel = channel('apm:http:client:request:asyncStart')
15
15
  const errorChannel = channel('apm:http:client:request:error')
16
16
 
17
- addHook({ name: 'https' }, hookFn)
17
+ const names = ['http', 'https', 'node:http', 'node:https']
18
18
 
19
- addHook({ name: 'http' }, hookFn)
19
+ addHook({ name: names }, hookFn)
20
20
 
21
21
  function hookFn (http) {
22
22
  patch(http, 'request')
@@ -69,29 +69,17 @@ function patch (http, methodName) {
69
69
  try {
70
70
  const req = request.call(this, options, callback)
71
71
  const emit = req.emit
72
-
73
- const requestSetTimeout = req.setTimeout
72
+ const setTimeout = req.setTimeout
74
73
 
75
74
  ctx.req = req
76
75
 
77
76
  // tracked to accurately discern custom request socket timeout
78
77
  let customRequestTimeout = false
79
-
80
78
  req.setTimeout = function () {
81
79
  customRequestTimeout = true
82
- return requestSetTimeout.apply(this, arguments)
80
+ return setTimeout.apply(this, arguments)
83
81
  }
84
82
 
85
- req.on('socket', socket => {
86
- if (socket) {
87
- const socketSetTimeout = socket.setTimeout
88
- socket.setTimeout = function () {
89
- customRequestTimeout = true
90
- return socketSetTimeout.apply(this, arguments)
91
- }
92
- }
93
- })
94
-
95
83
  req.emit = function (eventName, arg) {
96
84
  switch (eventName) {
97
85
  case 'response': {
@@ -15,14 +15,17 @@ const finishSetHeaderCh = channel('datadog:http:server:response:set-header:finis
15
15
 
16
16
  const requestFinishedSet = new WeakSet()
17
17
 
18
- addHook({ name: 'https' }, http => {
19
- // http.ServerResponse not present on https
18
+ const httpNames = ['http', 'node:http']
19
+ const httpsNames = ['https', 'node:https']
20
+
21
+ addHook({ name: httpNames }, http => {
22
+ shimmer.wrap(http.ServerResponse.prototype, 'emit', wrapResponseEmit)
20
23
  shimmer.wrap(http.Server.prototype, 'emit', wrapEmit)
21
24
  return http
22
25
  })
23
26
 
24
- addHook({ name: 'http' }, http => {
25
- shimmer.wrap(http.ServerResponse.prototype, 'emit', wrapResponseEmit)
27
+ addHook({ name: httpsNames }, http => {
28
+ // http.ServerResponse not present on https
26
29
  shimmer.wrap(http.Server.prototype, 'emit', wrapEmit)
27
30
  return http
28
31
  })
@@ -10,6 +10,8 @@ const asyncStartChannel = channel('apm:http2:client:request:asyncStart')
10
10
  const asyncEndChannel = channel('apm:http2:client:request:asyncEnd')
11
11
  const errorChannel = channel('apm:http2:client:request:error')
12
12
 
13
+ const names = ['http2', 'node:http2']
14
+
13
15
  function createWrapEmit (ctx) {
14
16
  return function wrapEmit (emit) {
15
17
  return function (event, arg1) {
@@ -66,7 +68,7 @@ function wrapConnect (connect) {
66
68
  }
67
69
  }
68
70
 
69
- addHook({ name: 'http2' }, http2 => {
71
+ addHook({ name: names }, http2 => {
70
72
  shimmer.wrap(http2, 'connect', wrapConnect)
71
73
 
72
74
  return http2
@@ -14,7 +14,9 @@ const startServerCh = channel('apm:http2:server:request:start')
14
14
  const errorServerCh = channel('apm:http2:server:request:error')
15
15
  const finishServerCh = channel('apm:http2:server:request:finish')
16
16
 
17
- addHook({ name: 'http2' }, http2 => {
17
+ const names = ['http2', 'node:http2']
18
+
19
+ addHook({ name: names }, http2 => {
18
20
  shimmer.wrap(http2, 'createSecureServer', wrapCreateServer)
19
21
  shimmer.wrap(http2, 'createServer', wrapCreateServer)
20
22
  return http2
@@ -403,7 +403,7 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
403
403
  const coverageFiles = getCoveredFilenamesFromCoverage(environment.global.__coverage__)
404
404
  .map(filename => getTestSuitePath(filename, environment.rootDir))
405
405
  asyncResource.runInAsyncScope(() => {
406
- testSuiteCodeCoverageCh.publish([...coverageFiles, environment.testSuite])
406
+ testSuiteCodeCoverageCh.publish({ coverageFiles, testSuite: environment.testSuite })
407
407
  })
408
408
  }
409
409
  testSuiteFinishCh.publish({ status, errorMessage })
@@ -8,13 +8,31 @@ const {
8
8
  const shimmer = require('../../datadog-shimmer')
9
9
 
10
10
  const producerStartCh = channel('apm:kafkajs:produce:start')
11
+ const producerCommitCh = channel('apm:kafkajs:produce:commit')
11
12
  const producerFinishCh = channel('apm:kafkajs:produce:finish')
12
13
  const producerErrorCh = channel('apm:kafkajs:produce:error')
13
14
 
14
15
  const consumerStartCh = channel('apm:kafkajs:consume:start')
16
+ const consumerCommitCh = channel('apm:kafkajs:consume:commit')
15
17
  const consumerFinishCh = channel('apm:kafkajs:consume:finish')
16
18
  const consumerErrorCh = channel('apm:kafkajs:consume:error')
17
19
 
20
+ function commitsFromEvent (event) {
21
+ const { payload: { groupId, topics } } = event
22
+ const commitList = []
23
+ for (const { topic, partitions } of topics) {
24
+ for (const { partition, offset } of partitions) {
25
+ commitList.push({
26
+ groupId,
27
+ partition,
28
+ offset,
29
+ topic
30
+ })
31
+ }
32
+ }
33
+ consumerCommitCh.publish(commitList)
34
+ }
35
+
18
36
  addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKafka) => {
19
37
  class Kafka extends BaseKafka {
20
38
  constructor (options) {
@@ -58,6 +76,12 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
58
76
  })
59
77
  )
60
78
 
79
+ result.then(res => {
80
+ if (producerCommitCh.hasSubscribers) {
81
+ producerCommitCh.publish(res)
82
+ }
83
+ })
84
+
61
85
  return result
62
86
  } catch (e) {
63
87
  producerErrorCh.publish(e)
@@ -75,6 +99,9 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
75
99
  }
76
100
 
77
101
  const consumer = createConsumer.apply(this, arguments)
102
+
103
+ consumer.on(consumer.events.COMMIT_OFFSETS, commitsFromEvent)
104
+
78
105
  const run = consumer.run
79
106
 
80
107
  const groupId = arguments[0].groupId
@@ -17,8 +17,16 @@ const errorTCPCh = channel('apm:net:tcp:error')
17
17
 
18
18
  const connectionCh = channel(`apm:net:tcp:connection`)
19
19
 
20
- addHook({ name: 'net' }, net => {
21
- require('dns')
20
+ const names = ['net', 'node:net']
21
+
22
+ addHook({ name: names }, (net, version, name) => {
23
+ // explicitly require dns so that net gets an instrumented instance
24
+ // so that we don't miss the dns calls
25
+ if (name === 'net') {
26
+ require('dns')
27
+ } else {
28
+ require('node:dns')
29
+ }
22
30
 
23
31
  shimmer.wrap(net.Socket.prototype, 'connect', connect => function () {
24
32
  if (!startICPCh.hasSubscribers || !startTCPCh.hasSubscribers) {
@@ -65,7 +65,7 @@ function wrapRenderToHTML (renderToHTML) {
65
65
 
66
66
  function wrapRenderErrorToHTML (renderErrorToHTML) {
67
67
  return function (err, req, res, pathname, query) {
68
- return instrument(req, res, () => renderErrorToHTML.apply(this, arguments))
68
+ return instrument(req, res, err, () => renderErrorToHTML.apply(this, arguments))
69
69
  }
70
70
  }
71
71
 
@@ -76,8 +76,8 @@ function wrapRenderToResponse (renderToResponse) {
76
76
  }
77
77
 
78
78
  function wrapRenderErrorToResponse (renderErrorToResponse) {
79
- return function (ctx) {
80
- return instrument(ctx.req, ctx.res, () => renderErrorToResponse.apply(this, arguments))
79
+ return function (ctx, err) {
80
+ return instrument(ctx.req, ctx.res, err, () => renderErrorToResponse.apply(this, arguments))
81
81
  }
82
82
  }
83
83
 
@@ -111,13 +111,23 @@ function getPageFromPath (page, dynamicRoutes = []) {
111
111
  return getPagePath(page)
112
112
  }
113
113
 
114
- function instrument (req, res, handler) {
114
+ function instrument (req, res, error, handler) {
115
+ if (typeof error === 'function') {
116
+ handler = error
117
+ error = null
118
+ }
119
+
115
120
  req = req.originalRequest || req
116
121
  res = res.originalResponse || res
117
122
 
118
123
  // TODO support middleware properly in the future?
119
124
  const isMiddleware = req.headers[MIDDLEWARE_HEADER]
120
- if (isMiddleware || requests.has(req)) return handler()
125
+ if (isMiddleware || requests.has(req)) {
126
+ if (error) {
127
+ errorChannel.publish({ error })
128
+ }
129
+ return handler()
130
+ }
121
131
 
122
132
  requests.add(req)
123
133
 
@@ -55,7 +55,7 @@ function wrapFn (fn) {
55
55
  return result.then(function () {
56
56
  nextChannel.publish({ req })
57
57
  finishChannel.publish({ req })
58
- return arguments
58
+ return arguments[0]
59
59
  }).catch(function (error) {
60
60
  errorChannel.publish({ req, error })
61
61
  nextChannel.publish({ req })