dd-trace 5.24.0 → 5.25.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 (98) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +335 -0
  3. package/package.json +13 -7
  4. package/packages/datadog-code-origin/index.js +4 -4
  5. package/packages/datadog-core/src/utils/src/parse-tags.js +33 -0
  6. package/packages/datadog-esbuild/index.js +4 -2
  7. package/packages/datadog-instrumentations/src/amqplib.js +65 -5
  8. package/packages/datadog-instrumentations/src/child_process.js +135 -27
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  10. package/packages/datadog-instrumentations/src/helpers/register.js +9 -0
  11. package/packages/datadog-instrumentations/src/kafkajs.js +123 -63
  12. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -2
  13. package/packages/datadog-instrumentations/src/multer.js +37 -0
  14. package/packages/datadog-instrumentations/src/openai.js +2 -2
  15. package/packages/datadog-instrumentations/src/url.js +84 -0
  16. package/packages/datadog-instrumentations/src/utils/src/extract-package-and-module-path.js +7 -4
  17. package/packages/datadog-plugin-amqplib/src/consumer.js +4 -4
  18. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  19. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  20. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -0
  21. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  22. package/packages/datadog-plugin-fastify/src/code_origin.js +2 -2
  23. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +8 -1
  24. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +8 -0
  25. package/packages/datadog-plugin-grpc/src/client.js +3 -0
  26. package/packages/datadog-plugin-grpc/src/server.js +3 -0
  27. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +6 -3
  28. package/packages/datadog-plugin-kafkajs/src/consumer.js +8 -4
  29. package/packages/datadog-plugin-kafkajs/src/producer.js +10 -4
  30. package/packages/datadog-plugin-mocha/src/index.js +4 -1
  31. package/packages/datadog-plugin-openai/src/index.js +9 -1015
  32. package/packages/datadog-plugin-openai/src/tracing.js +1023 -0
  33. package/packages/dd-trace/src/appsec/addresses.js +2 -0
  34. package/packages/dd-trace/src/appsec/channels.js +3 -1
  35. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +55 -7
  36. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -2
  37. package/packages/dd-trace/src/appsec/index.js +3 -0
  38. package/packages/dd-trace/src/appsec/rasp/command_injection.js +49 -0
  39. package/packages/dd-trace/src/appsec/rasp/index.js +3 -0
  40. package/packages/dd-trace/src/appsec/rasp/ssrf.js +4 -3
  41. package/packages/dd-trace/src/appsec/rasp/utils.js +3 -2
  42. package/packages/dd-trace/src/appsec/recommended.json +2 -4
  43. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -0
  44. package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
  45. package/packages/dd-trace/src/appsec/reporter.js +5 -4
  46. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -3
  47. package/packages/dd-trace/src/appsec/waf/waf_manager.js +4 -0
  48. package/packages/dd-trace/src/azure_metadata.js +120 -0
  49. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +97 -0
  50. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +90 -0
  51. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +19 -1
  52. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +53 -0
  53. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +8 -1
  54. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +43 -0
  55. package/packages/dd-trace/src/config.js +75 -6
  56. package/packages/dd-trace/src/constants.js +3 -1
  57. package/packages/dd-trace/src/datastreams/pathway.js +1 -0
  58. package/packages/dd-trace/src/debugger/devtools_client/index.js +9 -13
  59. package/packages/dd-trace/src/debugger/devtools_client/send.js +15 -1
  60. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +57 -23
  61. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +12 -2
  62. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +31 -20
  63. package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +6 -0
  64. package/packages/dd-trace/src/debugger/devtools_client/state.js +11 -2
  65. package/packages/dd-trace/src/debugger/index.js +10 -3
  66. package/packages/dd-trace/src/llmobs/constants/tags.js +34 -0
  67. package/packages/dd-trace/src/llmobs/constants/text.js +6 -0
  68. package/packages/dd-trace/src/llmobs/constants/writers.js +13 -0
  69. package/packages/dd-trace/src/llmobs/index.js +103 -0
  70. package/packages/dd-trace/src/llmobs/noop.js +82 -0
  71. package/packages/dd-trace/src/llmobs/plugins/base.js +65 -0
  72. package/packages/dd-trace/src/llmobs/plugins/openai.js +205 -0
  73. package/packages/dd-trace/src/llmobs/sdk.js +377 -0
  74. package/packages/dd-trace/src/llmobs/span_processor.js +195 -0
  75. package/packages/dd-trace/src/llmobs/storage.js +7 -0
  76. package/packages/dd-trace/src/llmobs/tagger.js +322 -0
  77. package/packages/dd-trace/src/llmobs/util.js +176 -0
  78. package/packages/dd-trace/src/llmobs/writers/base.js +111 -0
  79. package/packages/dd-trace/src/llmobs/writers/evaluations.js +29 -0
  80. package/packages/dd-trace/src/llmobs/writers/spans/agentProxy.js +23 -0
  81. package/packages/dd-trace/src/llmobs/writers/spans/agentless.js +17 -0
  82. package/packages/dd-trace/src/llmobs/writers/spans/base.js +49 -0
  83. package/packages/dd-trace/src/noop/proxy.js +3 -0
  84. package/packages/dd-trace/src/noop/span.js +3 -0
  85. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  86. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -0
  87. package/packages/dd-trace/src/opentracing/propagation/text_map.js +73 -12
  88. package/packages/dd-trace/src/opentracing/span.js +12 -0
  89. package/packages/dd-trace/src/opentracing/tracer.js +8 -1
  90. package/packages/dd-trace/src/payload-tagging/config/aws.json +71 -3
  91. package/packages/dd-trace/src/plugins/outbound.js +9 -0
  92. package/packages/dd-trace/src/priority_sampler.js +16 -0
  93. package/packages/dd-trace/src/profiling/config.js +3 -1
  94. package/packages/dd-trace/src/profiling/exporters/agent.js +7 -5
  95. package/packages/dd-trace/src/profiling/profilers/wall.js +2 -1
  96. package/packages/dd-trace/src/proxy.js +8 -1
  97. package/packages/dd-trace/src/span_processor.js +5 -0
  98. package/packages/dd-trace/src/telemetry/index.js +11 -1
