dd-trace 5.86.0 → 5.87.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 (38) hide show
  1. package/index.d.ts +18 -3
  2. package/package.json +1 -1
  3. package/packages/datadog-instrumentations/src/cucumber.js +14 -0
  4. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  5. package/packages/datadog-instrumentations/src/http/client.js +119 -1
  6. package/packages/datadog-instrumentations/src/jest.js +104 -4
  7. package/packages/datadog-instrumentations/src/mocha/utils.js +6 -0
  8. package/packages/datadog-instrumentations/src/mysql2.js +131 -64
  9. package/packages/datadog-instrumentations/src/playwright.js +8 -0
  10. package/packages/datadog-instrumentations/src/stripe.js +92 -0
  11. package/packages/datadog-instrumentations/src/vitest.js +11 -0
  12. package/packages/datadog-plugin-azure-functions/src/index.js +53 -37
  13. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +7 -0
  14. package/packages/dd-trace/src/appsec/addresses.js +11 -0
  15. package/packages/dd-trace/src/appsec/channels.js +5 -1
  16. package/packages/dd-trace/src/appsec/downstream_requests.js +302 -0
  17. package/packages/dd-trace/src/appsec/index.js +103 -0
  18. package/packages/dd-trace/src/appsec/rasp/ssrf.js +66 -4
  19. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +14 -1
  20. package/packages/dd-trace/src/config/defaults.js +2 -0
  21. package/packages/dd-trace/src/config/index.js +6 -0
  22. package/packages/dd-trace/src/config/supported-configurations.json +2 -0
  23. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -2
  24. package/packages/dd-trace/src/debugger/devtools_client/index.js +75 -23
  25. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +23 -1
  26. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +3 -3
  27. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +168 -36
  28. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +18 -0
  29. package/packages/dd-trace/src/exporters/common/agents.js +1 -1
  30. package/packages/dd-trace/src/llmobs/constants/writers.js +1 -1
  31. package/packages/dd-trace/src/llmobs/sdk.js +34 -5
  32. package/packages/dd-trace/src/plugins/database.js +42 -43
  33. package/packages/dd-trace/src/plugins/outbound.js +27 -2
  34. package/packages/dd-trace/src/plugins/tracing.js +39 -4
  35. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +7 -0
  36. package/packages/dd-trace/src/plugins/util/web.js +8 -7
  37. package/packages/dd-trace/src/startup-log.js +2 -2
  38. package/packages/dd-trace/src/plugins/util/serverless.js +0 -8
