dd-trace 4.18.0 → 5.6.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 (209) hide show
  1. package/CONTRIBUTING.md +98 -0
  2. package/LICENSE-3rdparty.csv +4 -5
  3. package/MIGRATING.md +15 -0
  4. package/README.md +20 -140
  5. package/ci/cypress/after-run.js +1 -0
  6. package/ci/cypress/after-spec.js +1 -0
  7. package/ci/init.js +1 -4
  8. package/ext/kinds.d.ts +1 -0
  9. package/ext/kinds.js +2 -1
  10. package/ext/tags.d.ts +2 -1
  11. package/ext/tags.js +6 -1
  12. package/index.d.ts +1523 -1460
  13. package/package.json +19 -19
  14. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  15. package/packages/datadog-core/src/utils/src/get.js +11 -0
  16. package/packages/datadog-core/src/utils/src/has.js +14 -0
  17. package/packages/datadog-core/src/utils/src/kebabcase.js +16 -0
  18. package/packages/datadog-core/src/utils/src/pick.js +11 -0
  19. package/packages/datadog-core/src/utils/src/set.js +16 -0
  20. package/packages/datadog-core/src/utils/src/uniq.js +5 -0
  21. package/packages/datadog-esbuild/index.js +1 -20
  22. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  23. package/packages/datadog-instrumentations/src/amqplib.js +2 -2
  24. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  25. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  26. package/packages/datadog-instrumentations/src/child_process.js +150 -0
  27. package/packages/datadog-instrumentations/src/couchbase.js +5 -4
  28. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  29. package/packages/datadog-instrumentations/src/cucumber.js +163 -46
  30. package/packages/datadog-instrumentations/src/dns.js +2 -1
  31. package/packages/datadog-instrumentations/src/express.js +20 -0
  32. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  33. package/packages/datadog-instrumentations/src/grpc/client.js +56 -36
  34. package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
  35. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
  36. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -3
  37. package/packages/datadog-instrumentations/src/helpers/instrument.js +9 -4
  38. package/packages/datadog-instrumentations/src/helpers/register.js +19 -3
  39. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  40. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  41. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  42. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  43. package/packages/datadog-instrumentations/src/jest.js +239 -52
  44. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  45. package/packages/datadog-instrumentations/src/mocha.js +154 -18
  46. package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
  47. package/packages/datadog-instrumentations/src/mongoose.js +23 -10
  48. package/packages/datadog-instrumentations/src/mquery.js +65 -0
  49. package/packages/datadog-instrumentations/src/net.js +10 -2
  50. package/packages/datadog-instrumentations/src/next.js +35 -9
  51. package/packages/datadog-instrumentations/src/playwright.js +110 -16
  52. package/packages/datadog-instrumentations/src/restify.js +14 -1
  53. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  54. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  55. package/packages/datadog-plugin-amqplib/src/consumer.js +14 -1
  56. package/packages/datadog-plugin-amqplib/src/producer.js +13 -1
  57. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
  58. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +163 -27
  59. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +46 -8
  60. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +129 -22
  61. package/packages/datadog-plugin-child_process/src/index.js +91 -0
  62. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +125 -0
  63. package/packages/datadog-plugin-cucumber/src/index.js +70 -13
  64. package/packages/datadog-plugin-cypress/src/after-run.js +3 -0
  65. package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
  66. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +625 -0
  67. package/packages/datadog-plugin-cypress/src/plugin.js +6 -454
  68. package/packages/datadog-plugin-cypress/src/support.js +50 -3
  69. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -0
  70. package/packages/datadog-plugin-graphql/src/index.js +1 -6
  71. package/packages/datadog-plugin-graphql/src/resolve.js +28 -18
  72. package/packages/datadog-plugin-grpc/src/client.js +16 -2
  73. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  74. package/packages/datadog-plugin-http/src/client.js +19 -2
  75. package/packages/datadog-plugin-jest/src/index.js +118 -12
  76. package/packages/datadog-plugin-jest/src/util.js +38 -16
  77. package/packages/datadog-plugin-kafkajs/src/consumer.js +76 -6
  78. package/packages/datadog-plugin-kafkajs/src/producer.js +64 -8
  79. package/packages/datadog-plugin-mocha/src/index.js +87 -17
  80. package/packages/datadog-plugin-next/src/index.js +40 -14
  81. package/packages/datadog-plugin-playwright/src/index.js +71 -8
  82. package/packages/datadog-plugin-rhea/src/consumer.js +16 -1
  83. package/packages/datadog-plugin-rhea/src/producer.js +10 -0
  84. package/packages/dd-trace/src/appsec/activation.js +29 -0
  85. package/packages/dd-trace/src/appsec/addresses.js +5 -1
  86. package/packages/dd-trace/src/appsec/api_security_sampler.js +61 -0
  87. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  88. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  89. package/packages/dd-trace/src/appsec/channels.js +7 -3
  90. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  91. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
  92. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +1 -1
  93. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  94. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +22 -17
  95. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +7 -28
  96. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +10 -6
  97. package/packages/dd-trace/src/appsec/iast/analyzers/weak-randomness-analyzer.js +19 -0
  98. package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +90 -0
  99. package/packages/dd-trace/src/appsec/iast/context/kafka-ctx-plugin.js +14 -0
  100. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  101. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +13 -2
  102. package/packages/dd-trace/src/appsec/iast/index.js +15 -5
  103. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
  104. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -0
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +10 -0
  107. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +53 -0
  108. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +10 -46
  109. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +13 -9
  110. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +47 -0
  111. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +19 -6
  112. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +3 -1
  113. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +41 -3
  114. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  115. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  116. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  117. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  118. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  119. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  120. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  121. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  122. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  123. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
  124. package/packages/dd-trace/src/appsec/index.js +49 -33
  125. package/packages/dd-trace/src/appsec/recommended.json +1763 -106
  126. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -1
  127. package/packages/dd-trace/src/appsec/remote_config/index.js +42 -16
  128. package/packages/dd-trace/src/appsec/remote_config/manager.js +9 -8
  129. package/packages/dd-trace/src/appsec/reporter.js +51 -34
  130. package/packages/dd-trace/src/appsec/rule_manager.js +11 -8
  131. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  132. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  133. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  134. package/packages/dd-trace/src/ci-visibility/{intelligent-test-runner/get-itr-configuration.js → early-flake-detection/get-known-tests.js} +17 -22
  135. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +25 -6
  136. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  137. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +2 -0
  138. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  139. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +95 -37
  140. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +134 -61
  141. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +37 -4
  142. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +131 -0
  143. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  144. package/packages/dd-trace/src/config.js +561 -470
  145. package/packages/dd-trace/src/data_streams_context.js +1 -1
  146. package/packages/dd-trace/src/datastreams/pathway.js +58 -1
  147. package/packages/dd-trace/src/datastreams/processor.js +196 -27
  148. package/packages/dd-trace/src/datastreams/writer.js +11 -5
  149. package/packages/dd-trace/src/dogstatsd.js +3 -5
  150. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -6
  151. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  152. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  153. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  154. package/packages/dd-trace/src/exporters/common/request.js +21 -3
  155. package/packages/dd-trace/src/format.js +30 -2
  156. package/packages/dd-trace/src/id.js +12 -0
  157. package/packages/dd-trace/src/iitm.js +1 -1
  158. package/packages/dd-trace/src/log/channels.js +1 -1
  159. package/packages/dd-trace/src/noop/proxy.js +4 -0
  160. package/packages/dd-trace/src/noop/span.js +1 -0
  161. package/packages/dd-trace/src/opentelemetry/span.js +104 -4
  162. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
  163. package/packages/dd-trace/src/opentracing/propagation/text_map.js +16 -7
  164. package/packages/dd-trace/src/opentracing/span.js +48 -4
  165. package/packages/dd-trace/src/opentracing/span_context.js +15 -6
  166. package/packages/dd-trace/src/opentracing/tracer.js +4 -3
  167. package/packages/dd-trace/src/plugin_manager.js +1 -1
  168. package/packages/dd-trace/src/plugins/ci_plugin.js +78 -19
  169. package/packages/dd-trace/src/plugins/database.js +1 -1
  170. package/packages/dd-trace/src/plugins/index.js +7 -0
  171. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  172. package/packages/dd-trace/src/plugins/util/ci.js +6 -19
  173. package/packages/dd-trace/src/plugins/util/git.js +104 -22
  174. package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
  175. package/packages/dd-trace/src/plugins/util/test.js +60 -10
  176. package/packages/dd-trace/src/plugins/util/url.js +26 -0
  177. package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
  178. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  179. package/packages/dd-trace/src/priority_sampler.js +30 -38
  180. package/packages/dd-trace/src/profiler.js +5 -3
  181. package/packages/dd-trace/src/profiling/config.js +77 -24
  182. package/packages/dd-trace/src/profiling/exporters/agent.js +77 -31
  183. package/packages/dd-trace/src/profiling/exporters/file.js +2 -1
  184. package/packages/dd-trace/src/profiling/profiler.js +33 -22
  185. package/packages/dd-trace/src/profiling/profilers/events.js +270 -0
  186. package/packages/dd-trace/src/profiling/profilers/shared.js +45 -0
  187. package/packages/dd-trace/src/profiling/profilers/space.js +18 -2
  188. package/packages/dd-trace/src/profiling/profilers/wall.js +146 -70
  189. package/packages/dd-trace/src/proxy.js +56 -24
  190. package/packages/dd-trace/src/ritm.js +1 -1
  191. package/packages/dd-trace/src/sampling_rule.js +130 -0
  192. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  193. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  194. package/packages/dd-trace/src/span_processor.js +9 -1
  195. package/packages/dd-trace/src/span_sampler.js +6 -64
  196. package/packages/dd-trace/src/spanleak.js +98 -0
  197. package/packages/dd-trace/src/startup-log.js +7 -1
  198. package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
  199. package/packages/dd-trace/src/telemetry/index.js +182 -53
  200. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  201. package/packages/dd-trace/src/telemetry/send-data.js +65 -7
  202. package/packages/dd-trace/src/tracer.js +12 -5
  203. package/register.js +4 -0
  204. package/scripts/install_plugin_modules.js +11 -3
  205. package/scripts/st.js +105 -0
  206. package/packages/datadog-instrumentations/src/child-process.js +0 -30
  207. package/packages/dd-trace/src/plugins/util/exec.js +0 -13
  208. package/packages/diagnostics_channel/index.js +0 -3
  209. package/packages/diagnostics_channel/src/index.js +0 -121