@@ -0,0 +1,90 @@
1
+ 'use strict'
2
+
3
+ const { workerData: { breakpointSetChannel, breakpointHitChannel } } = require('worker_threads')
4
+ // TODO: move debugger/devtools_client/session to common place
5
+ const session = require('../../../debugger/devtools_client/session')
6
+ // TODO: move debugger/devtools_client/snapshot to common place
7
+ const { getLocalStateForCallFrame } = require('../../../debugger/devtools_client/snapshot')
8
+ // TODO: move debugger/devtools_client/state to common place
9
+ const {
10
+ findScriptFromPartialPath,
11
+ getStackFromCallFrames
12
+ } = require('../../../debugger/devtools_client/state')
13
+ const log = require('../../../log')
14
+
15
+ let sessionStarted = false
16
+
17
+ const breakpointIdToSnapshotId = new Map()
18
+ const breakpointIdToProbe = new Map()
19
+
20
+ session.on('Debugger.paused', async ({ params: { hitBreakpoints: [hitBreakpoint], callFrames } }) => {
21
+ const probe = breakpointIdToProbe.get(hitBreakpoint)
22
+ if (!probe) {
23
+ log.warn(`No probe found for breakpoint ${hitBreakpoint}`)
24
+ return session.post('Debugger.resume')
25
+ }
26
+
27
+ const stack = getStackFromCallFrames(callFrames)
28
+
29
+ const getLocalState = await getLocalStateForCallFrame(callFrames[0])
30
+
31
+ await session.post('Debugger.resume')
32
+
33
+ const snapshotId = breakpointIdToSnapshotId.get(hitBreakpoint)
34
+
35
+ const snapshot = {
36
+ id: snapshotId,
37
+ timestamp: Date.now(),
38
+ probe: {
39
+ id: probe.probeId,
40
+ version: '0',
41
+ location: probe.location
42
+ },
43
+ stack,
44
+ language: 'javascript'
45
+ }
46
+
47
+ const state = getLocalState()
48
+ if (state) {
49
+ snapshot.captures = {
50
+ lines: { [probe.location.lines[0]]: { locals: state } }
51
+ }
52
+ }
53
+
54
+ breakpointHitChannel.postMessage({ snapshot })
55
+ })
56
+
57
+ // TODO: add option to remove breakpoint
58
+ breakpointSetChannel.on('message', async ({ snapshotId, probe: { id: probeId, file, line } }) => {
59
+ await addBreakpoint(snapshotId, { probeId, file, line })
60
+ breakpointSetChannel.postMessage({ probeId })
61
+ })
62
+
63
+ async function addBreakpoint (snapshotId, probe) {
64
+ if (!sessionStarted) await start()
65
+ const { file, line } = probe
66
+
67
+ probe.location = { file, lines: [String(line)] }
68
+
69
+ const script = findScriptFromPartialPath(file)
70
+ if (!script) throw new Error(`No loaded script found for ${file}`)
71
+
72
+ const [path, scriptId] = script
73
+
74
+ log.debug(`Adding breakpoint at ${path}:${line}`)
75
+
76
+ const { breakpointId } = await session.post('Debugger.setBreakpoint', {
77
+ location: {
78
+ scriptId,
79
+ lineNumber: line - 1
80
+ }
81
+ })
82
+
83
+ breakpointIdToProbe.set(breakpointId, probe)
84
+ breakpointIdToSnapshotId.set(breakpointId, snapshotId)
85
+ }
86
+
87
+ function start () {
88
+ sessionStarted = true
89
+ return session.post('Debugger.enable') // return instead of await to reduce number of promises created
90
+ }
@@ -7,6 +7,7 @@ const CiVisibilityExporter = require('../ci-visibility-exporter')
7
7
 
8
8
  const AGENT_EVP_PROXY_PATH_PREFIX = '/evp_proxy/v'
9
9
  const AGENT_EVP_PROXY_PATH_REGEX = /\/evp_proxy\/v(\d+)\/?/
10
+ const AGENT_DEBUGGER_INPUT = '/debugger/v1/input'
10
11
 
