dd-trace 5.24.0 → 5.26.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 (138) hide show
  1. package/LICENSE-3rdparty.csv +3 -0
  2. package/index.d.ts +345 -8
  3. package/init.js +60 -47
  4. package/package.json +16 -7
  5. package/packages/datadog-code-origin/index.js +4 -4
  6. package/packages/datadog-core/index.js +1 -3
  7. package/packages/datadog-core/src/storage.js +21 -0
  8. package/packages/datadog-core/src/utils/src/parse-tags.js +33 -0
  9. package/packages/datadog-esbuild/index.js +4 -2
  10. package/packages/datadog-instrumentations/src/amqplib.js +65 -5
  11. package/packages/datadog-instrumentations/src/child_process.js +135 -27
  12. package/packages/datadog-instrumentations/src/express.js +1 -1
  13. package/packages/datadog-instrumentations/src/handlebars.js +40 -0
  14. package/packages/datadog-instrumentations/src/helpers/hooks.js +5 -0
  15. package/packages/datadog-instrumentations/src/helpers/register.js +9 -0
  16. package/packages/datadog-instrumentations/src/jest.js +6 -2
  17. package/packages/datadog-instrumentations/src/kafkajs.js +123 -63
  18. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -2
  19. package/packages/datadog-instrumentations/src/multer.js +37 -0
  20. package/packages/datadog-instrumentations/src/openai.js +2 -2
  21. package/packages/datadog-instrumentations/src/pug.js +23 -0
  22. package/packages/datadog-instrumentations/src/router.js +2 -3
  23. package/packages/datadog-instrumentations/src/url.js +84 -0
  24. package/packages/datadog-instrumentations/src/utils/src/extract-package-and-module-path.js +7 -4
  25. package/packages/datadog-plugin-amqplib/src/consumer.js +6 -5
  26. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -0
  27. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  28. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +10 -7
  29. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +35 -0
  30. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +11 -9
  31. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +59 -45
  32. package/packages/datadog-plugin-cypress/src/support.js +1 -0
  33. package/packages/datadog-plugin-fastify/src/code_origin.js +2 -2
  34. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +10 -2
  35. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +8 -0
  36. package/packages/datadog-plugin-grpc/src/client.js +3 -0
  37. package/packages/datadog-plugin-grpc/src/server.js +5 -1
  38. package/packages/datadog-plugin-http/src/client.js +42 -1
  39. package/packages/datadog-plugin-http2/src/client.js +26 -1
  40. package/packages/datadog-plugin-jest/src/index.js +2 -1
  41. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +6 -3
  42. package/packages/datadog-plugin-kafkajs/src/consumer.js +10 -5
  43. package/packages/datadog-plugin-kafkajs/src/producer.js +10 -4
  44. package/packages/datadog-plugin-mocha/src/index.js +5 -2
  45. package/packages/datadog-plugin-moleculer/src/server.js +2 -2
  46. package/packages/datadog-plugin-openai/src/index.js +9 -1015
  47. package/packages/datadog-plugin-openai/src/tracing.js +1023 -0
  48. package/packages/datadog-plugin-rhea/src/consumer.js +2 -1
  49. package/packages/datadog-plugin-vitest/src/index.js +2 -1
  50. package/packages/dd-trace/src/appsec/addresses.js +2 -0
  51. package/packages/dd-trace/src/appsec/api_security_sampler.js +50 -27
  52. package/packages/dd-trace/src/appsec/channels.js +3 -1
  53. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  54. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +33 -16
  55. package/packages/dd-trace/src/appsec/iast/analyzers/template-injection-analyzer.js +18 -0
  56. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +55 -7
  57. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -2
  58. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  59. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -2
  60. package/packages/dd-trace/src/appsec/index.js +9 -6
  61. package/packages/dd-trace/src/appsec/rasp/command_injection.js +49 -0
  62. package/packages/dd-trace/src/appsec/rasp/index.js +3 -0
  63. package/packages/dd-trace/src/appsec/rasp/ssrf.js +4 -3
  64. package/packages/dd-trace/src/appsec/rasp/utils.js +3 -2
  65. package/packages/dd-trace/src/appsec/recommended.json +354 -158
  66. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  67. package/packages/dd-trace/src/appsec/remote_config/index.js +2 -7
  68. package/packages/dd-trace/src/appsec/reporter.js +6 -4
  69. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -3
  70. package/packages/dd-trace/src/appsec/waf/waf_manager.js +4 -0
  71. package/packages/dd-trace/src/azure_metadata.js +120 -0
  72. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +97 -0
  73. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +90 -0
  74. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +19 -1
  75. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +53 -0
  76. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +8 -1
  77. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +43 -0
  78. package/packages/dd-trace/src/config.js +88 -10
  79. package/packages/dd-trace/src/constants.js +8 -1
  80. package/packages/dd-trace/src/crashtracking/crashtracker.js +98 -0
  81. package/packages/dd-trace/src/crashtracking/index.js +15 -0
  82. package/packages/dd-trace/src/crashtracking/noop.js +8 -0
  83. package/packages/dd-trace/src/datastreams/pathway.js +1 -0
  84. package/packages/dd-trace/src/debugger/devtools_client/index.js +9 -13
  85. package/packages/dd-trace/src/debugger/devtools_client/send.js +15 -1
  86. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +57 -23
  87. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +12 -2
  88. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +31 -20
  89. package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +6 -0
  90. package/packages/dd-trace/src/debugger/devtools_client/state.js +11 -2
  91. package/packages/dd-trace/src/debugger/index.js +10 -3
  92. package/packages/dd-trace/src/llmobs/constants/tags.js +34 -0
  93. package/packages/dd-trace/src/llmobs/constants/text.js +6 -0
  94. package/packages/dd-trace/src/llmobs/constants/writers.js +13 -0
  95. package/packages/dd-trace/src/llmobs/index.js +103 -0
  96. package/packages/dd-trace/src/llmobs/noop.js +82 -0
  97. package/packages/dd-trace/src/llmobs/plugins/base.js +65 -0
  98. package/packages/dd-trace/src/llmobs/plugins/openai.js +205 -0
  99. package/packages/dd-trace/src/llmobs/sdk.js +377 -0
  100. package/packages/dd-trace/src/llmobs/span_processor.js +195 -0
  101. package/packages/dd-trace/src/llmobs/storage.js +7 -0
  102. package/packages/dd-trace/src/llmobs/tagger.js +322 -0
  103. package/packages/dd-trace/src/llmobs/util.js +176 -0
  104. package/packages/dd-trace/src/llmobs/writers/base.js +111 -0
  105. package/packages/dd-trace/src/llmobs/writers/evaluations.js +29 -0
  106. package/packages/dd-trace/src/llmobs/writers/spans/agentProxy.js +23 -0
  107. package/packages/dd-trace/src/llmobs/writers/spans/agentless.js +17 -0
  108. package/packages/dd-trace/src/llmobs/writers/spans/base.js +52 -0
  109. package/packages/dd-trace/src/log/index.js +10 -13
  110. package/packages/dd-trace/src/log/log.js +52 -0
  111. package/packages/dd-trace/src/log/writer.js +50 -19
  112. package/packages/dd-trace/src/noop/proxy.js +3 -0
  113. package/packages/dd-trace/src/noop/span.js +4 -0
  114. package/packages/dd-trace/src/opentelemetry/span.js +16 -1
  115. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -0
  116. package/packages/dd-trace/src/opentracing/propagation/text_map.js +106 -32
  117. package/packages/dd-trace/src/opentracing/span.js +26 -0
  118. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  119. package/packages/dd-trace/src/opentracing/tracer.js +8 -1
  120. package/packages/dd-trace/src/payload-tagging/config/aws.json +71 -3
  121. package/packages/dd-trace/src/plugins/outbound.js +9 -0
  122. package/packages/dd-trace/src/plugins/tracing.js +3 -3
  123. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +121 -0
  124. package/packages/dd-trace/src/plugins/util/ip_extractor.js +0 -1
  125. package/packages/dd-trace/src/plugins/util/web.js +39 -11
  126. package/packages/dd-trace/src/priority_sampler.js +16 -0
  127. package/packages/dd-trace/src/profiling/config.js +3 -1
  128. package/packages/dd-trace/src/profiling/exporters/agent.js +7 -5
  129. package/packages/dd-trace/src/profiling/profilers/wall.js +2 -1
  130. package/packages/dd-trace/src/proxy.js +13 -1
  131. package/packages/dd-trace/src/span_processor.js +5 -0
  132. package/packages/dd-trace/src/telemetry/index.js +11 -1
  133. package/packages/dd-trace/src/telemetry/logs/index.js +16 -11
  134. package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -8
  135. package/packages/dd-trace/src/telemetry/metrics.js +6 -1
  136. package/packages/dd-trace/src/util.js +16 -1
  137. package/version.js +4 -2
  138. /package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/{code-injection-sensitive-analyzer.js → tainted-range-based-sensitive-analyzer.js} +0 -0
