dd-trace 4.18.0 → 4.23.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 (137) hide show
  1. package/LICENSE-3rdparty.csv +3 -2
  2. package/README.md +3 -3
  3. package/ext/kinds.d.ts +1 -0
  4. package/ext/kinds.js +2 -1
  5. package/ext/tags.d.ts +2 -1
  6. package/ext/tags.js +6 -1
  7. package/index.d.ts +29 -0
  8. package/package.json +12 -11
  9. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  10. package/packages/datadog-esbuild/index.js +1 -20
  11. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  12. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  13. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  14. package/packages/datadog-instrumentations/src/child-process.js +4 -5
  15. package/packages/datadog-instrumentations/src/couchbase.js +5 -4
  16. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  17. package/packages/datadog-instrumentations/src/dns.js +2 -1
  18. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  19. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
  20. package/packages/datadog-instrumentations/src/helpers/hooks.js +10 -2
  21. package/packages/datadog-instrumentations/src/helpers/instrument.js +9 -4
  22. package/packages/datadog-instrumentations/src/helpers/register.js +19 -3
  23. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  24. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  25. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  26. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  27. package/packages/datadog-instrumentations/src/jest.js +12 -6
  28. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  29. package/packages/datadog-instrumentations/src/net.js +10 -2
  30. package/packages/datadog-instrumentations/src/next.js +18 -6
  31. package/packages/datadog-instrumentations/src/restify.js +14 -1
  32. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  33. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  34. package/packages/datadog-plugin-cucumber/src/index.js +34 -2
  35. package/packages/datadog-plugin-cypress/src/plugin.js +60 -8
  36. package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
  37. package/packages/datadog-plugin-http/src/client.js +19 -2
  38. package/packages/datadog-plugin-jest/src/index.js +38 -4
  39. package/packages/datadog-plugin-kafkajs/src/consumer.js +59 -6
  40. package/packages/datadog-plugin-kafkajs/src/producer.js +64 -6
  41. package/packages/datadog-plugin-mocha/src/index.js +32 -1
  42. package/packages/datadog-plugin-next/src/index.js +40 -14
  43. package/packages/datadog-plugin-playwright/src/index.js +17 -1
  44. package/packages/dd-trace/src/appsec/activation.js +29 -0
  45. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  46. package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
  47. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  48. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  49. package/packages/dd-trace/src/appsec/channels.js +5 -2
  50. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  51. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  52. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  53. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  54. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  55. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  56. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
  57. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
  58. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  59. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  60. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  61. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  62. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  63. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  64. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  65. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  66. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  67. package/packages/dd-trace/src/appsec/index.js +33 -32
  68. package/packages/dd-trace/src/appsec/recommended.json +1737 -120
  69. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
  70. package/packages/dd-trace/src/appsec/remote_config/index.js +40 -15
  71. package/packages/dd-trace/src/appsec/reporter.js +50 -34
  72. package/packages/dd-trace/src/appsec/rule_manager.js +9 -6
  73. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  74. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  75. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  76. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  77. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  78. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
  79. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +110 -59
  80. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +40 -7
  81. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +26 -1
  82. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  83. package/packages/dd-trace/src/config.js +145 -63
  84. package/packages/dd-trace/src/datastreams/processor.js +166 -26
  85. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +14 -1
  86. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  87. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  88. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  89. package/packages/dd-trace/src/format.js +6 -1
  90. package/packages/dd-trace/src/id.js +12 -0
  91. package/packages/dd-trace/src/iitm.js +1 -1
  92. package/packages/dd-trace/src/log/channels.js +1 -1
  93. package/packages/dd-trace/src/noop/proxy.js +4 -0
  94. package/packages/dd-trace/src/opentelemetry/span.js +95 -2
  95. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
  96. package/packages/dd-trace/src/opentracing/propagation/text_map.js +14 -5
  97. package/packages/dd-trace/src/opentracing/span.js +6 -0
  98. package/packages/dd-trace/src/opentracing/span_context.js +5 -2
  99. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  100. package/packages/dd-trace/src/plugin_manager.js +1 -1
  101. package/packages/dd-trace/src/plugins/ci_plugin.js +46 -9
  102. package/packages/dd-trace/src/plugins/database.js +1 -1
  103. package/packages/dd-trace/src/plugins/index.js +6 -0
  104. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  105. package/packages/dd-trace/src/plugins/util/ci.js +6 -19
  106. package/packages/dd-trace/src/plugins/util/exec.js +23 -2
  107. package/packages/dd-trace/src/plugins/util/git.js +98 -22
  108. package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
  109. package/packages/dd-trace/src/plugins/util/test.js +3 -2
  110. package/packages/dd-trace/src/plugins/util/url.js +26 -0
  111. package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
  112. package/packages/dd-trace/src/priority_sampler.js +30 -38
  113. package/packages/dd-trace/src/profiler.js +5 -3
  114. package/packages/dd-trace/src/profiling/config.js +26 -2
  115. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -0
  116. package/packages/dd-trace/src/profiling/profiler.js +17 -10
  117. package/packages/dd-trace/src/profiling/profilers/events.js +264 -0
  118. package/packages/dd-trace/src/profiling/profilers/shared.js +39 -0
  119. package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
  120. package/packages/dd-trace/src/profiling/profilers/wall.js +121 -58
  121. package/packages/dd-trace/src/proxy.js +25 -1
  122. package/packages/dd-trace/src/ritm.js +1 -1
  123. package/packages/dd-trace/src/sampling_rule.js +130 -0
  124. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  125. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  126. package/packages/dd-trace/src/span_processor.js +4 -0
  127. package/packages/dd-trace/src/span_sampler.js +6 -64
  128. package/packages/dd-trace/src/spanleak.js +98 -0
  129. package/packages/dd-trace/src/startup-log.js +7 -1
  130. package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
  131. package/packages/dd-trace/src/telemetry/index.js +171 -41
  132. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  133. package/packages/dd-trace/src/telemetry/send-data.js +47 -5
  134. package/packages/dd-trace/src/tracer.js +8 -2
  135. package/scripts/install_plugin_modules.js +11 -3
  136. package/packages/diagnostics_channel/index.js +0 -3
  137. package/packages/diagnostics_channel/src/index.js +0 -121
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { channel } = require('../../../diagnostics_channel')
3
+ const { channel } = require('dc-polyfill')
4
4
  const path = require('path')