@@ -7,71 +7,57 @@ class DatabasePlugin extends StoragePlugin {
7
7
  static operation = 'query'
8
8
  static peerServicePrecursors = ['db.name']
9
9
 
10
- constructor (...args) {
11
- super(...args)
12
- this.serviceTags = {
13
- dddbs: '',
14
- encodedDddbs: '',
15
- dde: '',
16
- encodedDde: '',
17
- ddps: '',
18
- encodedDdps: '',
19
- ddpv: '',
20
- encodedDdpv: '',
21
- }
22
- }
23
-
24
- encodingServiceTags (serviceTag, encodeATag, spanConfig) {
25
- if (serviceTag !== spanConfig) {
26
- this.serviceTags[serviceTag] = spanConfig
27
- this.serviceTags[encodeATag] = encodeURIComponent(spanConfig)
28
- }
29
- }
30
-
31
- createDBMPropagationCommentService (serviceName, span) {
32
- this.encodingServiceTags('dddbs', 'encodedDddbs', serviceName)
33
- this.encodingServiceTags('dde', 'encodedDde', this.tracer._env)
34
- this.encodingServiceTags('ddps', 'encodedDdps', this.tracer._service)
35
- this.encodingServiceTags('ddpv', 'encodedDdpv', this.tracer._version)
36
- if (span.context()._tags['out.host']) {
37
- this.encodingServiceTags('ddh', 'encodedDdh', span._spanContext._tags['out.host'])
38
- }
39
- if (span.context()._tags['db.name']) {
40
- this.encodingServiceTags('dddb', 'encodedDddb', span._spanContext._tags['db.name'])
41
- }
42
-
43
- const { encodedDddb, encodedDddbs, encodedDde, encodedDdh, encodedDdps, encodedDdpv } = this.serviceTags
10
+ /**
11
+ * @param {string} serviceName
12
+ * @param {import('../../../..').Span} span
13
+ * @param {object} peerData
14
+ * @returns {string}
15
+ */
16
+ #createDBMPropagationCommentService (serviceName, span, peerData) {
17
+ const spanTags = span.context()._tags
18
+ const encodedDddb = encode(spanTags['db.name'])
19
+ const encodedDddbs = encode(serviceName)
20
+ const encodedDde = encode(this.tracer._env)
21
+ const encodedDdh = encode(spanTags['out.host'])
22
+ const encodedDdps = this.tracer._service ?? ''
23
+ const encodedDdpv = this.tracer._version
44
24
 
45
25
  let dbmComment = `dddb='${encodedDddb}',dddbs='${encodedDddbs}',dde='${encodedDde}',ddh='${encodedDdh}',` +
46
26
  `ddps='${encodedDdps}',ddpv='${encodedDdpv}'`
47
27
 
48
- const peerData = this.getPeerService(span.context()._tags)
49
28
  if (peerData !== undefined && peerData[PEER_SERVICE_SOURCE_KEY] === PEER_SERVICE_KEY) {
50
- this.encodingServiceTags('ddprs', 'encodedDdprs', peerData[PEER_SERVICE_KEY])
51
-
52
- const { encodedDdprs } = this.serviceTags
53
- dbmComment += `,ddprs='${encodedDdprs}'`
29
+ dbmComment += `,ddprs='${encode(peerData[PEER_SERVICE_KEY])}'`
54
30
  }
55
31
  return dbmComment
56
32
  }
57
33
 
58
- getDbmServiceName (span, tracerService) {
34
+ /**
35
+ * @param {string} tracerService
36
+ * @param {object} peerData
37
+ * @returns {string}
38
+ */
39
+ #getDbmServiceName (tracerService, peerData) {
59
40
  if (this._tracerConfig.spanComputePeerService) {
60
- const peerData = this.getPeerService(span.context()._tags)
61
41
  return this.getPeerServiceRemap(peerData)[PEER_SERVICE_KEY] || tracerService
62
42
  }
63
43
  return tracerService
64
44
  }
65
45
 
46
+ /**
47
+ * @param {import('../../../..').Span} span
48
+ * @param {string} serviceName
49
+ * @param {boolean} disableFullMode
50
+ */
66
51
  createDbmComment (span, serviceName, disableFullMode = false) {
67
52
  const mode = this.config.dbmPropagationMode
68
- const dbmService = this.getDbmServiceName(span, serviceName)
69
53
 
70
54
  if (mode === 'disabled') {
71
55
  return null
72
56
  }
73
57
 
74
- const servicePropagation = this.createDBMPropagationCommentService(dbmService, span)
58
+ const peerData = this.getPeerService(span.context()._tags)
59
+ const dbmService = this.#getDbmServiceName(serviceName, peerData)
60
+ const servicePropagation = this.#createDBMPropagationCommentService(dbmService, span, peerData)
75
61
 
76
62
  if (disableFullMode || mode === 'service') {
77
63
  return servicePropagation
@@ -83,6 +69,13 @@ class DatabasePlugin extends StoragePlugin {
83
69
  }
84
70
  }
85
71
 
72
+ /**
73
+ * @param {import('../../../..').Span} span
74
+ * @param {string} query
75
+ * @param {string} serviceName
76
+ * @param {boolean} disableFullMode
77
+ * @returns {string}
78
+ */
86
79
  injectDbmQuery (span, query, serviceName, disableFullMode = false) {
87
80
  const dbmTraceComment = this.createDbmComment(span, serviceName, disableFullMode)
88
81
 
@@ -95,6 +88,10 @@ class DatabasePlugin extends StoragePlugin {
95
88
  : `/*${dbmTraceComment}*/ ${query}`
96
89
  }
97
90
 
91
+ /**
92
+ * @param {string} query
93
+ * @returns {string}
94
+ */
98
95
  maybeTruncate (query) {
99
96
  const maxLength = typeof this.config.truncate === 'number'
100
97
  ? this.config.truncate
@@ -108,4 +105,6 @@ class DatabasePlugin extends StoragePlugin {
108
105
  }
109
106
  }
110
107
 
108
+ const encode = value => value ? encodeURIComponent(value) : ''
109
+
111
110
  module.exports = DatabasePlugin
@@ -18,6 +18,10 @@ const COMMON_PEER_SVC_SOURCE_TAGS = [
18
18
 
19
19
  // TODO: Exit span on finish when AsyncResource instances are removed.
20
20
  class OutboundPlugin extends TracingPlugin {
21
+ /**
22
+ *
23
+ * @type {string[]}
24
+ */
21
25
  static peerServicePrecursors = []
22
26
 
23
27
  constructor (...args) {
@@ -28,12 +32,15 @@ class OutboundPlugin extends TracingPlugin {
28
32
  })
29
33
  }
30
34
 
35
+ /**
36
+ * @param {{ parentStore?: { span: import('../../../..').Span } }} ctx
37
+ */
31
38
  bindFinish (ctx) {
32
39
  return ctx.parentStore
33
40
  }
34
41
 
35
- startSpan (...args) {
36
- const span = super.startSpan(...args)
42
+ startSpan (name, options, enterOrCtx) {
43
+ const span = super.startSpan(name, options, enterOrCtx)
37
44
  if (
38
45
  this._tracerConfig.codeOriginForSpans.enabled &&
39
46
  this._tracerConfig.codeOriginForSpans.experimental.exit_spans.enabled
@@ -43,6 +50,9 @@ class OutboundPlugin extends TracingPlugin {
43
50
  return span
44
51
  }
45
52
 
53
+ /**
54
+ * @param {Record<string, string>} tags
55
+ */
46
56
  getPeerService (tags) {
47
57
  /**
48
58
  * Compute `peer.service` and associated metadata from available tags, based
@@ -75,6 +85,9 @@ class OutboundPlugin extends TracingPlugin {
75
85
  }
76
86
  }
77
87
 
88
+ /**
89
+ * @param {Record<string, string>} peerData
90
+ */
78
91
  getPeerServiceRemap (peerData) {
79
92
  /**
80
93
  * If DD_TRACE_PEER_SERVICE_MAPPING is matched, we need to override the existing
@@ -92,6 +105,9 @@ class OutboundPlugin extends TracingPlugin {
92
105
  return peerData
93
106
  }
94
107
 
108
+ /**
109
+ * @param {{ currentStore?: { span: import('../../../..').Span } }} ctx
110
+ */
95
111
  finish (ctx) {
96
112
  const span = ctx?.currentStore?.span || this.activeSpan
97
113
  this.tagPeerService(span)
@@ -104,6 +120,9 @@ class OutboundPlugin extends TracingPlugin {
104
120
  super.finish(...arguments)
105
121
  }
106
122
 
123
+ /**
124
+ * @param {import('../../../..').Span} span
125
+ */
107
126
  tagPeerService (span) {
108
127
  if (this._tracerConfig.spanComputePeerService) {
109
128
  const peerData = this.getPeerService(span.context()._tags)
@@ -113,10 +132,16 @@ class OutboundPlugin extends TracingPlugin {
113
132
  }
114
133
  }
115
134
 
135
+ /**
136
+ * @param {object} ctx
137
+ */
116
138
  connect (ctx) {
117
139
  this.addHost(ctx)
118
140
  }
119
141
 
142
+ /**
143
+ * @param {{ hostname: string, port: number, currentStore?: { span: import('../../../..').Span } }} ctx
144
+ */
120
145
  addHost (ctx) {
121
146
  const { hostname, port } = ctx
122
147
 
@@ -16,11 +16,18 @@ class TracingPlugin extends Plugin {
16
16
  }
17
17
 
18
18
  get activeSpan () {
19
- const store = storage('legacy').getStore()
19
+ const store = /** @type {{ span?: import('../../../..').Span }} */ (storage('legacy').getStore())
20
20
 
21
- return store && store.span
21
+ return store?.span
22
22
  }
23
23
 
24
+ /**
25
+ * @param {object} opts
26
+ * @param {string} [opts.type]
27
+ * @param {string} [opts.id]
28
+ * @param {string} [opts.kind]
29
+ * @returns {string}
30
+ */
24
31
  serviceName (opts = {}) {
25
32
  const {
26
33
  type = this.constructor.type,
@@ -31,6 +38,13 @@ class TracingPlugin extends Plugin {
31
38
  return this._tracer._nomenclature.serviceName(type, kind, id, opts)
32
39
  }
33
40
 
41
+ /**
42
+ * @param {object} opts
43
+ * @param {string} [opts.type]
44
+ * @param {string} [opts.id]
45
+ * @param {string} [opts.kind]
46
+ * @returns {string}
47
+ */
34
48
  operationName (opts = {}) {
35
49
  const {
36
50
  type = this.constructor.type,
@@ -41,6 +55,10 @@ class TracingPlugin extends Plugin {
41
55
  return this._tracer._nomenclature.opName(type, kind, id, opts)
42
56
  }
43
57
 
58
+ /**
59
+ * @param {object} config
60
+ * @returns {object}
61
+ */
44
62
  configure (config) {
45
63
  return super.configure({
46
64
  ...config,
@@ -53,11 +71,17 @@ class TracingPlugin extends Plugin {
53
71
 
54
72
  start () {} // implemented by individual plugins
55
73
 
74
+ /**
75
+ * @param {{ currentStore?: { span: import('../../../..').Span } }} ctx
76
+ */
56
77
  finish (ctx) {
57
78
  const span = ctx?.currentStore?.span || this.activeSpan
58
79
  span?.finish()
59
80
  }
60
81
 
82
+ /**
83
+ * @param {{ currentStore?: { span: import('../../../..').Span }, error?: unknown }} ctxOrError
84
+ */
61
85
  error (ctxOrError) {
62
86
  if (ctxOrError?.currentStore) {
63
87
  ctxOrError.currentStore?.span.setTag('error', ctxOrError?.error)
@@ -84,21 +108,32 @@ class TracingPlugin extends Plugin {
84
108
  }
85
109
  }
86
110
 
111
+ /**
112
+ * @param {string} eventName
113
+ * @param {Function} handler
114
+ */
87
115
  addTraceSub (eventName, handler) {
88
116
  const prefix = this.constructor.prefix || `apm:${this.component}:${this.operation}`
89
117
  this.addSub(`${prefix}:${eventName}`, handler)
90
118
  }
91
119
 
120
+ /**
121
+ * @param {string} eventName
122
+ * @param {Function} transform
123
+ */
92
124
  addTraceBind (eventName, transform) {
93
125
  const prefix = this.constructor.prefix || `apm:${this.component}:${this.operation}`
94
126
  this.addBind(`${prefix}:${eventName}`, transform)
95
127
  }
96
128
 
129
+ /**
130
+ * @param {unknown} error
131
+ * @param {import('../../../..').Span} [span]
132
+ */
97
133
  addError (error, span = this.activeSpan) {
98
134
  if (span && !span._spanContext._tags.error) {
99
135
  // Errors may be wrapped in a context.
100
- error = (error && error.error) || error
101
- span.setTag('error', error || 1)
136
+ span.setTag('error', error?.error || error || 1)
102
137
  }
103
138
  }
104
139
 
@@ -14,12 +14,17 @@ const PROXY_HEADER_PATH = 'x-dd-proxy-path'
14
14
  const PROXY_HEADER_HTTPMETHOD = 'x-dd-proxy-httpmethod'
15
15
  const PROXY_HEADER_DOMAIN = 'x-dd-proxy-domain-name'
16
16
  const PROXY_HEADER_STAGE = 'x-dd-proxy-stage'
17
+ const PROXY_HEADER_REGION = 'x-dd-proxy-region'
17
18
 
18
19
  const supportedProxies = {
19
20
  'aws-apigateway': {
20
21
  spanName: 'aws.apigateway',
21
22
  component: 'aws-apigateway',
22
23
  },
24
+ 'azure-apim': {
25
+ spanName: 'azure.apim',
26
+ component: 'azure-apim',
27
+ },
23
28
  }
24
29
 
25
30
  function createInferredProxySpan (headers, childOf, tracer, reqCtx, traceCtx, config, startSpanHelper) {
@@ -53,6 +58,7 @@ function createInferredProxySpan (headers, childOf, tracer, reqCtx, traceCtx, co
53
58
  [HTTP_METHOD]: proxyContext.method,
54
59
  [HTTP_URL]: proxyContext.domainName + proxyContext.path,
55
60
  stage: proxyContext.stage,
61
+ region: proxyContext.region,
56
62
  },
57
63
  }, traceCtx, config)
58
64
 
@@ -91,6 +97,7 @@ function extractInferredProxyContext (headers) {
91
97
  stage: headers[PROXY_HEADER_STAGE],
92
98
  domainName: headers[PROXY_HEADER_DOMAIN],
93
99
  proxySystemName: headers[PROXY_HEADER_SYSTEM],
100
+ region: headers[PROXY_HEADER_REGION],
94
101
  }
95
102
  }
96
103
 
@@ -119,7 +119,7 @@ const web = {
119
119
  context.span.context()._name = name
120
120
  span = context.span
121
121
  } else {
122
- span = web.startChildSpan(tracer, config, name, req, traceCtx)
122
+ span = web.startServerlessSpanWithInferredProxy(tracer, config, name, req, traceCtx)
123
123
  }
124
124
 
125
125
  context.tracer = tracer
@@ -275,7 +275,7 @@ const web = {
275
275
  return context.middleware.at(-1)
276
276
  },
277
277
 
278
- startChildSpan (tracer, config, name, req, traceCtx) {
278
+ startServerlessSpanWithInferredProxy (tracer, config, name, req, traceCtx) {
279
279
  const headers = req.headers
280
280
  const reqCtx = contexts.get(req)
281
281
  const { storage } = require('../../../../datadog-core')
@@ -338,12 +338,12 @@ const web = {
338
338
  }
339
339
  },
340
340
 
341
- finishSpan (context) {
341
+ finishSpan (context, spanType) {
342
342
  const { req, res } = context
343
343
 
344
344
  if (context.finished && !req.stream) return
345
345
 
346
- addRequestTags(context, this.TYPE)
346
+ addRequestTags(context, spanType)
347
347
  addResponseTags(context)
348
348
 
349
349
  context.config.hooks.request(context.span, req, res)
@@ -353,14 +353,14 @@ const web = {
353
353
  context.finished = true
354
354
  },
355
355
 
356
- finishAll (context) {
356
+ finishAll (context, spanType) {
357
357
  for (const beforeEnd of context.beforeEnd) {
358
358
  beforeEnd()
359
359
  }
360
360
 
361
361
  web.finishMiddleware(context)
362
362
 
363
- web.finishSpan(context)
363
+ web.finishSpan(context, spanType)
364
364
 
365
365
  finishInferredProxySpan(context)
366
366
  },
@@ -457,12 +457,13 @@ function reactivate (req, fn) {
457
457
  function addRequestTags (context, spanType) {
458
458
  const { req, span, inferredProxySpan, config } = context
459
459
  const url = extractURL(req)
460
+ const type = spanType ?? WEB
460
461
 
461
462
  span.addTags({
462
463
  [HTTP_URL]: obfuscateQs(config, url),
463
464
  [HTTP_METHOD]: req.method,
464
465
  [SPAN_KIND]: SERVER,
465
- [SPAN_TYPE]: spanType,
466
+ [SPAN_TYPE]: type,
466
467
  [HTTP_USERAGENT]: req.headers['user-agent'],
467
468
  })
468
469
 
@@ -4,7 +4,7 @@ const os = require('os')
4
4
  const { inspect } = require('util')
5
5
  const tracerVersion = require('../../../package.json').version
6
6
  const { getAgentUrl } = require('./agent/url')
7
- const { info, warn } = require('./log/writer')
7
+ const { warn } = require('./log/writer')
8
8
 
9
9
  const errors = {}
10
10
  let config
@@ -29,7 +29,7 @@ function startupLog (agentError) {
29
29
  out.agent_error = agentError.message
30
30
  }
31
31
 
32
- info('DATADOG TRACER CONFIGURATION - ' + out)
32
+ warn('DATADOG TRACER CONFIGURATION - ' + out)
33
33
  if (agentError) {
34
34
  warn('DATADOG TRACER DIAGNOSTIC - Agent Error: ' + agentError.message)
35
35
  errors.agentError = {
@@ -1,8 +0,0 @@
1
- 'use strict'
2
-
3
- const types = require('../../../../../ext/types')
4
- const web = require('./web')
5
-
6
- const serverless = { ...web, TYPE: types.SERVERLESS }
7
-
8
- module.exports = serverless