11
12
  function getLatestEvpProxyVersion (err, agentInfo) {
12
13
  if (err) {
@@ -24,6 +25,10 @@ function getLatestEvpProxyVersion (err, agentInfo) {
24
25
  }, 0)
25
26
  }
26
27
 
28
+ function getCanForwardDebuggerLogs (err, agentInfo) {
29
+ return !err && agentInfo.endpoints.some(endpoint => endpoint === AGENT_DEBUGGER_INPUT)
30
+ }
31
+
27
32
  class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
28
33
  constructor (config) {
29
34
  super(config)
@@ -33,7 +38,8 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
33
38
  prioritySampler,
34
39
  lookup,
35
40
  protocolVersion,
36
- headers
41
+ headers,
42
+ isTestDynamicInstrumentationEnabled
37
43
  } = config
38
44
 
39
45
  this.getAgentInfo((err, agentInfo) => {
@@ -60,6 +66,18 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
60
66
  url: this._url,
61
67
  evpProxyPrefix
62
68
  })
69
+ if (isTestDynamicInstrumentationEnabled) {
70
+ const canFowardLogs = getCanForwardDebuggerLogs(err, agentInfo)
71
+ if (canFowardLogs) {
72
+ const DynamicInstrumentationLogsWriter = require('../agentless/di-logs-writer')
73
+ this._logsWriter = new DynamicInstrumentationLogsWriter({
74
+ url: this._url,
75
+ tags,
76
+ isAgentProxy: true
77
+ })
78
+ this._canForwardLogs = true
79
+ }
80
+ }
63
81
  } else {
64
82
  this._writer = new AgentWriter({
65
83
  url: this._url,
@@ -0,0 +1,53 @@
1
+ 'use strict'
2
+ const request = require('../../../exporters/common/request')
3
+ const log = require('../../../log')
4
+ const { safeJSONStringify } = require('../../../exporters/common/util')
5
+ const { JSONEncoder } = require('../../encode/json-encoder')
6
+
7
+ const BaseWriter = require('../../../exporters/common/writer')
8
+
9
+ // Writer used by the integration between Dynamic Instrumentation and Test Visibility
10
+ // It is used to encode and send logs to both the logs intake directly and the
11
+ // `/debugger/v1/input` endpoint in the agent, which is a proxy to the logs intake.
12
+ class DynamicInstrumentationLogsWriter extends BaseWriter {
13
+ constructor ({ url, timeout, isAgentProxy = false }) {
14
+ super(...arguments)
15
+ this._url = url
16
+ this._encoder = new JSONEncoder()
17
+ this._isAgentProxy = isAgentProxy
18
+ this.timeout = timeout
19
+ }
20
+
21
+ _sendPayload (data, _, done) {
22
+ const options = {
23
+ path: '/api/v2/logs',
24
+ method: 'POST',
25
+ headers: {
26
+ 'dd-api-key': process.env.DATADOG_API_KEY || process.env.DD_API_KEY,
27
+ 'Content-Type': 'application/json'
28
+ },
29
+ // TODO: what's a good value for timeout for the logs intake?
30
+ timeout: this.timeout || 15000,
31
+ url: this._url
32
+ }
33
+
34
+ if (this._isAgentProxy) {
35
+ delete options.headers['dd-api-key']
36
+ options.path = '/debugger/v1/input'
37
+ }
38
+
39
+ log.debug(() => `Request to the logs intake: ${safeJSONStringify(options)}`)
40
+
41
+ request(data, options, (err, res) => {
42
+ if (err) {
43
+ log.error(err)
44
+ done()
45
+ return
46
+ }
47
+ log.debug(`Response from the logs intake: ${res}`)
48
+ done()
49
+ })
50
+ }
51
+ }
52
+
53
+ module.exports = DynamicInstrumentationLogsWriter
@@ -9,10 +9,11 @@ const log = require('../../../log')
9
9
  class AgentlessCiVisibilityExporter extends CiVisibilityExporter {
10
10
  constructor (config) {
11
11
  super(config)
12
- const { tags, site, url } = config
12
+ const { tags, site, url, isTestDynamicInstrumentationEnabled } = config
13
13
  // we don't need to request /info because we are using agentless by configuration
14
14
  this._isInitialized = true
15
15
  this._resolveCanUseCiVisProtocol(true)
16
+ this._canForwardLogs = true
16
17
 
17
18
  this._url = url || new URL(`https://citestcycle-intake.${site}`)
18
19
  this._writer = new Writer({ url: this._url, tags })
@@ -20,6 +21,12 @@ class AgentlessCiVisibilityExporter extends CiVisibilityExporter {
20
21
  this._coverageUrl = url || new URL(`https://citestcov-intake.${site}`)
21
22
  this._coverageWriter = new CoverageWriter({ url: this._coverageUrl })
22
23
 
24
+ if (isTestDynamicInstrumentationEnabled) {
25
+ const DynamicInstrumentationLogsWriter = require('./di-logs-writer')
26
+ this._logsUrl = url || new URL(`https://http-intake.logs.${site}`)
27
+ this._logsWriter = new DynamicInstrumentationLogsWriter({ url: this._logsUrl, tags })
28
+ }
29
+
23
30
  this._apiUrl = url || new URL(`https://api.${site}`)
24
31
  // Agentless is always gzip compatible
25
32
  this._isGzipCompatible = true
@@ -8,6 +8,7 @@ const { getSkippableSuites: getSkippableSuitesRequest } = require('../intelligen
8
8
  const { getKnownTests: getKnownTestsRequest } = require('../early-flake-detection/get-known-tests')
9
9
  const log = require('../../log')
10
10
  const AgentInfoExporter = require('../../exporters/common/agent-info-exporter')
11
+ const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../../plugins/util/tags')
11
12
 
12
13
  function getTestConfigurationTags (tags) {
13
14
  if (!tags) {
@@ -36,6 +37,7 @@ class CiVisibilityExporter extends AgentInfoExporter {
36
37
  super(config)
37
38
  this._timer = undefined
38
39
  this._coverageTimer = undefined
40
+ this._logsTimer = undefined
39
41
  this._coverageBuffer = []
40
42
  // The library can use new features like ITR and test suite level visibility
41
43
  // AKA CI Vis Protocol
@@ -255,6 +257,47 @@ class CiVisibilityExporter extends AgentInfoExporter {
255
257
  this._export(formattedCoverage, this._coverageWriter, '_coverageTimer')
256
258
  }
257
259
 
260
+ formatLogMessage (testConfiguration, logMessage) {
261
+ const {
262
+ [GIT_REPOSITORY_URL]: gitRepositoryUrl,
263
+ [GIT_COMMIT_SHA]: gitCommitSha
264
+ } = testConfiguration
265
+
266
+ const { service, env, version } = this._config
267
+
268
+ return {
269
+ ddtags: [
270
+ ...(logMessage.ddtags || []),
271
+ `${GIT_REPOSITORY_URL}:${gitRepositoryUrl}`,
272
+ `${GIT_COMMIT_SHA}:${gitCommitSha}`
273
+ ].join(','),
274
+ level: 'error',
275
+ service,
276
+ dd: {
277
+ ...(logMessage.dd || []),
278
+ service,
279
+ env,
280
+ version
281
+ },
282
+ ddsource: 'dd_debugger',
283
+ ...logMessage
284
+ }
285
+ }
286
+
287
+ // DI logs
288
+ exportDiLogs (testConfiguration, logMessage) {
289
+ // TODO: could we lose logs if it's not initialized?
290
+ if (!this._config.isTestDynamicInstrumentationEnabled || !this._isInitialized || !this._canForwardLogs) {
291
+ return
292
+ }
293
+
294
+ this._export(
295
+ this.formatLogMessage(testConfiguration, logMessage),
296
+ this._logsWriter,
297
+ '_logsTimer'
298
+ )
299
+ }
300
+
258
301
  flush (done = () => {}) {
259
302
  if (!this._isInitialized) {
260
303
  return done()
@@ -17,7 +17,7 @@ const { getGitMetadataFromGitProperties, removeUserSensitiveInfo } = require('./
17
17
  const { updateConfig } = require('./telemetry')
18
18
  const telemetryMetrics = require('./telemetry/metrics')
19
19
  const { getIsGCPFunction, getIsAzureFunction } = require('./serverless')
20
- const { ORIGIN_KEY } = require('./constants')
20
+ const { ORIGIN_KEY, GRPC_CLIENT_ERROR_STATUSES, GRPC_SERVER_ERROR_STATUSES } = require('./constants')
21
21
  const { appendRules } = require('./payload-tagging/config')
22
22
 
23
23
  const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
@@ -462,6 +462,9 @@ class Config {
462
462
  this._setValue(defaults, 'appsec.stackTrace.maxDepth', 32)
463
463
  this._setValue(defaults, 'appsec.stackTrace.maxStackTraces', 2)
464
464
  this._setValue(defaults, 'appsec.wafTimeout', 5e3) // µs
465
+ this._setValue(defaults, 'baggageMaxBytes', 8192)
466
+ this._setValue(defaults, 'baggageMaxItems', 64)
467
+ this._setValue(defaults, 'ciVisibilityTestSessionName', '')
465
468
  this._setValue(defaults, 'clientIpEnabled', false)
466
469
  this._setValue(defaults, 'clientIpHeader', null)
467
470
  this._setValue(defaults, 'codeOriginForSpans.enabled', false)
@@ -477,6 +480,8 @@ class Config {
477
480
  this._setValue(defaults, 'flushInterval', 2000)
478
481
  this._setValue(defaults, 'flushMinSpans', 1000)
479
482
  this._setValue(defaults, 'gitMetadataEnabled', true)
483
+ this._setValue(defaults, 'grpc.client.error.statuses', GRPC_CLIENT_ERROR_STATUSES)
484
+ this._setValue(defaults, 'grpc.server.error.statuses', GRPC_SERVER_ERROR_STATUSES)
480
485
  this._setValue(defaults, 'headerTags', [])
481
486
  this._setValue(defaults, 'hostname', '127.0.0.1')
482
487
  this._setValue(defaults, 'iast.cookieFilterPattern', '.{32,}')
@@ -499,8 +504,13 @@ class Config {
499
504
  this._setValue(defaults, 'isGitUploadEnabled', false)
500
505
  this._setValue(defaults, 'isIntelligentTestRunnerEnabled', false)
501
506
  this._setValue(defaults, 'isManualApiEnabled', false)
507
+ this._setValue(defaults, 'llmobs.agentlessEnabled', false)
508
+ this._setValue(defaults, 'llmobs.enabled', false)
509
+ this._setValue(defaults, 'llmobs.mlApp', undefined)
502
510
  this._setValue(defaults, 'ciVisibilityTestSessionName', '')
503
511
  this._setValue(defaults, 'ciVisAgentlessLogSubmissionEnabled', false)
512
+ this._setValue(defaults, 'legacyBaggageEnabled', true)
513
+ this._setValue(defaults, 'isTestDynamicInstrumentationEnabled', false)
504
514
  this._setValue(defaults, 'logInjection', false)
505
515
  this._setValue(defaults, 'lookup', undefined)
506
516
  this._setValue(defaults, 'memcachedCommandEnabled', false)
@@ -520,7 +530,7 @@ class Config {
520
530
  this._setValue(defaults, 'reportHostname', false)
521
531
  this._setValue(defaults, 'runtimeMetrics', false)
522
532
  this._setValue(defaults, 'sampleRate', undefined)
523
- this._setValue(defaults, 'sampler.rateLimit', undefined)
533
+ this._setValue(defaults, 'sampler.rateLimit', 100)
524
534
  this._setValue(defaults, 'sampler.rules', [])
525
535
  this._setValue(defaults, 'sampler.spanSamplingRules', [])
526
536
  this._setValue(defaults, 'scope', undefined)
@@ -541,11 +551,12 @@ class Config {
541
551
  this._setValue(defaults, 'telemetry.heartbeatInterval', 60000)
542
552
  this._setValue(defaults, 'telemetry.logCollection', false)
543
553
  this._setValue(defaults, 'telemetry.metrics', true)
554
+ this._setValue(defaults, 'traceEnabled', true)
544
555
  this._setValue(defaults, 'traceId128BitGenerationEnabled', true)
545
556
  this._setValue(defaults, 'traceId128BitLoggingEnabled', false)
546
557
  this._setValue(defaults, 'tracePropagationExtractFirst', false)
547
- this._setValue(defaults, 'tracePropagationStyle.inject', ['datadog', 'tracecontext'])
548
- this._setValue(defaults, 'tracePropagationStyle.extract', ['datadog', 'tracecontext'])
558
+ this._setValue(defaults, 'tracePropagationStyle.inject', ['datadog', 'tracecontext', 'baggage'])
559
+ this._setValue(defaults, 'tracePropagationStyle.extract', ['datadog', 'tracecontext', 'baggage'])
549
560
  this._setValue(defaults, 'tracePropagationStyle.otelPropagators', false)
550
561
  this._setValue(defaults, 'tracing', true)
551
562
  this._setValue(defaults, 'url', undefined)
@@ -584,6 +595,8 @@ class Config {
584
595
  DD_EXPERIMENTAL_API_SECURITY_ENABLED,
585
596
  DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED,
586
597
  DD_EXPERIMENTAL_PROFILING_ENABLED,
598
+ DD_GRPC_CLIENT_ERROR_STATUSES,
599
+ DD_GRPC_SERVER_ERROR_STATUSES,
587
600
  JEST_WORKER_ID,
588
601
  DD_IAST_COOKIE_FILTER_PATTERN,
589
602
  DD_IAST_DEDUPLICATION_ENABLED,
@@ -599,6 +612,9 @@ class Config {
599
612
  DD_INSTRUMENTATION_TELEMETRY_ENABLED,
600
613
  DD_INSTRUMENTATION_CONFIG_ID,
601
614
  DD_LOGS_INJECTION,
615
+ DD_LLMOBS_AGENTLESS_ENABLED,
616
+ DD_LLMOBS_ENABLED,
617
+ DD_LLMOBS_ML_APP,
602
618
  DD_OPENAI_LOGS_ENABLED,
603
619
  DD_OPENAI_SPAN_CHAR_LIMIT,
604
620
  DD_PROFILING_ENABLED,
@@ -625,14 +641,18 @@ class Config {
625
641
  DD_TRACE_AGENT_HOSTNAME,
626
642
  DD_TRACE_AGENT_PORT,
627
643
  DD_TRACE_AGENT_PROTOCOL_VERSION,
644
+ DD_TRACE_BAGGAGE_MAX_BYTES,
645
+ DD_TRACE_BAGGAGE_MAX_ITEMS,
628
646
  DD_TRACE_CLIENT_IP_ENABLED,
629
647
  DD_TRACE_CLIENT_IP_HEADER,
648
+ DD_TRACE_ENABLED,
630
649
  DD_TRACE_EXPERIMENTAL_EXPORTER,
631
650
  DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED,
632
651
  DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED,
633
652
  DD_TRACE_GIT_METADATA_ENABLED,
634
653
  DD_TRACE_GLOBAL_TAGS,
635
654
  DD_TRACE_HEADER_TAGS,
655
+ DD_TRACE_LEGACY_BAGGAGE_ENABLED,
636
656
  DD_TRACE_MEMCACHED_COMMAND_ENABLED,
637
657
  DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP,
638
658
  DD_TRACE_PARTIAL_FLUSH_MIN_SPANS,
@@ -704,6 +724,8 @@ class Config {
704
724
  this._envUnprocessed['appsec.stackTrace.maxStackTraces'] = DD_APPSEC_MAX_STACK_TRACES
705
725
  this._setValue(env, 'appsec.wafTimeout', maybeInt(DD_APPSEC_WAF_TIMEOUT))
706
726
  this._envUnprocessed['appsec.wafTimeout'] = DD_APPSEC_WAF_TIMEOUT
727
+ this._setValue(env, 'baggageMaxBytes', DD_TRACE_BAGGAGE_MAX_BYTES)
728
+ this._setValue(env, 'baggageMaxItems', DD_TRACE_BAGGAGE_MAX_ITEMS)
707
729
  this._setBoolean(env, 'clientIpEnabled', DD_TRACE_CLIENT_IP_ENABLED)
708
730
  this._setString(env, 'clientIpHeader', DD_TRACE_CLIENT_IP_HEADER)
709
731
  this._setBoolean(env, 'codeOriginForSpans.enabled', DD_CODE_ORIGIN_FOR_SPANS_ENABLED)
@@ -713,6 +735,7 @@ class Config {
713
735
  this._setBoolean(env, 'dsmEnabled', DD_DATA_STREAMS_ENABLED)
714
736
  this._setBoolean(env, 'dynamicInstrumentationEnabled', DD_DYNAMIC_INSTRUMENTATION_ENABLED)
715
737
  this._setString(env, 'env', DD_ENV || tags.env)
738
+ this._setBoolean(env, 'traceEnabled', DD_TRACE_ENABLED)
716
739
  this._setBoolean(env, 'experimental.enableGetRumData', DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED)
717
740
  this._setString(env, 'experimental.exporter', DD_TRACE_EXPERIMENTAL_EXPORTER)
718
741
  this._setBoolean(env, 'experimental.runtimeId', DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED)
@@ -720,6 +743,8 @@ class Config {
720
743
  this._setValue(env, 'flushMinSpans', maybeInt(DD_TRACE_PARTIAL_FLUSH_MIN_SPANS))
721
744
  this._envUnprocessed.flushMinSpans = DD_TRACE_PARTIAL_FLUSH_MIN_SPANS
722
745
  this._setBoolean(env, 'gitMetadataEnabled', DD_TRACE_GIT_METADATA_ENABLED)
746
+ this._setIntegerRangeSet(env, 'grpc.client.error.statuses', DD_GRPC_CLIENT_ERROR_STATUSES)
747
+ this._setIntegerRangeSet(env, 'grpc.server.error.statuses', DD_GRPC_SERVER_ERROR_STATUSES)
723
748
  this._setArray(env, 'headerTags', DD_TRACE_HEADER_TAGS)
724
749
  this._setString(env, 'hostname', coalesce(DD_AGENT_HOST, DD_TRACE_AGENT_HOSTNAME))
725
750
  this._setString(env, 'iast.cookieFilterPattern', DD_IAST_COOKIE_FILTER_PATTERN)
@@ -741,6 +766,10 @@ class Config {
741
766
  this._setArray(env, 'injectionEnabled', DD_INJECTION_ENABLED)
742
767
  this._setBoolean(env, 'isAzureFunction', getIsAzureFunction())
743
768
  this._setBoolean(env, 'isGCPFunction', getIsGCPFunction())
769
+ this._setBoolean(env, 'legacyBaggageEnabled', DD_TRACE_LEGACY_BAGGAGE_ENABLED)
770
+ this._setBoolean(env, 'llmobs.agentlessEnabled', DD_LLMOBS_AGENTLESS_ENABLED)
771
+ this._setBoolean(env, 'llmobs.enabled', DD_LLMOBS_ENABLED)
772
+ this._setString(env, 'llmobs.mlApp', DD_LLMOBS_ML_APP)
744
773
  this._setBoolean(env, 'logInjection', DD_LOGS_INJECTION)
745
774
  // Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent
746
775
  this._setBoolean(env, 'memcachedCommandEnabled', DD_TRACE_MEMCACHED_COMMAND_ENABLED)
@@ -874,6 +903,8 @@ class Config {
874
903
  this._optsUnprocessed['appsec.wafTimeout'] = options.appsec.wafTimeout
875
904
  this._setBoolean(opts, 'clientIpEnabled', options.clientIpEnabled)
876
905
  this._setString(opts, 'clientIpHeader', options.clientIpHeader)
906
+ this._setValue(opts, 'baggageMaxBytes', options.baggageMaxBytes)
907
+ this._setValue(opts, 'baggageMaxItems', options.baggageMaxItems)
877
908
  this._setBoolean(opts, 'codeOriginForSpans.enabled', options.codeOriginForSpans?.enabled)
878
909
  this._setString(opts, 'dbmPropagationMode', options.dbmPropagationMode)
879
910
  if (options.dogstatsd) {
@@ -911,6 +942,9 @@ class Config {
911
942
  }
912
943
  this._setString(opts, 'iast.telemetryVerbosity', options.iast && options.iast.telemetryVerbosity)
913
944
  this._setBoolean(opts, 'isCiVisibility', options.isCiVisibility)
945
+ this._setBoolean(opts, 'legacyBaggageEnabled', options.legacyBaggageEnabled)
946
+ this._setBoolean(opts, 'llmobs.agentlessEnabled', options.llmobs?.agentlessEnabled)
947
+ this._setString(opts, 'llmobs.mlApp', options.llmobs?.mlApp)
914
948
  this._setBoolean(opts, 'logInjection', options.logInjection)
915
949
  this._setString(opts, 'lookup', options.lookup)
916
950
  this._setBoolean(opts, 'openAiLogsEnabled', options.openAiLogsEnabled)
@@ -946,6 +980,15 @@ class Config {
946
980
  this._setBoolean(opts, 'traceId128BitGenerationEnabled', options.traceId128BitGenerationEnabled)
947
981
  this._setBoolean(opts, 'traceId128BitLoggingEnabled', options.traceId128BitLoggingEnabled)
948
982
  this._setString(opts, 'version', options.version || tags.version)
983
+
984
+ // For LLMObs, we want the environment variable to take precedence over the options.
985
+ // This is reliant on environment config being set before options.
986
+ // This is to make sure the origins of each value are tracked appropriately for telemetry.
987
+ // We'll only set `llmobs.enabled` on the opts when it's not set on the environment, and options.llmobs is provided.
988
+ const llmobsEnabledEnv = this._env['llmobs.enabled']
989
+ if (llmobsEnabledEnv == null && options.llmobs) {
990
+ this._setBoolean(opts, 'llmobs.enabled', !!options.llmobs)
991
+ }
949
992
  }
950
993
 
951
994
  _isCiVisibility () {
@@ -1045,7 +1088,8 @@ class Config {
1045
1088
  DD_CIVISIBILITY_FLAKY_RETRY_ENABLED,
1046
1089
  DD_CIVISIBILITY_FLAKY_RETRY_COUNT,
1047
1090
  DD_TEST_SESSION_NAME,
1048
- DD_AGENTLESS_LOG_SUBMISSION_ENABLED
1091
+ DD_AGENTLESS_LOG_SUBMISSION_ENABLED,
1092
+ DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED
1049
1093
  } = process.env
1050
1094
 
1051
1095
  if (DD_CIVISIBILITY_AGENTLESS_URL) {
@@ -1063,6 +1107,7 @@ class Config {
1063
1107
  this._setBoolean(calc, 'isManualApiEnabled', !isFalse(this._isCiVisibilityManualApiEnabled()))
1064
1108
  this._setString(calc, 'ciVisibilityTestSessionName', DD_TEST_SESSION_NAME)
1065
1109
  this._setBoolean(calc, 'ciVisAgentlessLogSubmissionEnabled', isTrue(DD_AGENTLESS_LOG_SUBMISSION_ENABLED))
1110
+ this._setBoolean(calc, 'isTestDynamicInstrumentationEnabled', isTrue(DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED))
1066
1111
  }
1067
1112
  this._setString(calc, 'dogstatsd.hostname', this._getHostname())
1068
1113
  this._setBoolean(calc, 'isGitUploadEnabled',
@@ -1155,7 +1200,11 @@ class Config {
1155
1200
  }
1156
1201
 
1157
1202
  if (typeof value === 'string') {
1158
- value = value.split(',')
1203
+ value = value.split(',').map(item => {
1204
+ // Trim each item and remove whitespace around the colon
1205
+ const [key, val] = item.split(':').map(part => part.trim())
1206
+ return val !== undefined ? `${key}:${val}` : key
1207
+ })
1159
1208
  }
1160
1209
 
1161
1210
  if (Array.isArray(value)) {
@@ -1163,6 +1212,26 @@ class Config {
1163
1212
  }
1164
1213
  }
1165
1214
 
1215
+ _setIntegerRangeSet (obj, name, value) {
1216
+ if (value == null) {
1217
+ return this._setValue(obj, name, null)
1218
+ }
1219
+ value = value.split(',')
1220
+ const result = []
1221
+
1222
+ value.forEach(val => {
1223
+ if (val.includes('-')) {
1224
+ const [start, end] = val.split('-').map(Number)
1225
+ for (let i = start; i <= end; i++) {
1226
+ result.push(i)
1227
+ }
1228
+ } else {
1229
+ result.push(Number(val))
1230
+ }
1231
+ })
1232
+ this._setValue(obj, name, result)
1233
+ }
1234
+
1166
1235
  _setSamplingRule (obj, name, value) {
1167
1236
  if (value == null) {
1168
1237
  return this._setValue(obj, name, null)
@@ -44,5 +44,7 @@ module.exports = {
44
44
  SCHEMA_ID: 'schema.id',
45
45
  SCHEMA_TOPIC: 'schema.topic',
46
46
  SCHEMA_OPERATION: 'schema.operation',
47
- SCHEMA_NAME: 'schema.name'
47
+ SCHEMA_NAME: 'schema.name',
48
+ GRPC_CLIENT_ERROR_STATUSES: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
49
+ GRPC_SERVER_ERROR_STATUSES: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
48
50
  }
@@ -21,6 +21,7 @@ function shaHash (checkpointString) {
21
21
  }
22
22
 
23
23
  function computeHash (service, env, edgeTags, parentHash) {
24
+ edgeTags.sort()
24
25
  const hashableEdgeTags = edgeTags.filter(item => item !== 'manual_checkpoint:true')
25
26
 
26
27
  const key = `${service}${env}` + hashableEdgeTags.join('') + parentHash.toString()
@@ -5,7 +5,7 @@ const { breakpoints } = require('./state')
5
5
  const session = require('./session')
6
6
  const { getLocalStateForCallFrame } = require('./snapshot')
7
7
  const send = require('./send')
8
- const { getScriptUrlFromId } = require('./state')
8
+ const { getStackFromCallFrames } = require('./state')
9
9
  const { ackEmitting, ackError } = require('./status')
10
10
  const { parentThreadId } = require('./config')
11
11
  const log = require('../../log')
@@ -23,12 +23,14 @@ session.on('Debugger.paused', async ({ params }) => {
23
23
  const timestamp = Date.now()
24
24
 
25
25
  let captureSnapshotForProbe = null
26
- let maxReferenceDepth, maxLength
26
+ let maxReferenceDepth, maxCollectionSize, maxFieldCount, maxLength
27
27
  const probes = params.hitBreakpoints.map((id) => {
28
28
  const probe = breakpoints.get(id)
29
29
  if (probe.captureSnapshot) {
30
30
  captureSnapshotForProbe = probe
31
31
  maxReferenceDepth = highestOrUndefined(probe.capture.maxReferenceDepth, maxReferenceDepth)
32
+ maxCollectionSize = highestOrUndefined(probe.capture.maxCollectionSize, maxCollectionSize)
33
+ maxFieldCount = highestOrUndefined(probe.capture.maxFieldCount, maxFieldCount)
32
34
  maxLength = highestOrUndefined(probe.capture.maxLength, maxLength)
33
35
  }
34
36
  return probe
@@ -38,7 +40,10 @@ session.on('Debugger.paused', async ({ params }) => {
38
40
  if (captureSnapshotForProbe !== null) {
39
41
  try {
40
42
  // TODO: Create unique states for each affected probe based on that probes unique `capture` settings (DEBUG-2863)
41
- processLocalState = await getLocalStateForCallFrame(params.callFrames[0], { maxReferenceDepth, maxLength })
43
+ processLocalState = await getLocalStateForCallFrame(
44
+ params.callFrames[0],
45
+ { maxReferenceDepth, maxCollectionSize, maxFieldCount, maxLength }
46
+ )
42
47
  } catch (err) {
43
48
  // TODO: This error is not tied to a specific probe, but to all probes with `captureSnapshot: true`.
44
49
  // However, in 99,99% of cases, there will be just a single probe, so I guess this simplification is ok?
@@ -61,16 +66,7 @@ session.on('Debugger.paused', async ({ params }) => {
61
66
  thread_name: threadName
62
67
  }
63
68
 
64
- const stack = params.callFrames.map((frame) => {
65
- let fileName = getScriptUrlFromId(frame.location.scriptId)
66
- if (fileName.startsWith('file://')) fileName = fileName.substr(7) // TODO: This might not be required
67
- return {
68
- fileName,
69
- function: frame.functionName,
70
- lineNumber: frame.location.lineNumber + 1, // Beware! lineNumber is zero-indexed
71
- columnNumber: frame.location.columnNumber + 1 // Beware! columnNumber is zero-indexed
72
- }
73
- })
69
+ const stack = getStackFromCallFrames(params.callFrames)
74
70
 
75
71
  // TODO: Send multiple probes in one HTTP request as an array (DEBUG-2848)
76
72
  for (const probe of probes) {
@@ -9,6 +9,8 @@ const { GIT_COMMIT_SHA, GIT_REPOSITORY_URL } = require('../../plugins/util/tags'
9
9
 
10
10
  module.exports = send
11
11
 
12
+ const MAX_PAYLOAD_SIZE = 1024 * 1024 // 1MB
13
+
12
14
  const ddsource = 'dd_debugger'
13
15
  const hostname = getHostname()
14
16
  const service = config.service
@@ -37,5 +39,17 @@ function send (message, logger, snapshot, cb) {
37
39
  'debugger.snapshot': snapshot
38
40
  }
39
41
 
40
- request(JSON.stringify(payload), opts, cb)
42
+ let json = JSON.stringify(payload)
43
+
44
+ if (Buffer.byteLength(json) > MAX_PAYLOAD_SIZE) {
45
+ // TODO: This is a very crude way to handle large payloads. Proper pruning will be implemented later (DEBUG-2624)
46
+ const line = Object.values(payload['debugger.snapshot'].captures.lines)[0]
47
+ line.locals = {
48
+ notCapturedReason: 'Snapshot was too large',
49
+ size: Object.keys(line.locals).length
50
+ }
51
+ json = JSON.stringify(payload)
52
+ }
53
+
54
+ request(json, opts, cb)
41
55
  }