@@ -53,6 +53,8 @@ class TextMapPropagator {
53
53
  }
54
54
 
55
55
  inject (spanContext, carrier) {
56
+ if (!spanContext || !carrier) return
57
+
56
58
  this._injectBaggageItems(spanContext, carrier)
57
59
  this._injectDatadog(spanContext, carrier)
58
60
  this._injectB3MultipleHeaders(spanContext, carrier)
@@ -107,10 +109,35 @@ class TextMapPropagator {
107
109
  }
108
110
  }
109
111
 
112
+ _encodeOtelBaggageKey (key) {
113
+ let encoded = encodeURIComponent(key)
114
+ encoded = encoded.replaceAll('(', '%28')
115
+ encoded = encoded.replaceAll(')', '%29')
116
+ return encoded
117
+ }
118
+
110
119
  _injectBaggageItems (spanContext, carrier) {
111
- spanContext._baggageItems && Object.keys(spanContext._baggageItems).forEach(key => {
112
- carrier[baggagePrefix + key] = String(spanContext._baggageItems[key])
113
- })
120
+ if (this._config.legacyBaggageEnabled) {
121
+ spanContext._baggageItems && Object.keys(spanContext._baggageItems).forEach(key => {
122
+ carrier[baggagePrefix + key] = String(spanContext._baggageItems[key])
123
+ })
124
+ }
125
+ if (this._hasPropagationStyle('inject', 'baggage')) {
126
+ let baggage = ''
127
+ let itemCounter = 0
128
+ let byteCounter = 0
129
+
130
+ for (const [key, value] of Object.entries(spanContext._baggageItems)) {
131
+ const item = `${this._encodeOtelBaggageKey(String(key).trim())}=${encodeURIComponent(String(value).trim())},`
132
+ itemCounter += 1
133
+ byteCounter += item.length
134
+ if (itemCounter > this._config.baggageMaxItems || byteCounter > this._config.baggageMaxBytes) break
135
+ baggage += item
136
+ }
137
+
138
+ baggage = baggage.slice(0, baggage.length - 1)
139
+ if (baggage) carrier.baggage = baggage
140
+ }
114
141
  }