@@ -7,6 +7,8 @@ const { Readable } = require('stream')
7
7
  const http = require('http')
8
8
  const https = require('https')
9
9
  const { parse: urlParse } = require('url')
10
+ const zlib = require('zlib')
11
+
10
12
  const docker = require('./docker')
11
13
  const { httpAgent, httpsAgent } = require('./agents')
12
14
  const { storage } = require('../../../../datadog-core')
@@ -93,16 +95,31 @@ function request (data, options, callback) {
93
95
  options.agent = isSecure ? httpsAgent : httpAgent
94
96
 
95
97
  const onResponse = res => {
96
- let responseData = ''
98
+ const chunks = []
97
99
 
98
100
  res.setTimeout(timeout)
99
101
 
100
- res.on('data', chunk => { responseData += chunk })
102
+ res.on('data', chunk => {
103
+ chunks.push(chunk)
104
+ })
101
105
  res.on('end', () => {
102
106
  activeRequests--
107
+ const buffer = Buffer.concat(chunks)
103
108
 
104
109
  if (res.statusCode >= 200 && res.statusCode <= 299) {
105
- callback(null, responseData, res.statusCode)
110
+ const isGzip = res.headers['content-encoding'] === 'gzip'
111
+ if (isGzip) {
112
+ zlib.gunzip(buffer, (err, result) => {
113
+ if (err) {
114
+ log.error(`Could not gunzip response: ${err.message}`)
115
+ callback(null, '', res.statusCode)
116
+ } else {
117
+ callback(null, result.toString(), res.statusCode)
118
+ }
119
+ })
120
+ } else {
121
+ callback(null, buffer.toString(), res.statusCode)
122
+ }
106
123
  } else {
107
124
  let errorMessage = ''
108
125
  try {
@@ -114,6 +131,7 @@ function request (data, options, callback) {
114
131
  } catch (e) {
115
132
  // ignore error
116
133
  }
134
+ const responseData = buffer.toString()
117
135
  if (responseData) {
118
136
  errorMessage += ` Response from the endpoint: "${responseData}"`
119
137
  }
@@ -14,7 +14,7 @@ const SPAN_SAMPLING_MECHANISM = constants.SPAN_SAMPLING_MECHANISM
14
14
  const SPAN_SAMPLING_RULE_RATE = constants.SPAN_SAMPLING_RULE_RATE
15
15
  const SPAN_SAMPLING_MAX_PER_SECOND = constants.SPAN_SAMPLING_MAX_PER_SECOND
16
16
  const SAMPLING_MECHANISM_SPAN = constants.SAMPLING_MECHANISM_SPAN
17
- const { MEASURED, BASE_SERVICE } = tags
17
+ const { MEASURED, BASE_SERVICE, ANALYTICS } = tags
18
18
  const ORIGIN_KEY = constants.ORIGIN_KEY
19
19
  const HOSTNAME_KEY = constants.HOSTNAME_KEY
20
20
  const TOP_LEVEL_KEY = constants.TOP_LEVEL_KEY
@@ -24,6 +24,7 @@ const ERROR_STACK = constants.ERROR_STACK
24
24
  const ERROR_TYPE = constants.ERROR_TYPE
25
25
 
26
26
  const map = {
27
+ 'operation.name': 'name',
27
28
  'service.name': 'service',
28
29
  'span.type': 'type',
29
30
  'resource.name': 'resource'
@@ -32,6 +33,7 @@ const map = {
32
33
  function format (span) {
33
34
  const formatted = formatSpan(span)
34
35
 
36
+ extractSpanLinks(formatted, span)
35
37
  extractRootTags(formatted, span)
36
38
  extractChunkTags(formatted, span)
37
39
  extractTags(formatted, span)
@@ -52,7 +54,8 @@ function formatSpan (span) {
52
54
  meta: {},
53
55
  metrics: {},
54
56
  start: Math.round(span._startTime * 1e6),
55
- duration: Math.round(span._duration * 1e6)
57
+ duration: Math.round(span._duration * 1e6),
58
+ links: []
56
59
  }
57
60
  }
58
61
 
@@ -63,6 +66,28 @@ function setSingleSpanIngestionTags (span, options) {
63
66
  addTag({}, span.metrics, SPAN_SAMPLING_MAX_PER_SECOND, options.maxPerSecond)
64
67
  }
65
68
 
69
+ function extractSpanLinks (trace, span) {
70
+ const links = []
71
+ if (span._links) {
72
+ for (const link of span._links) {
73
+ const { context, attributes } = link
74
+ const formattedLink = {}
75
+
76
+ formattedLink.trace_id = context.toTraceId(true)
77
+ formattedLink.span_id = context.toSpanId(true)
78
+
79
+ if (attributes && Object.keys(attributes).length > 0) {
80
+ formattedLink.attributes = attributes
81
+ }
82
+ if (context?._sampling?.priority >= 0) formattedLink.flags = context._sampling.priority > 0 ? 1 : 0
83
+ if (context?._tracestate) formattedLink.tracestate = context._tracestate.toString()
84
+
85
+ links.push(formattedLink)
86
+ }
87
+ }
88
+ if (links.length > 0) { trace.meta['_dd.span_links'] = JSON.stringify(links) }
89
+ }
90
+
66
91
  function extractTags (trace, span) {
67
92
  const context = span.context()
68
93
  const origin = context._trace.origin
@@ -92,6 +117,9 @@ function extractTags (trace, span) {
92
117
  case 'http.status_code':
93
118
  addTag(trace.meta, {}, tag, tags[tag] && String(tags[tag]))
94
119
  break
120
+ case 'analytics.event':
121
+ addTag({}, trace.metrics, ANALYTICS, tags[tag] === undefined || tags[tag] ? 1 : 0)
122
+ break
95
123
  case HOSTNAME_KEY:
96
124
  case MEASURED:
97
125
  addTag({}, trace.metrics, tag, tags[tag] === undefined || tags[tag] ? 1 : 0)
@@ -42,6 +42,18 @@ class Identifier {
42
42
  toJSON () {
43
43
  return this.toString()
44
44
  }
45
+
46
+ equals (other) {
47
+ const length = this._buffer.length
48
+ const otherLength = other._buffer.length
49
+
50
+ // Only compare the bytes available in both IDs.
51
+ for (let i = length, j = otherLength; i >= 0 && j >= 0; i--, j--) {
52
+ if (this._buffer[i] !== other._buffer[j]) return false
53
+ }
54
+
55
+ return true
56
+ }
45
57
  }
46
58
 
47
59
  // Create a buffer, using an optional hexadecimal value if provided.
@@ -3,7 +3,7 @@
3
3
  const semver = require('semver')
4
4
  const logger = require('./log')
5
5
  const { addHook } = require('import-in-the-middle')
6
- const dc = require('../../diagnostics_channel')
6
+ const dc = require('dc-polyfill')
7
7
 
8
8
  if (semver.satisfies(process.versions.node, '>=14.13.1')) {
9
9
  const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { channel } = require('../../../diagnostics_channel')
3
+ const { channel } = require('dc-polyfill')
4
4
 
5
5
  const Level = {
6
6
  Debug: 'debug',
@@ -20,6 +20,10 @@ class Tracer {
20
20
  return this
21
21
  }
22
22
 
23
+ profilerStarted () {
24
+ return Promise.resolve(false)
25
+ }
26
+
23
27
  trace (name, options, fn) {
24
28
  if (!fn) {
25
29
  fn = options
@@ -18,6 +18,7 @@ class NoopSpan {
18
18
  getBaggageItem (key) {}
19
19
  setTag (key, value) { return this }
20
20
  addTags (keyValueMap) { return this }
21
+ addLink (link) { return this }
21
22
  log () { return this }
22
23
  logEvent () {}
23
24
  finish (finishTime) {}
@@ -11,6 +11,7 @@ const tracer = require('../../')
11
11
  const DatadogSpan = require('../opentracing/span')
12
12
  const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../constants')
13
13
  const { SERVICE_NAME, RESOURCE_NAME } = require('../../../../ext/tags')
14
+ const kinds = require('../../../../ext/kinds')
14
15
 
15
16
  const SpanContext = require('./span_context')
16
17
 
@@ -19,6 +20,93 @@ function hrTimeToMilliseconds (time) {
19
20
  return time[0] * 1e3 + time[1] / 1e6
20
21
  }
21
22
 
23
+ const spanKindNames = {
24
+ [api.SpanKind.INTERNAL]: kinds.INTERNAL,
25
+ [api.SpanKind.SERVER]: kinds.SERVER,
26
+ [api.SpanKind.CLIENT]: kinds.CLIENT,
27
+ [api.SpanKind.PRODUCER]: kinds.PRODUCER,
28
+ [api.SpanKind.CONSUMER]: kinds.CONSUMER
29
+ }
30
+
31
+ /**
32
+ * Several of these attributes are not yet supported by the Node.js OTel API.
33
+ * We check for old equivalents where we can, but not all had equivalents.
34
+ */
35
+ function spanNameMapper (spanName, kind, attributes) {
36
+ if (spanName) return spanName
37
+
38
+ const opName = attributes['operation.name']
39
+ if (opName) return opName
40
+
41
+ const { INTERNAL, SERVER, CLIENT } = api.SpanKind
42
+
43
+ // HTTP server and client requests
44
+ // TODO: Drop http.method when http.request.method is supported.
45
+ for (const key of ['http.method', 'http.request.method']) {
46
+ if (key in attributes) {
47
+ if (kind === SERVER) {
48
+ return 'http.server.request'
49
+ }
50
+ if (kind === CLIENT) {
51
+ return 'http.client.request'
52
+ }
53
+ }
54
+ }
55
+
56
+ // Databases
57
+ const dbSystem = attributes['db.system']
58
+ if (dbSystem && kind === CLIENT) {
59
+ return `${dbSystem}.query`
60
+ }
61
+
62
+ // Messaging
63
+ const msgSys = attributes['messaging.system']
64
+ const msgOp = attributes['messaging.operation']
65
+ if (msgSys && msgOp && kind !== INTERNAL) {
66
+ return `${msgSys}.${msgOp}`
67
+ }
68
+
69
+ // RPC (and AWS)
70
+ const rpcSystem = attributes['rpc.system']
71
+ if (rpcSystem) {
72
+ if (kind === CLIENT) {
73
+ return rpcSystem === 'aws-api'
74
+ ? `aws.${attributes['rpc.service'] || 'client'}.request`
75
+ : `${rpcSystem}.client.request`
76
+ }
77
+ if (kind === SERVER) {
78
+ return `${rpcSystem}.server.request`
79
+ }
80
+ }
81
+
82
+ // FaaS
83
+ const faasProvider = attributes['faas.invoked_provider']
84
+ const faasName = attributes['faas.invoked_name']
85
+ const faasTrigger = attributes['faas.trigger']
86
+ if (kind === CLIENT && faasProvider && faasName) {
87
+ return `${faasProvider}.${faasName}.invoke`
88
+ }
89
+ if (kind === SERVER && faasTrigger) {
90
+ return `${faasTrigger}.invoke`
91
+ }
92
+
93
+ // GraphQL
94
+ // NOTE: Not part of Semantic Convention spec yet, but is used in the GraphQL
95
+ // integration.
96
+ const isGraphQL = 'graphql.operation.type' in attributes
97
+ if (isGraphQL) return 'graphql.server.request'
98
+
99
+ // Network
100
+ // TODO: Doesn't exist yet. No equivalent.
101
+ const protocol = attributes['network.protocol.name']
102
+ const protocolPrefix = protocol ? `${protocol}.` : ''
103
+ if (kind === SERVER) return `${protocolPrefix}server.request`
104
+ if (kind === CLIENT) return `${protocolPrefix}client.request`
105
+
106
+ // If all else fails, default to stringified span.kind.
107
+ return spanKindNames[kind]
108
+ }
109
+
22
110
  class Span {
23
111
  constructor (
24
112
  parentTracer,
@@ -27,7 +115,8 @@ class Span {
27
115
  spanContext,
28
116
  kind,
29
117
  links = [],
30
- timeInput
118
+ timeInput,
119
+ attributes
31
120
  ) {
32
121
  const { _tracer } = tracer
33
122
 
@@ -35,7 +124,7 @@ class Span {
35
124
  const startTime = hrTimeToMilliseconds(hrStartTime)
36
125
 
37
126
  this._ddSpan = new DatadogSpan(_tracer, _tracer._processor, _tracer._prioritySampler, {
38
- operationName: spanName,
127
+ operationName: spanNameMapper(spanName, kind, attributes),
39
128
  context: spanContext._ddContext,
40
129
  startTime,
41
130
  hostname: _tracer._hostname,
@@ -43,9 +132,14 @@ class Span {
43
132
  tags: {
44
133
  [SERVICE_NAME]: _tracer._service,
45
134
  [RESOURCE_NAME]: spanName
46
- }
135
+ },
136
+ links
47
137
  }, _tracer._debug)
48
138
 
139
+ if (attributes) {
140
+ this.setAttributes(attributes)
141
+ }
142
+
49
143
  this._parentTracer = parentTracer
50
144
  this._context = context
51
145
 
@@ -55,7 +149,6 @@ class Span {
55
149
  // math for computing opentracing timestamps is apparently lossy...
56
150
  this.startTime = hrStartTime
57
151
  this.kind = kind
58
- this.links = links
59
152
  this._spanProcessor.onStart(this, context)
60
153
  }
61
154
 
@@ -98,6 +191,13 @@ class Span {
98
191
  return this
99
192
  }
100
193
 
194
+ addLink (context, attributes) {
195
+ // extract dd context
196
+ const ddSpanContext = context._ddContext
197
+ this._ddSpan.addLink(ddSpanContext, attributes)
198
+ return this
199
+ }
200
+
101
201
  setStatus ({ code, message }) {
102
202
  if (!this.ended && !this._hasStatus && code) {
103
203
  this._hasStatus = true
@@ -78,23 +78,22 @@ class Tracer {
78
78
  // return api.trace.wrapSpanContext(spanContext)
79
79
  // }
80
80
 
81
- const span = new Span(
81
+ return new Span(
82
82
  this,
83
83
  context,
84
84
  name,
85
85
  spanContext,
86
86
  spanKind,
87
87
  links,
88
- options.startTime
88
+ options.startTime,
89
+
90
+ // Set initial span attributes. The attributes object may have been mutated
91
+ // by the sampler, so we sanitize the merged attributes before setting them.
92
+ sanitizeAttributes(
93
+ // Object.assign(attributes, samplingResult.attributes)
94
+ attributes
95
+ )
89
96
  )
90
- // Set initial span attributes. The attributes object may have been mutated
91
- // by the sampler, so we sanitize the merged attributes before setting them.
92
- const initAttributes = sanitizeAttributes(
93
- // Object.assign(attributes, samplingResult.attributes)
94
- attributes
95
- )
96
- span.setAttributes(initAttributes)
97
- return span
98
97
  }
99
98
 
100
99
  startActiveSpan (name, options, context, fn) {
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const pick = require('lodash.pick')
3
+ const pick = require('../../../../datadog-core/src/utils/src/pick')
4
4
  const id = require('../../id')
5
5
  const DatadogSpanContext = require('../span_context')
6
6
  const log = require('../../log')
@@ -173,7 +173,7 @@ class TextMapPropagator {
173
173
  ts.forVendor('dd', state => {
174
174
  state.set('s', priority)
175
175
  if (mechanism) {
176
- state.set('t.dm', mechanism)
176
+ state.set('t.dm', `-${mechanism}`)
177
177
  }
178
178
 
179
179
  if (typeof origin === 'string') {
@@ -236,11 +236,20 @@ class TextMapPropagator {
236
236
  _extractDatadogContext (carrier) {
237
237
  const spanContext = this._extractGenericContext(carrier, traceKey, spanKey, 10)
238
238
 
239
- if (spanContext) {
240
- this._extractOrigin(carrier, spanContext)
241
- this._extractBaggageItems(carrier, spanContext)
242
- this._extractSamplingPriority(carrier, spanContext)
243
- this._extractTags(carrier, spanContext)
239
+ if (!spanContext) return spanContext
240
+
241
+ this._extractOrigin(carrier, spanContext)
242
+ this._extractBaggageItems(carrier, spanContext)
243
+ this._extractSamplingPriority(carrier, spanContext)
244
+ this._extractTags(carrier, spanContext)
245
+
246
+ if (this._config.tracePropagationExtractFirst) return spanContext
247
+
248
+ const tc = this._extractTraceparentContext(carrier)
249
+
250
+ if (tc && spanContext._traceId.equals(tc._traceId)) {
251
+ spanContext._traceparent = tc._traceparent
252
+ spanContext._tracestate = tc._tracestate
244
253
  }
245
254
 
246
255
  return spanContext
@@ -12,6 +12,8 @@ const runtimeMetrics = require('../runtime_metrics')
12
12
  const log = require('../log')
13
13
  const { storage } = require('../../../datadog-core')
14
14
  const telemetryMetrics = require('../telemetry/metrics')
15
+ const { channel } = require('dc-polyfill')
16
+ const spanleak = require('../spanleak')
15
17
 
16
18
  const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
17
19
 
@@ -24,12 +26,15 @@ const unfinishedRegistry = createRegistry('unfinished')
24
26
  const finishedRegistry = createRegistry('finished')
25
27
 
26
28
  const OTEL_ENABLED = !!process.env.DD_TRACE_OTEL_ENABLED
29
+ const ALLOWED = ['string', 'number', 'boolean']
27
30
 
28
31
  const integrationCounters = {
29
- span_created: {},
30
- span_finished: {}
32
+ spans_created: {},
33
+ spans_finished: {}
31
34
  }
32
35
 
36
+ const finishCh = channel('dd-trace:span:finish')
37
+
33
38
  function getIntegrationCounter (event, integration) {
34
39
  const counters = integrationCounters[event]
35
40
 
@@ -67,7 +72,7 @@ class DatadogSpan {
67
72
  this._name = operationName
68
73
  this._integrationName = fields.integrationName || 'opentracing'
69
74
 
70
- getIntegrationCounter('span_created', this._integrationName).inc()
75
+ getIntegrationCounter('spans_created', this._integrationName).inc()
71
76
 
72
77
  this._spanContext = this._createContext(parent, fields)
73
78
  this._spanContext._name = operationName
@@ -78,6 +83,9 @@ class DatadogSpan {
78
83
 
79
84
  this._startTime = fields.startTime || this._getTime()
80
85
 
86
+ this._links = []
87
+ fields.links && fields.links.forEach(link => this.addLink(link.context, link.attributes))
88
+
81
89
  if (DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
82
90
  runtimeMetrics.increment('runtime.node.spans.unfinished')
83
91
  runtimeMetrics.increment('runtime.node.spans.unfinished.by.name', `span_name:${operationName}`)
@@ -87,6 +95,7 @@ class DatadogSpan {
87
95
 
88
96
  unfinishedRegistry.register(this, operationName, this)
89
97
  }
98
+ spanleak.addSpan(this, operationName)
90
99
  }
91
100
 
92
101
  toString () {
@@ -145,6 +154,13 @@ class DatadogSpan {
145
154
 
146
155
  logEvent () {}
147
156
 
157
+ addLink (context, attributes) {
158
+ this._links.push({
159
+ context: context._ddContext ? context._ddContext : context,
160
+ attributes: this._sanitizeAttributes(attributes)
161
+ })
162
+ }
163
+
148
164
  finish (finishTime) {
149
165
  if (this._duration !== undefined) {
150
166
  return
@@ -156,7 +172,7 @@ class DatadogSpan {
156
172
  }
157
173
  }
158
174
 
159
- getIntegrationCounter('span_finished', this._integrationName).inc()
175
+ getIntegrationCounter('spans_finished', this._integrationName).inc()
160
176
 
161
177
  if (DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
162
178
  runtimeMetrics.decrement('runtime.node.spans.unfinished')
@@ -176,9 +192,37 @@ class DatadogSpan {
176
192
  this._duration = finishTime - this._startTime
177
193
  this._spanContext._trace.finished.push(this)
178
194
  this._spanContext._isFinished = true
195
+ finishCh.publish(this)
179
196
  this._processor.process(this)
180
197
  }
181
198
 
199
+ _sanitizeAttributes (attributes = {}) {
200
+ const sanitizedAttributes = {}
201
+
202
+ const addArrayOrScalarAttributes = (key, maybeArray) => {
203
+ if (Array.isArray(maybeArray)) {
204
+ for (const subkey in maybeArray) {
205
+ addArrayOrScalarAttributes(`${key}.${subkey}`, maybeArray[subkey])
206
+ }
207
+ } else {
208
+ const maybeScalar = maybeArray
209
+ if (ALLOWED.includes(typeof maybeScalar)) {
210
+ // Wrap the value as a string if it's not already a string
211
+ sanitizedAttributes[key] = typeof maybeScalar === 'string' ? maybeScalar : String(maybeScalar)
212
+ } else {
213
+ log.warn(`Dropping span link attribute. It is not of an allowed type`)
214
+ }
215
+ }
216
+ }
217
+
218
+ Object.entries(attributes).forEach(entry => {
219
+ const [key, value] = entry
220
+ addArrayOrScalarAttributes(key, value)
221
+ })
222
+
223
+ return sanitizedAttributes
224
+ }
225
+
182
226
  _createContext (parent, fields) {
183
227
  let spanContext
184
228
  let startTime
@@ -2,6 +2,9 @@
2
2
 
3
3
  const { AUTO_KEEP } = require('../../../../ext/priority')
4
4
 
5
+ // the lowercase, hex encoded upper 64 bits of a 128-bit trace id, if present
6
+ const TRACE_ID_128 = '_dd.p.tid'
7
+
5
8
  class DatadogSpanContext {
6
9
  constructor (props) {
7
10
  props = props || {}
@@ -25,20 +28,26 @@ class DatadogSpanContext {
25
28
  }
26
29
  }
27
30
 
28
- toTraceId () {
31
+ toTraceId (get128bitId = false) {
32
+ if (get128bitId) {
33
+ return this._traceId.toBuffer().length <= 8 && this._trace.tags[TRACE_ID_128]
34
+ ? this._trace.tags[TRACE_ID_128] + this._traceId.toString(16).padStart(16, '0')
35
+ : this._traceId.toString(16).padStart(32, '0')
36
+ }
29
37
  return this._traceId.toString(10)
30
38
  }
31
39
 
32
- toSpanId () {
40
+ toSpanId (get128bitId = false) {
41
+ if (get128bitId) {
42
+ return this._spanId.toString(16).padStart(16, '0')
43
+ }
33
44
  return this._spanId.toString(10)
34
45
  }
35
46
 
36
47
  toTraceparent () {
37
48
  const flags = this._sampling.priority >= AUTO_KEEP ? '01' : '00'
38
- const traceId = this._traceId.toBuffer().length <= 8 && this._trace.tags['_dd.p.tid']
39
- ? this._trace.tags['_dd.p.tid'] + this._traceId.toString(16).padStart(16, '0')
40
- : this._traceId.toString(16).padStart(32, '0')
41
- const spanId = this._spanId.toString(16).padStart(16, '0')
49
+ const traceId = this.toTraceId(true)
50
+ const spanId = this.toSpanId(true)
42
51
  const version = (this._traceparent && this._traceparent.version) || '00'
43
52
  return `${version}-${traceId}-${spanId}-${flags}`
44
53
  }
@@ -22,10 +22,10 @@ class DatadogTracer {
22
22
  constructor (config) {
23
23
  const Exporter = getExporter(config.experimental.exporter)
24
24
 
25
+ this._config = config
25
26
  this._service = config.service
26
27
  this._version = config.version
27
28
  this._env = config.env
28
- this._tags = config.tags
29
29
  this._logInjection = config.logInjection
30
30
  this._debug = config.debug
31
31
  this._prioritySampler = new PrioritySampler(config.env, config.sampler)
@@ -61,10 +61,11 @@ class DatadogTracer {
61
61
  startTime: options.startTime,
62
62
  hostname: this._hostname,
63
63
  traceId128BitGenerationEnabled: this._traceId128BitGenerationEnabled,
64
- integrationName: options.integrationName
64
+ integrationName: options.integrationName,
65
+ links: options.links
65
66
  }, this._debug)
66
67
 
67
- span.addTags(this._tags)
68
+ span.addTags(this._config.tags)
68
69
  span.addTags(options.tags)
69
70
 
70
71
  return span
@@ -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 { isFalse } = require('./util')
5
5
  const plugins = require('./plugins')
6
6
  const log = require('./log')