5
5
  const semver = require('semver')
6
6
  const Hook = require('./hook')
@@ -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')
@@ -58,6 +58,7 @@ function patch (http, methodName) {
58
58
  }
59
59
 
60
60
  const options = args.options
61
+
61
62
  const finish = () => {
62
63
  if (!finished) {
63
64
  finished = true
@@ -68,9 +69,17 @@ function patch (http, methodName) {
68
69
  try {
69
70
  const req = request.call(this, options, callback)
70
71
  const emit = req.emit
72
+ const setTimeout = req.setTimeout
71
73
 
72
74
  ctx.req = req
73
75
 
76
+ // tracked to accurately discern custom request socket timeout
77
+ let customRequestTimeout = false
78
+ req.setTimeout = function () {
79
+ customRequestTimeout = true
80
+ return setTimeout.apply(this, arguments)
81
+ }
82
+
74
83
  req.emit = function (eventName, arg) {
75
84
  switch (eventName) {
76
85
  case 'response': {
@@ -88,6 +97,7 @@ function patch (http, methodName) {
88
97
  case 'error':
89
98
  case 'timeout':
90
99
  ctx.error = arg
100
+ ctx.customRequestTimeout = customRequestTimeout
91
101
  errorChannel.publish(ctx)
92
102
  case 'abort': // deprecated and replaced by `close` in node 17
93
103
  case 'close':
@@ -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
@@ -44,6 +44,7 @@ const itrSkippedSuitesCh = channel('ci:jest:itr:skipped-suites')
44
44
  let skippableSuites = []
45
45
  let isCodeCoverageEnabled = false
46
46
  let isSuitesSkippingEnabled = false
47
+ let isUserCodeCoverageEnabled = false
47
48
  let isSuitesSkipped = false
48
49
  let numSkippedSuites = 0
49
50
  let hasUnskippableSuites = false
@@ -289,11 +290,14 @@ function cliWrapper (cli, jestVersion) {
289
290
  } = result
290
291
 
291
292
  let testCodeCoverageLinesTotal
292
- try {
293
- const { pct, total } = coverageMap.getCoverageSummary().lines
294
- testCodeCoverageLinesTotal = total !== 0 ? pct : 0
295
- } catch (e) {
296
- // ignore errors
293
+
294
+ if (isUserCodeCoverageEnabled) {
295
+ try {
296
+ const { pct, total } = coverageMap.getCoverageSummary().lines
297
+ testCodeCoverageLinesTotal = total !== 0 ? pct : 0
298
+ } catch (e) {
299
+ // ignore errors
300
+ }
297
301
  }
298
302
  let status, error
299
303
 
@@ -399,7 +403,7 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
399
403
  const coverageFiles = getCoveredFilenamesFromCoverage(environment.global.__coverage__)
400
404
  .map(filename => getTestSuitePath(filename, environment.rootDir))
401
405
  asyncResource.runInAsyncScope(() => {
402
- testSuiteCodeCoverageCh.publish([...coverageFiles, environment.testSuite])
406
+ testSuiteCodeCoverageCh.publish({ coverageFiles, testSuite: environment.testSuite })
403
407
  })
404
408
  }
405
409
  testSuiteFinishCh.publish({ status, errorMessage })
@@ -436,6 +440,8 @@ function configureTestEnvironment (readConfigsResult) {
436
440
  config.testEnvironmentOptions._ddTestCodeCoverageEnabled = isCodeCoverageEnabled
437
441
  })
438
442
 
443
+ isUserCodeCoverageEnabled = !!readConfigsResult.globalConfig.collectCoverage
444
+
439
445
  if (isCodeCoverageEnabled) {
440
446
  const globalConfig = {
441
447
  ...readConfigsResult.globalConfig,
@@ -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
 
@@ -144,7 +154,9 @@ function instrument (req, res, handler) {
144
154
  function wrapServeStatic (serveStatic) {
145
155
  return function (req, res, path) {
146
156
  return instrument(req, res, () => {
147
- if (pageLoadChannel.hasSubscribers && path) pageLoadChannel.publish({ page: path })
157
+ if (pageLoadChannel.hasSubscribers && path) {
158
+ pageLoadChannel.publish({ page: path, isStatic: true })
159
+ }
148
160
 
149
161
  return serveStatic.apply(this, arguments)
150
162
  })
@@ -50,7 +50,20 @@ function wrapFn (fn) {
50
50
  enterChannel.publish({ req, route })
51
51
 
52
52
  try {
53
- return fn.apply(this, arguments)
53
+ const result = fn.apply(this, arguments)
54
+ if (result && typeof result === 'object' && typeof result.then === 'function') {
55
+ return result.then(function () {
56
+ nextChannel.publish({ req })
57
+ finishChannel.publish({ req })
58
+ return arguments[0]
59
+ }).catch(function (error) {
60
+ errorChannel.publish({ req, error })
61
+ nextChannel.publish({ req })
62
+ finishChannel.publish({ req })
63
+ throw error
64
+ })
65
+ }
66
+ return result
54
67
  } catch (error) {
55
68
  errorChannel.publish({ req, error })
56
69
  nextChannel.publish({ req })
@@ -22,7 +22,7 @@ const dispatchReceiveCh = channel('apm:rhea:receive:dispatch')
22
22
  const errorReceiveCh = channel('apm:rhea:receive:error')
23
23
  const finishReceiveCh = channel('apm:rhea:receive:finish')
24
24
 
25
- const contexts = new WeakMap()
25
+ const contexts = new WeakMap() // key: delivery Fn, val: context
26
26
 
27
27
  addHook({ name: 'rhea', versions: ['>=1'] }, rhea => {
28
28
  shimmer.wrap(rhea.message, 'encode', encode => function (msg) {
@@ -52,7 +52,8 @@ addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/link.js' }, obj => {
52
52
  startSendCh.publish({ targetAddress, host, port, msg })
53
53
  const delivery = send.apply(this, arguments)
54
54
  const context = {
55
- asyncResource
55
+ asyncResource,
56
+ connection: this.connection
56
57
  }
57
58
  contexts.set(delivery, context)
58
59
 
@@ -80,7 +81,8 @@ addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/link.js' }, obj => {
80
81
 
81
82
  if (msgObj.delivery) {
82
83
  const context = {
83
- asyncResource
84
+ asyncResource,
85
+ connection: this.connection
84
86
  }
85
87
  contexts.set(msgObj.delivery, context)
86
88
  msgObj.delivery.update = wrapDeliveryUpdate(msgObj.delivery, msgObj.delivery.update)
@@ -114,7 +116,7 @@ addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/connection.js' }, Connecti
114
116
 
115
117
  asyncResource.runInAsyncScope(() => {
116
118
  errorReceiveCh.publish(error)
117
- beforeFinish(delivery, null)
119
+ exports.beforeFinish(delivery, null)
118
120
  finishReceiveCh.publish()
119
121
  })
120
122
  })
@@ -187,7 +189,7 @@ function patchCircularBuffer (proto, Session) {
187
189
  const state = remoteState && remoteState.constructor
188
190
  ? entry.remote_state.constructor.composite_type : undefined
189
191
  asyncResource.runInAsyncScope(() => {
190
- beforeFinish(entry, state)
192
+ exports.beforeFinish(entry, state)
191
193
  finishSendCh.publish()
192
194
  })
193
195
  }
@@ -217,13 +219,13 @@ function addToInFlightDeliveries (connection, delivery) {
217
219
  }
218
220
 
219
221
  function beforeFinish (delivery, state) {
220
- const obj = contexts.get(delivery)
221
- if (obj) {
222
+ const context = contexts.get(delivery)
223
+ if (context) {
222
224
  if (state) {
223
225
  dispatchReceiveCh.publish({ state })
224
226
  }
225
- if (obj.connection && obj.connection[inFlightDeliveries]) {
226
- obj.connection[inFlightDeliveries].delete(delivery)
227
+ if (context.connection && context.connection[inFlightDeliveries]) {
228
+ context.connection[inFlightDeliveries].delete(delivery)
227
229
  }
228
230
  }
229
231
  }
@@ -238,3 +240,7 @@ function getStateFromData (stateData) {
238
240
  }
239
241
  }
240
242
  }
243
+
244
+ module.exports.inFlightDeliveries = inFlightDeliveries
245
+ module.exports.beforeFinish = beforeFinish
246
+ module.exports.contexts = contexts
@@ -0,0 +1,113 @@
1
+ 'use strict'
2
+
3
+ const { storage } = require('../../datadog-core')
4
+ const DatabasePlugin = require('../../dd-trace/src/plugins/database')
5
+
6
+ const AEROSPIKE_PEER_SERVICE = 'aerospike.namespace'
7
+
8
+ class AerospikePlugin extends DatabasePlugin {
9
+ static get id () { return 'aerospike' }
10
+ static get operation () { return 'command' }
11
+ static get system () { return 'aerospike' }
12
+ static get prefix () {
13
+ return 'tracing:apm:aerospike:command'
14
+ }
15
+
16
+ static get peerServicePrecursors () {
17
+ return [AEROSPIKE_PEER_SERVICE]
18
+ }
19
+
20
+ bindStart (ctx) {
21
+ const { commandName, commandArgs } = ctx
22
+ const resourceName = commandName.slice(0, commandName.indexOf('Command'))
23
+ const store = storage.getStore()
24
+ const childOf = store ? store.span : null
25
+ const meta = getMeta(resourceName, commandArgs)
26
+
27
+ const span = this.startSpan(this.operationName(), {
28
+ childOf,
29
+ service: this.serviceName({ pluginConfig: this.config }),
30
+ type: 'aerospike',
31
+ kind: 'client',
32
+ resource: resourceName,
33
+ meta
34
+ }, false)
35
+
36
+ ctx.parentStore = store
37
+ ctx.currentStore = { ...store, span }
38
+
39
+ return ctx.currentStore
40
+ }
41
+
42
+ bindAsyncStart (ctx) {
43
+ if (ctx.currentStore) {
44
+ // have to manually trigger peer service calculation when using tracing channel
45
+ this.tagPeerService(ctx.currentStore.span)
46
+ ctx.currentStore.span.finish()
47
+ }
48
+ return ctx.parentStore
49
+ }
50
+
51
+ end (ctx) {
52
+ if (ctx.result) {
53
+ // have to manually trigger peer service calculation when using tracing channel
54
+ this.tagPeerService(ctx.currentStore.span)
55
+ ctx.currentStore.span.finish()
56
+ }
57
+ }
58
+
59
+ error (ctx) {
60
+ if (ctx.error) {
61
+ const error = ctx.error
62
+ const span = ctx.currentStore.span
63
+ span.setTag('error', error)
64
+ }
65
+ }
66
+ }
67
+
68
+ function getMeta (resourceName, commandArgs) {
69
+ let meta = {}
70
+ if (resourceName.includes('Index')) {
71
+ const [ns, set, bin, index] = commandArgs
72
+ meta = getMetaForIndex(ns, set, bin, index)
73
+ } else if (resourceName === 'Query') {
74
+ const { ns, set } = commandArgs[2]
75
+ meta = getMetaForQuery({ ns, set })
76
+ } else if (isKeyObject(commandArgs[0])) {
77
+ const { ns, set, key } = commandArgs[0]
78
+ meta = getMetaForKey(ns, set, key)
79
+ }
80
+ return meta
81
+ }
82
+
83
+ function getMetaForIndex (ns, set, bin, index) {
84
+ return {
85
+ [AEROSPIKE_PEER_SERVICE]: ns,
86
+ 'aerospike.setname': set,
87
+ 'aerospike.bin': bin,
88
+ 'aerospike.index': index
89
+ }
90
+ }
91
+
92
+ function getMetaForKey (ns, set, key) {
93
+ return {
94
+ 'aerospike.key': `${ns}:${set}:${key}`,
95
+ [AEROSPIKE_PEER_SERVICE]: ns,
96
+ 'aerospike.setname': set,
97
+ 'aerospike.userkey': key
98
+ }
99
+ }
100
+
101
+ function getMetaForQuery (queryObj) {
102
+ const { ns, set } = queryObj
103
+ return {
104
+ [AEROSPIKE_PEER_SERVICE]: ns,
105
+ 'aerospike.setname': set
106
+ }
107
+ }
108
+
109
+ function isKeyObject (obj) {
110
+ return obj && obj.ns !== undefined && obj.set !== undefined && obj.key !== undefined
111
+ }
112
+
113
+ module.exports = AerospikePlugin
@@ -12,10 +12,21 @@ const {
12
12
  getTestSuiteCommonTags,
13
13
  addIntelligentTestRunnerSpanTags,
14
14
  TEST_ITR_UNSKIPPABLE,
15
- TEST_ITR_FORCED_RUN
15
+ TEST_ITR_FORCED_RUN,
16
+ TEST_CODE_OWNERS
16
17
  } = require('../../dd-trace/src/plugins/util/test')
17
18
  const { RESOURCE_NAME } = require('../../../ext/tags')
18
19
  const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
20
+ const {
21
+ TELEMETRY_EVENT_CREATED,
22
+ TELEMETRY_EVENT_FINISHED,
23
+ TELEMETRY_CODE_COVERAGE_STARTED,
24
+ TELEMETRY_CODE_COVERAGE_FINISHED,
25
+ TELEMETRY_ITR_FORCED_TO_RUN,
26
+ TELEMETRY_CODE_COVERAGE_EMPTY,
27
+ TELEMETRY_ITR_UNSKIPPABLE,
28
+ TELEMETRY_CODE_COVERAGE_NUM_FILES
29
+ } = require('../../dd-trace/src/ci-visibility/telemetry')
19
30
 
20
31
  class CucumberPlugin extends CiPlugin {
21
32
  static get id () {
@@ -54,7 +65,9 @@ class CucumberPlugin extends CiPlugin {
54
65
  this.testSessionSpan.setTag(TEST_STATUS, status)
55
66
  this.testModuleSpan.setTag(TEST_STATUS, status)
56
67
  this.testModuleSpan.finish()
68
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
57
69
  this.testSessionSpan.finish()
70
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
58
71
  finishAllTraceSpans(this.testSessionSpan)
59
72
 
60
73
  this.itrConfig = null
@@ -69,9 +82,11 @@ class CucumberPlugin extends CiPlugin {
69
82
  'cucumber'
70
83
  )
71
84
  if (isUnskippable) {
85
+ this.telemetry.count(TELEMETRY_ITR_UNSKIPPABLE, { testLevel: 'suite' })
72
86
  testSuiteMetadata[TEST_ITR_UNSKIPPABLE] = 'true'
73
87
  }
74
88
  if (isForcedToRun) {
89
+ this.telemetry.count(TELEMETRY_ITR_FORCED_TO_RUN, { testLevel: 'suite' })
75
90
  testSuiteMetadata[TEST_ITR_FORCED_RUN] = 'true'
76
91
  }
77
92
  this.testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', {
@@ -82,20 +97,31 @@ class CucumberPlugin extends CiPlugin {
82
97
  ...testSuiteMetadata
83
98
  }
84
99
  })
100
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
101
+ if (this.itrConfig?.isCodeCoverageEnabled) {
102
+ this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_STARTED, 'suite', { library: 'istanbul' })
103
+ }
85
104
  })
86
105
 
87
106
  this.addSub('ci:cucumber:test-suite:finish', status => {
88
107
  this.testSuiteSpan.setTag(TEST_STATUS, status)
89
108
  this.testSuiteSpan.finish()
109
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
90
110
  })
91
111
 
92
112
  this.addSub('ci:cucumber:test-suite:code-coverage', ({ coverageFiles, suiteFile }) => {
93
- if (!this.itrConfig || !this.itrConfig.isCodeCoverageEnabled) {
113
+ if (!this.itrConfig?.isCodeCoverageEnabled) {
94
114
  return
95
115
  }
116
+ if (!coverageFiles.length) {
117
+ this.telemetry.count(TELEMETRY_CODE_COVERAGE_EMPTY)
118
+ }
119
+
96
120
  const relativeCoverageFiles = [...coverageFiles, suiteFile]
97
121
  .map(filename => getTestSuitePath(filename, this.sourceRoot))
98
122
 
123
+ this.telemetry.distribution(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeCoverageFiles.length)
124
+
99
125
  const formattedCoverage = {
100
126
  sessionId: this.testSuiteSpan.context()._traceId,
101
127
  suiteId: this.testSuiteSpan.context()._spanId,
@@ -103,6 +129,7 @@ class CucumberPlugin extends CiPlugin {
103
129
  }
104
130
 
105
131
  this.tracer._exporter.exportCoverage(formattedCoverage)
132
+ this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: 'istanbul' })
106
133
  })
107
134
 
108
135
  this.addSub('ci:cucumber:test:start', ({ testName, fullTestSuite, testSourceLine }) => {
@@ -142,6 +169,11 @@ class CucumberPlugin extends CiPlugin {
142
169
  }
143
170
 
144
171
  span.finish()
172
+ this.telemetry.ciVisEvent(
173
+ TELEMETRY_EVENT_FINISHED,
174
+ 'test',
175
+ { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
176
+ )
145
177
  if (!isStep) {
146
178
  finishAllTraceSpans(span)
147
179
  }