115
142
 
116
143
  _injectTags (spanContext, carrier) {
@@ -263,45 +290,63 @@ class TextMapPropagator {
263
290
  }
264
291
 
265
292
  _extractSpanContext (carrier) {
266
- let spanContext = null
293
+ let context = null
267
294
  for (const extractor of this._config.tracePropagationStyle.extract) {
268
- // add logic to ensure tracecontext headers takes precedence over other extracted headers
269
- if (spanContext !== null) {
270
- if (this._config.tracePropagationExtractFirst) {
271
- return spanContext
272
- }
273
- if (extractor !== 'tracecontext') {
274
- continue
275
- }
276
- spanContext = this._resolveTraceContextConflicts(
277
- this._extractTraceparentContext(carrier), spanContext, carrier)
278
- break
279
- }
280
-
295
+ let extractedContext = null
281
296
  switch (extractor) {
282
297
  case 'datadog':
283
- spanContext = this._extractDatadogContext(carrier)
298
+ extractedContext = this._extractDatadogContext(carrier)
284
299
  break
285
300
  case 'tracecontext':
286
- spanContext = this._extractTraceparentContext(carrier)
301
+ extractedContext = this._extractTraceparentContext(carrier)
287
302
  break
288
303
  case 'b3' && this
289
304
  ._config
290
305
  .tracePropagationStyle
291
306
  .otelPropagators: // TODO: should match "b3 single header" in next major
292
307
  case 'b3 single header': // TODO: delete in major after singular "b3"
293
- spanContext = this._extractB3SingleContext(carrier)
308
+ extractedContext = this._extractB3SingleContext(carrier)
294
309
  break
295
310
  case 'b3':
296
311
  case 'b3multi':
297
- spanContext = this._extractB3MultiContext(carrier)
312
+ extractedContext = this._extractB3MultiContext(carrier)
298
313
  break
299
314
  default:
300
- log.warn(`Unknown propagation style: ${extractor}`)
315
+ if (extractor !== 'baggage') log.warn(`Unknown propagation style: ${extractor}`)
316
+ }
317
+
318
+ if (extractedContext === null) { // If the current extractor was invalid, continue to the next extractor
319
+ continue
320
+ }
321
+
322
+ if (context === null) {
323
+ context = extractedContext
324
+ if (this._config.tracePropagationExtractFirst) {
325
+ return context
326
+ }
327
+ } else {
328
+ // If extractor is tracecontext, add tracecontext specific information to the context
329
+ if (extractor === 'tracecontext') {
330
+ context = this._resolveTraceContextConflicts(
331
+ this._extractTraceparentContext(carrier), context, carrier)
332
+ }
333
+ if (extractedContext._traceId && extractedContext._spanId &&
334
+ extractedContext.toTraceId(true) !== context.toTraceId(true)) {
335
+ const link = {
336
+ context: extractedContext,
337
+ attributes: { reason: 'terminated_context', context_headers: extractor }
338
+ }
339
+ context._links.push(link)
340
+ }
341
+ }
342
+
343
+ if (this._config.tracePropagationStyle.extract.includes('baggage') && carrier.baggage) {
344
+ context = context || new DatadogSpanContext()
345
+ this._extractBaggageItems(carrier, context)
301
346
  }
302
347
  }
303
348
 
304
- return spanContext || this._extractSqsdContext(carrier)
349
+ return context || this._extractSqsdContext(carrier)
305
350
  }
306
351
 
307
352
  _extractDatadogContext (carrier) {
@@ -310,7 +355,7 @@ class TextMapPropagator {
310
355
  if (!spanContext) return spanContext
311
356
 
312
357
  this._extractOrigin(carrier, spanContext)
313
- this._extractBaggageItems(carrier, spanContext)
358
+ this._extractLegacyBaggageItems(carrier, spanContext)
314
359
  this._extractSamplingPriority(carrier, spanContext)
315
360
  this._extractTags(carrier, spanContext)
316
361
 
@@ -383,7 +428,7 @@ class TextMapPropagator {
383
428
  return null
384
429
  }
385
430
  const matches = headerValue.trim().match(traceparentExpr)
386
- if (matches.length) {
431
+ if (matches?.length) {
387
432
  const [version, traceId, spanId, flags, tail] = matches.slice(1)
388
433
  const traceparent = { version }
389
434
  const tracestate = TraceState.fromString(carrier.tracestate)
@@ -444,7 +489,7 @@ class TextMapPropagator {
444
489
  }
445
490
  })
446
491
 
447
- this._extractBaggageItems(carrier, spanContext)
492
+ this._extractLegacyBaggageItems(carrier, spanContext)
448
493
  return spanContext
449
494
  }
450
495
  return null
@@ -528,14 +573,43 @@ class TextMapPropagator {
528
573
  }
529
574
  }
530
575
 
531
- _extractBaggageItems (carrier, spanContext) {
532
- Object.keys(carrier).forEach(key => {
533
- const match = key.match(baggageExpr)
576
+ _decodeOtelBaggageKey (key) {
577
+ let decoded = decodeURIComponent(key)
578
+ decoded = decoded.replaceAll('%28', '(')
579
+ decoded = decoded.replaceAll('%29', ')')
580
+ return decoded
581
+ }
582
+
583
+ _extractLegacyBaggageItems (carrier, spanContext) {
584
+ if (this._config.legacyBaggageEnabled) {
585
+ Object.keys(carrier).forEach(key => {
586
+ const match = key.match(baggageExpr)
587
+
588
+ if (match) {
589
+ spanContext._baggageItems[match[1]] = carrier[key]
590
+ }
591
+ })
592
+ }
593
+ }
534
594
 
535
- if (match) {
536
- spanContext._baggageItems[match[1]] = carrier[key]
595
+ _extractBaggageItems (carrier, spanContext) {
596
+ const baggages = carrier.baggage.split(',')
597
+ for (const keyValue of baggages) {
598
+ if (!keyValue.includes('=')) {
599
+ spanContext._baggageItems = {}
600
+ return
537
601
  }
538
- })
602
+ let [key, value] = keyValue.split('=')
603
+ key = this._decodeOtelBaggageKey(key.trim())
604
+ value = decodeURIComponent(value.trim())
605
+ if (!key || !value) {
606
+ spanContext._baggageItems = {}
607
+ return
608
+ }
609
+ // the current code assumes precedence of ot-baggage- (legacy opentracing baggage) over baggage
610
+ if (key in spanContext._baggageItems) return
611
+ spanContext._baggageItems[key] = value
612
+ }
539
613
  }
540
614
 
541
615
  _extractSamplingPriority (carrier, spanContext) {
@@ -145,6 +145,18 @@ class DatadogSpan {
145
145
  return this._spanContext._baggageItems[key]
146
146
  }
147
147
 
148
+ getAllBaggageItems () {
149
+ return JSON.stringify(this._spanContext._baggageItems)
150
+ }
151
+
152
+ removeBaggageItem (key) {
153
+ delete this._spanContext._baggageItems[key]
154
+ }
155
+
156
+ removeAllBaggageItems () {
157
+ this._spanContext._baggageItems = {}
158
+ }
159
+
148
160
  setTag (key, value) {
149
161
  this._addTags({ [key]: value })
150
162
  return this
@@ -168,6 +180,20 @@ class DatadogSpan {
168
180
  })
169
181
  }
170
182
 
183
+ addSpanPointer (ptrKind, ptrDir, ptrHash) {
184
+ const zeroContext = new SpanContext({
185
+ traceId: id('0'),
186
+ spanId: id('0')
187
+ })
188
+ const attributes = {
189
+ 'ptr.kind': ptrKind,
190
+ 'ptr.dir': ptrDir,
191
+ 'ptr.hash': ptrHash,
192
+ 'link.kind': 'span-pointer'
193
+ }
194
+ this.addLink(zeroContext, attributes)
195
+ }
196
+
171
197
  addEvent (name, attributesOrStartTime, startTime) {
172
198
  const event = { name }
173
199
  if (attributesOrStartTime) {
@@ -18,6 +18,7 @@ class DatadogSpanContext {
18
18
  this._tags = props.tags || {}
19
19
  this._sampling = props.sampling || {}
20
20
  this._spanSampling = undefined
21
+ this._links = props.links || []
21
22
  this._baggageItems = props.baggageItems || {}
22
23
  this._traceparent = props.traceparent
23
24
  this._tracestate = props.tracestate
@@ -52,8 +52,15 @@ class DatadogTracer {
52
52
  ? getContext(options.childOf)
53
53
  : getParent(options.references)
54
54
 
55
+ // as per spec, allow the setting of service name through options
55
56
  const tags = {
56
- 'service.name': this._service
57
+ 'service.name': options?.tags?.service ? String(options.tags.service) : this._service
58
+ }
59
+
60
+ // As per unified service tagging spec if a span is created with a service name different from the global
61
+ // service name it will not inherit the global version value
62
+ if (options?.tags?.service && options.tags.service !== this._service) {
63
+ options.tags.version = undefined
57
64
  }
58
65
 
59
66
  const span = new Span(this, this._processor, this._prioritySampler, {
@@ -17,14 +17,82 @@
17
17
  "$.Attributes.Token",
18
18
  "$.Endpoints.*.Token",
19
19
  "$.PhoneNumber",
20
- "$.PhoneNumbers",
21
- "$.phoneNumbers",
22
20
  "$.PlatformApplication.*.PlatformCredential",
23
21
  "$.PlatformApplication.*.PlatformPrincipal",
24
- "$.Subscriptions.*.Endpoint"
22
+ "$.Subscriptions.*.Endpoint",
23
+ "$.PhoneNumbers[*].PhoneNumber",
24
+ "$.phoneNumbers[*]"
25
25
  ],
26
26
  "expand": [
27
27
  "$.MessageAttributes.*.StringValue"
28
28
  ]
29
+ },
30
+ "eventbridge": {
31
+ "request": [
32
+ "$.AuthParameters.OAuthParameters.OAuthHttpParameters.HeaderParameters[*].Value",
33
+ "$.AuthParameters.OAuthParameters.OAuthHttpParameters.QueryStringParameters[*].Value",
34
+ "$.AuthParameters.OAuthParameters.OAuthHttpParameters.BodyParameters[*].Value",
35
+ "$.AuthParameters.InvocationHttpParameters.HeaderParameters[*].Value",
36
+ "$.AuthParameters.InvocationHttpParameters.QueryStringParameters[*].Value",
37
+ "$.AuthParameters.InvocationHttpParameters.BodyParameters[*].Value",
38
+ "$.Targets[*].RedshiftDataParameters.Sql",
39
+ "$.Targets[*].RedshiftDataParameters.Sqls",
40
+ "$.Targets[*].AppSyncParameters.GraphQLOperation",
41
+ "$.AuthParameters.BasicAuthParameters.Password",
42
+ "$.AuthParameters.OAuthParameters.ClientParameters.ClientSecret",
43
+ "$.AuthParameters.ApiKeyAuthParameters.ApiKeyValue"
44
+ ],
45
+ "response": [
46
+ "$.AuthParameters.OAuthParameters.OAuthHttpParameters.HeaderParameters[*].Value",
47
+ "$.AuthParameters.OAuthParameters.OAuthHttpParameters.QueryStringParameters[*].Value",
48
+ "$.AuthParameters.OAuthParameters.OAuthHttpParameters.BodyParameters[*].Value",
49
+ "$.AuthParameters.InvocationHttpParameters.HeaderParameters[*].Value",
50
+ "$.AuthParameters.InvocationHttpParameters.QueryStringParameters[*].Value",
51
+ "$.AuthParameters.InvocationHttpParameters.BodyParameters[*].Value",
52
+ "$.Targets[*].RedshiftDataParameters.Sql",
53
+ "$.Targets[*].RedshiftDataParameters.Sqls",
54
+ "$.Targets[*].AppSyncParameters.GraphQLOperation"
55
+ ],
56
+ "expand": [
57
+ ]
58
+ },
59
+ "s3": {
60
+ "request": [
61
+ "$.SSEKMSKeyId",
62
+ "$.SSEKMSEncryptionContext",
63
+ "$.ServerSideEncryptionConfiguration.Rules[*].ApplyServerSideEncryptionByDefault.KMSMasterKeyID",
64
+ "$.InventoryConfiguration.Destination.S3BucketDestination.Encryption.SSEKMS.KeyId",
65
+ "$.SSECustomerKey",
66
+ "$.CopySourceSSECustomerKey",
67
+ "$.RestoreRequest.OutputLocation.S3.Encryption.KMSKeyId"
68
+
69
+ ],
70
+ "response": [
71
+ "$.SSEKMSKeyId",
72
+ "$.SSEKMSEncryptionContext",
73
+ "$.ServerSideEncryptionConfiguration.Rules[*].ApplyServerSideEncryptionByDefault.KMSMasterKeyID",
74
+ "$.InventoryConfiguration.Destination.S3BucketDestination.Encryption.SSEKMS.KeyId",
75
+ "$.Credentials.SecretAccessKey",
76
+ "$.Credentials.SessionToken",
77
+ "$.InventoryConfigurationList[*].Destination.S3BucketDestination.Encryption.SSEKMS.KeyId"
78
+ ],
79
+ "expand": [
80
+ ]
81
+ },
82
+ "sqs": {
83
+ "request": [
84
+ ],
85
+ "response": [
86
+ ],
87
+ "expand": [
88
+ ]
89
+ },
90
+ "kinesis": {
91
+ "request": [
92
+ ],
93
+ "response": [
94
+ ],
95
+ "expand": [
96
+ ]
29
97
  }
30
98
  }
@@ -7,6 +7,7 @@ const {
7
7
  PEER_SERVICE_REMAP_KEY
8
8
  } = require('../constants')
9
9
  const TracingPlugin = require('./tracing')
10
+ const { exitTags } = require('../../../datadog-code-origin')
10
11
 
11
12
  const COMMON_PEER_SVC_SOURCE_TAGS = [
12
13
  'net.peer.name',
@@ -25,6 +26,14 @@ class OutboundPlugin extends TracingPlugin {
25
26
  })
26
27
  }
27
28
 
29
+ startSpan (...args) {
30
+ const span = super.startSpan(...args)
31
+ if (this._tracerConfig.codeOriginForSpans.enabled) {
32
+ span.addTags(exitTags(this.startSpan))
33
+ }
34
+ return span
35
+ }
36
+
28
37
  getPeerService (tags) {
29
38
  /**
30
39
  * Compute `peer.service` and associated metadata from available tags, based
@@ -101,9 +101,8 @@ class TracingPlugin extends Plugin {
101
101
  }
102
102
  }
103
103
 
104
- startSpan (name, { childOf, kind, meta, metrics, service, resource, type } = {}, enter = true) {
104
+ startSpan (name, { childOf, kind, meta, metrics, service, resource, type, extractedLinks } = {}, enter = true) {
105
105
  const store = storage.getStore()
106
-
107
106
  if (store && childOf === undefined) {
108
107
  childOf = store.span
109
108
  }
@@ -119,7 +118,8 @@ class TracingPlugin extends Plugin {
119
118
  ...meta,
120
119
  ...metrics
121
120
  },
122
- integrationName: type
121
+ integrationName: type,
122
+ links: extractedLinks
123
123
  })
124
124
 
125
125
  analyticsSampler.sample(span, this.config.measured)
@@ -0,0 +1,121 @@
1
+ const log = require('../../log')
2
+ const tags = require('../../../../../ext/tags')
3
+
4
+ const RESOURCE_NAME = tags.RESOURCE_NAME
5
+ const HTTP_ROUTE = tags.HTTP_ROUTE
6
+ const SPAN_KIND = tags.SPAN_KIND
7
+ const SPAN_TYPE = tags.SPAN_TYPE
8
+ const HTTP_URL = tags.HTTP_URL
9
+ const HTTP_METHOD = tags.HTTP_METHOD
10
+
11
+ const PROXY_HEADER_SYSTEM = 'x-dd-proxy'
12
+ const PROXY_HEADER_START_TIME_MS = 'x-dd-proxy-request-time-ms'
13
+ const PROXY_HEADER_PATH = 'x-dd-proxy-path'
14
+ const PROXY_HEADER_HTTPMETHOD = 'x-dd-proxy-httpmethod'
15
+ const PROXY_HEADER_DOMAIN = 'x-dd-proxy-domain-name'
16
+ const PROXY_HEADER_STAGE = 'x-dd-proxy-stage'
17
+
18
+ const supportedProxies = {
19
+ 'aws-apigateway': {
20
+ spanName: 'aws.apigateway',
21
+ component: 'aws-apigateway'
22
+ }
23
+ }
24
+
25
+ function createInferredProxySpan (headers, childOf, tracer, context) {
26
+ if (!headers) {
27
+ return null
28
+ }
29
+
30
+ if (!tracer._config?.inferredProxyServicesEnabled) {
31
+ return null
32
+ }
33
+
34
+ const proxyContext = extractInferredProxyContext(headers)
35
+
36
+ if (!proxyContext) {
37
+ return null
38
+ }
39
+
40
+ const proxySpanInfo = supportedProxies[proxyContext.proxySystemName]
41
+
42
+ log.debug(`Successfully extracted inferred span info ${proxyContext} for proxy: ${proxyContext.proxySystemName}`)
43
+
44
+ const span = tracer.startSpan(
45
+ proxySpanInfo.spanName,
46
+ {
47
+ childOf,
48
+ type: 'web',
49
+ startTime: proxyContext.requestTime,
50
+ tags: {
51
+ service: proxyContext.domainName || tracer._config.service,
52
+ component: proxySpanInfo.component,
53
+ [SPAN_KIND]: 'internal',
54
+ [SPAN_TYPE]: 'web',
55
+ [HTTP_METHOD]: proxyContext.method,
56
+ [HTTP_URL]: proxyContext.domainName + proxyContext.path,
57
+ [HTTP_ROUTE]: proxyContext.path,
58
+ stage: proxyContext.stage
59
+ }
60
+ }
61
+ )
62
+
63
+ tracer.scope().activate(span)
64
+ context.inferredProxySpan = span
65
+ childOf = span
66
+
67
+ log.debug('Successfully created inferred proxy span.')
68
+
69
+ setInferredProxySpanTags(span, proxyContext)
70
+
71
+ return childOf
72
+ }
73
+
74
+ function setInferredProxySpanTags (span, proxyContext) {
75
+ span.setTag(RESOURCE_NAME, `${proxyContext.method} ${proxyContext.path}`)
76
+ span.setTag('_dd.inferred_span', '1')
77
+ return span
78
+ }
79
+
80
+ function extractInferredProxyContext (headers) {
81
+ if (!(PROXY_HEADER_START_TIME_MS in headers)) {
82
+ return null
83
+ }
84
+
85
+ if (!(PROXY_HEADER_SYSTEM in headers && headers[PROXY_HEADER_SYSTEM] in supportedProxies)) {
86
+ log.debug(`Received headers to create inferred proxy span but headers include an unsupported proxy type ${headers}`)
87
+ return null
88
+ }
89
+
90
+ return {
91
+ requestTime: headers[PROXY_HEADER_START_TIME_MS]
92
+ ? parseInt(headers[PROXY_HEADER_START_TIME_MS], 10)
93
+ : null,
94
+ method: headers[PROXY_HEADER_HTTPMETHOD],
95
+ path: headers[PROXY_HEADER_PATH],
96
+ stage: headers[PROXY_HEADER_STAGE],
97
+ domainName: headers[PROXY_HEADER_DOMAIN],
98
+ proxySystemName: headers[PROXY_HEADER_SYSTEM]
99
+ }
100
+ }
101
+
102
+ function finishInferredProxySpan (context) {
103
+ const { req } = context
104
+
105
+ if (!context.inferredProxySpan) return
106
+
107
+ if (context.inferredProxySpanFinished && !req.stream) return
108
+
109
+ // context.config.hooks.request(context.inferredProxySpan, req, res) # TODO: Do we need this??
110
+
111
+ // Only close the inferred span if one was created
112
+ if (context.inferredProxySpan) {
113
+ context.inferredProxySpan.finish()
114
+ context.inferredProxySpanFinished = true
115
+ }
116
+ }
117
+
118
+ module.exports = {
119
+ createInferredProxySpan,
120
+ finishInferredProxySpan
121
+ }
@@ -8,7 +8,6 @@ const ipHeaderList = [
8
8
  'x-real-ip',
9
9
  'true-client-ip',
10
10
  'x-client-ip',
11
- 'x-forwarded',
12
11
  'forwarded-for',
13
12
  'x-cluster-client-ip',
14
13
  'fastly-client-ip',
@@ -10,6 +10,7 @@ const kinds = require('../../../../../ext/kinds')
10
10
  const urlFilter = require('./urlfilter')
11
11
  const { extractIp } = require('./ip_extractor')
12
12
  const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../constants')
13
+ const { createInferredProxySpan, finishInferredProxySpan } = require('./inferred_proxy')
13
14
 
14
15
  const WEB = types.WEB
15
16
  const SERVER = kinds.SERVER
@@ -97,7 +98,7 @@ const web = {
97
98
  context.span.context()._name = name
98
99
  span = context.span
99
100
  } else {
100
- span = web.startChildSpan(tracer, name, req.headers)
101
+ span = web.startChildSpan(tracer, name, req)
101
102
  }
102
103
 
103
104
  context.tracer = tracer
@@ -253,9 +254,20 @@ const web = {
253
254
  },
254
255
 
255
256
  // Extract the parent span from the headers and start a new span as its child
256
- startChildSpan (tracer, name, headers) {
257
- const childOf = tracer.extract(FORMAT_HTTP_HEADERS, headers)
258
- const span = tracer.startSpan(name, { childOf })
257
+ startChildSpan (tracer, name, req) {
258
+ const headers = req.headers
259
+ const context = contexts.get(req)
260
+ let childOf = tracer.extract(FORMAT_HTTP_HEADERS, headers)
261
+
262
+ // we may have headers signaling a router proxy span should be created (such as for AWS API Gateway)
263
+ if (tracer._config?.inferredProxyServicesEnabled) {
264
+ const proxySpan = createInferredProxySpan(headers, childOf, tracer, context)
265
+ if (proxySpan) {
266
+ childOf = proxySpan
267
+ }
268
+ }
269
+
270
+ const span = tracer.startSpan(name, { childOf, extractedLinks: childOf?.links })
259
271
 
260
272
  return span
261
273
  },
@@ -263,13 +275,21 @@ const web = {
263
275
  // Validate a request's status code and then add error tags if necessary
264
276
  addStatusError (req, statusCode) {
265
277
  const context = contexts.get(req)
266
- const span = context.span
267
- const error = context.error
268
- const hasExistingError = span.context()._tags.error || span.context()._tags[ERROR_MESSAGE]
278
+ const { span, inferredProxySpan, error } = context
269
279
 
270
- if (!hasExistingError && !context.config.validateStatus(statusCode)) {
280
+ const spanHasExistingError = span.context()._tags.error || span.context()._tags[ERROR_MESSAGE]
281
+ const inferredSpanContext = inferredProxySpan?.context()
282
+ const inferredSpanHasExistingError = inferredSpanContext?._tags.error || inferredSpanContext?._tags[ERROR_MESSAGE]
283
+
284
+ const isValidStatusCode = context.config.validateStatus(statusCode)
285
+
286
+ if (!spanHasExistingError && !isValidStatusCode) {
271
287
  span.setTag(ERROR, error || true)
272
288
  }
289
+
290
+ if (inferredProxySpan && !inferredSpanHasExistingError && !isValidStatusCode) {
291
+ inferredProxySpan.setTag(ERROR, error || true)
292
+ }
273
293
  },
274
294
 
275
295
  // Add an error to the request
@@ -316,6 +336,8 @@ const web = {
316
336
  web.finishMiddleware(context)
317
337
 
318
338
  web.finishSpan(context)
339
+
340
+ finishInferredProxySpan(context)
319
341
  },
320
342
 
321
343
  obfuscateQs (config, url) {
@@ -426,7 +448,7 @@ function reactivate (req, fn) {
426
448
  }
427
449
 
428
450
  function addRequestTags (context, spanType) {
429
- const { req, span, config } = context
451
+ const { req, span, inferredProxySpan, config } = context
430
452
  const url = extractURL(req)
431
453
 
432
454
  span.addTags({
@@ -443,6 +465,7 @@ function addRequestTags (context, spanType) {
443
465
 
444
466
  if (clientIp) {
445
467
  span.setTag(HTTP_CLIENT_IP, clientIp)
468
+ inferredProxySpan?.setTag(HTTP_CLIENT_IP, clientIp)
446
469
  }
447
470
  }
448
471
 
@@ -450,7 +473,7 @@ function addRequestTags (context, spanType) {
450
473
  }
451
474
 
452
475
  function addResponseTags (context) {
453
- const { req, res, paths, span } = context
476
+ const { req, res, paths, span, inferredProxySpan } = context
454
477
 
455
478
  if (paths.length > 0) {
456
479
  span.setTag(HTTP_ROUTE, paths.join(''))
@@ -459,6 +482,9 @@ function addResponseTags (context) {
459
482
  span.addTags({
460
483
  [HTTP_STATUS_CODE]: res.statusCode
461
484
  })
485
+ inferredProxySpan?.addTags({
486
+ [HTTP_STATUS_CODE]: res.statusCode
487
+ })
462
488
 
463
489
  web.addStatusError(req, res.statusCode)
464
490
  }
@@ -477,7 +503,7 @@ function addResourceTag (context) {
477
503
  }
478
504
 
479
505
  function addHeaders (context) {
480
- const { req, res, config, span } = context
506
+ const { req, res, config, span, inferredProxySpan } = context
481
507
 
482
508
  config.headers.forEach(([key, tag]) => {
483
509
  const reqHeader = req.headers[key]
@@ -485,10 +511,12 @@ function addHeaders (context) {
485
511
 
486
512
  if (reqHeader) {
487
513
  span.setTag(tag || `${HTTP_REQUEST_HEADERS}.${key}`, reqHeader)
514
+ inferredProxySpan?.setTag(tag || `${HTTP_REQUEST_HEADERS}.${key}`, reqHeader)
488
515
  }
489
516
 
490
517
  if (resHeader) {
491
518
  span.setTag(tag || `${HTTP_RESPONSE_HEADERS}.${key}`, resHeader)
519
+ inferredProxySpan?.setTag(tag || `${HTTP_RESPONSE_HEADERS}.${key}`, resHeader)
492
520
  }
493
521
  })
494
522
  }