dd-trace 5.90.0 → 5.92.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 (42) hide show
  1. package/index.d.ts +7 -0
  2. package/package.json +8 -7
  3. package/packages/datadog-instrumentations/src/child_process.js +14 -8
  4. package/packages/datadog-instrumentations/src/cucumber.js +18 -3
  5. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  6. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  7. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  8. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +30 -0
  9. package/packages/datadog-instrumentations/src/jest.js +9 -2
  10. package/packages/datadog-instrumentations/src/langgraph.js +7 -0
  11. package/packages/datadog-instrumentations/src/mocha/main.js +32 -9
  12. package/packages/datadog-instrumentations/src/mocha/utils.js +0 -1
  13. package/packages/datadog-instrumentations/src/mocha/worker.js +2 -2
  14. package/packages/datadog-instrumentations/src/vitest.js +53 -24
  15. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -1
  16. package/packages/datadog-plugin-cypress/src/support.js +5 -7
  17. package/packages/datadog-plugin-graphql/src/execute.js +2 -2
  18. package/packages/datadog-plugin-graphql/src/resolve.js +22 -35
  19. package/packages/datadog-plugin-http/src/client.js +1 -1
  20. package/packages/datadog-plugin-langgraph/src/index.js +24 -0
  21. package/packages/datadog-plugin-langgraph/src/stream.js +41 -0
  22. package/packages/dd-trace/src/config/defaults.js +3 -0
  23. package/packages/dd-trace/src/config/index.js +14 -1
  24. package/packages/dd-trace/src/config/supported-configurations.json +7 -0
  25. package/packages/dd-trace/src/constants.js +1 -0
  26. package/packages/dd-trace/src/crashtracking/crashtracker.js +1 -1
  27. package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -0
  28. package/packages/dd-trace/src/dogstatsd.js +1 -0
  29. package/packages/dd-trace/src/encode/agentless-json.js +4 -1
  30. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +11 -0
  31. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +2 -0
  32. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +114 -0
  33. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -2
  34. package/packages/dd-trace/src/plugins/index.js +1 -0
  35. package/packages/dd-trace/src/plugins/util/test.js +7 -10
  36. package/packages/dd-trace/src/priority_sampler.js +20 -2
  37. package/packages/dd-trace/src/process-tags/index.js +41 -34
  38. package/packages/dd-trace/src/profiling/profilers/wall.js +9 -1
  39. package/packages/dd-trace/src/proxy.js +4 -0
  40. package/packages/dd-trace/src/telemetry/send-data.js +5 -0
  41. package/packages/dd-trace/src/telemetry/session-propagation.js +78 -0
  42. package/packages/dd-trace/src/telemetry/telemetry.js +3 -0
@@ -12,8 +12,19 @@ const ENTRYPOINT_PATH = require.main?.filename || ''
12
12
  // entrypoint.basedir = baz
13
13
  // package.json.name = <from package.json>
14
14
 
15
- // process tags are constant throughout the lifetime of a process
16
- function getProcessTags () {
15
+ /**
16
+ * Sanitize a process tag value
17
+ *
18
+ * @param {string} value
19
+ * @returns {string}
20
+ */
21
+ function sanitize (value) {
22
+ return String(value)
23
+ .toLowerCase()
24
+ .replaceAll(/[^a-zA-Z0-9/_.-]+/g, '_')
25
+ }
26
+
27
+ function buildProcessTags (config) {
17
28
  // Lazy load pkg to avoid issues with require.main during test initialization
18
29
  const pkg = require('../pkg')
19
30
 
@@ -35,6 +46,13 @@ function getProcessTags () {
35
46
  ['package.json.name', pkg.name || undefined],
36
47
  ]
37
48
 
49
+ // If config dependent tags keep growing, we should consider moving this into a function
50
+ if (config?.isServiceNameInferred) {
51
+ tags.push(['svc.auto', config.service])
52
+ } else if (config) {
53
+ tags.push(['svc.user', true])
54
+ }
55
+
38
56
  const tagsArray = []
39
57
  const tagsObject = {}
40
58
 
@@ -46,38 +64,27 @@ function getProcessTags () {
46
64
  }
47
65
  }
48
66
 
49
- const serialized = tagsArray.join(',')
50
-
51
- return {
52
- tags,
53
- serialized,
54
- tagsObject,
55
- tagsArray,
56
- }
67
+ processTags.tags = tags
68
+ processTags.serialized = tagsArray.join(',')
69
+ processTags.tagsObject = tagsObject
70
+ processTags.tagsArray = tagsArray
57
71
  }
58
72
 
59
- // Export the singleton
60
- module.exports = getProcessTags()
61
-
62
- module.exports.TRACING_FIELD_NAME = '_dd.tags.process'
63
- module.exports.DSM_FIELD_NAME = 'ProcessTags'
64
- module.exports.PROFILING_FIELD_NAME = 'process_tags'
65
- module.exports.DYNAMIC_INSTRUMENTATION_FIELD_NAME = 'process_tags'
66
- module.exports.TELEMETRY_FIELD_NAME = 'process_tags'
67
- module.exports.REMOTE_CONFIG_FIELD_NAME = 'process_tags'
68
- module.exports.CRASH_TRACKING_FIELD_NAME = 'process_tags'
69
- module.exports.CLIENT_TRACE_STATISTICS_FIELD_NAME = 'ProcessTags'
70
-
71
- /**
72
- * Sanitize a process tag value
73
- *
74
- * @param {string} value
75
- * @returns {string}
76
- */
77
- function sanitize (value) {
78
- return String(value)
79
- .toLowerCase()
80
- .replaceAll(/[^a-zA-Z0-9/_.-]+/g, '_')
73
+ // Singleton with constant defaults so pre-init reads don't blow up
74
+ const processTags = module.exports = {
75
+ initialize (config) {
76
+ // check if one of the properties added during build exist and if so return
77
+ if (processTags.tags) return
78
+ buildProcessTags(config)
79
+ },
80
+
81
+ TRACING_FIELD_NAME: '_dd.tags.process',
82
+ DSM_FIELD_NAME: 'ProcessTags',
83
+ PROFILING_FIELD_NAME: 'process_tags',
84
+ DYNAMIC_INSTRUMENTATION_FIELD_NAME: 'process_tags',
85
+ TELEMETRY_FIELD_NAME: 'process_tags',
86
+ REMOTE_CONFIG_FIELD_NAME: 'process_tags',
87
+ CRASH_TRACKING_FIELD_NAME: 'process_tags',
88
+ CLIENT_TRACE_STATISTICS_FIELD_NAME: 'ProcessTags',
89
+ sanitize,
81
90
  }
82
-
83
- module.exports.sanitize = sanitize
@@ -290,7 +290,15 @@ class NativeWallProfiler {
290
290
  }
291
291
 
292
292
  profilingContext = { spanId, rootSpanId, webTags }
293
- span[ProfilingContext] = profilingContext
293
+ // Don't cache if endpoint collection is enabled and webTags is undefined but
294
+ // the span's type hasn't been set yet. TracingPlugin.startSpan() calls
295
+ // enterWith() before the plugin sets span.type='web' via addRequestTags(),
296
+ // so the first enterCh event fires before the type is known. Without this
297
+ // guard we'd cache webTags=undefined and then serve that stale value on the
298
+ // subsequent activation (when span.type='web' is already set).
299
+ if (!this.#endpointCollectionEnabled || webTags !== undefined || context._tags['span.type']) {
300
+ span[ProfilingContext] = profilingContext
301
+ }
294
302
  }
295
303
  return profilingContext
296
304
  }
@@ -13,6 +13,7 @@ const nomenclature = require('./service-naming')
13
13
  const PluginManager = require('./plugin_manager')
14
14
  const NoopDogStatsDClient = require('./noop/dogstatsd')
15
15
  const { IS_SERVERLESS } = require('./serverless')
16
+ const processTags = require('./process-tags')
16
17
  const {
17
18
  setBaggageItem,
18
19
  getBaggageItem,
@@ -102,6 +103,9 @@ class Tracer extends NoopProxy {
102
103
  try {
103
104
  const config = getConfig(options) // TODO: support dynamic code config
104
105
 
106
+ // Add config dependent process tags
107
+ processTags.initialize(config)
108
+
105
109
  // Configure propagation hash manager for process tags + container tags
106
110
  const propagationHash = require('./propagation-hash')
107
111
  propagationHash.configure(config)
@@ -91,12 +91,17 @@ let agentTelemetry = true
91
91
  * @returns {Record<string, string>}
92
92
  */
93
93
  function getHeaders (config, application, reqType) {
94
+ const sessionId = config.tags['runtime-id']
94
95
  const headers = {
95
96
  'content-type': 'application/json',
96
97
  'dd-telemetry-api-version': 'v2',
97
98
  'dd-telemetry-request-type': reqType,
98
99
  'dd-client-library-language': application.language_name,
99
100
  'dd-client-library-version': application.tracer_version,
101
+ 'dd-session-id': sessionId,
102
+ }
103
+ if (config.rootSessionId && config.rootSessionId !== sessionId) {
104
+ headers['dd-root-session-id'] = config.rootSessionId
100
105
  }
101
106
  const debug = config.telemetry && config.telemetry.debug
102
107
  if (debug) {
@@ -0,0 +1,78 @@
1
+ 'use strict'
2
+
3
+ const dc = require('dc-polyfill')
4
+
5
+ const childProcessChannel = dc.tracingChannel('datadog:child_process:execution')
6
+
7
+ let subscribed = false
8
+ let rootSessionId
9
+ let runtimeId
10
+
11
+ function injectSessionEnv (existingEnv) {
12
+ // eslint-disable-next-line eslint-rules/eslint-process-env -- not in supported-configurations.json
13
+ const base = existingEnv == null ? process.env : existingEnv
14
+ return {
15
+ ...base,
16
+ DD_ROOT_JS_SESSION_ID: rootSessionId,
17
+ DD_PARENT_JS_SESSION_ID: runtimeId,
18
+ }
19
+ }
20
+
21
+ function findOptionsIndex (args, shell) {
22
+ if (Array.isArray(args[1])) {
23
+ return { index: 2, exists: args[2] != null && typeof args[2] === 'object' }
24
+ }
25
+ if (args[1] != null && typeof args[1] === 'object') {
26
+ return { index: 1, exists: true }
27
+ }
28
+ if (!shell && args[2] != null && typeof args[2] === 'object') {
29
+ return { index: 2, exists: true }
30
+ }
31
+ return { index: shell ? 1 : 2, exists: false }
32
+ }
33
+
34
+ function onChildProcessStart (context) {
35
+ if (!context.callArgs) return
36
+
37
+ const args = context.callArgs
38
+ const { index, exists } = findOptionsIndex(args, context.shell)
39
+
40
+ if (exists) {
41
+ args[index] = { ...args[index], env: injectSessionEnv(args[index].env) }
42
+ return
43
+ }
44
+
45
+ const opts = { env: injectSessionEnv(null) }
46
+
47
+ if (!context.shell && !Array.isArray(args[1])) {
48
+ args.splice(1, 0, [])
49
+ }
50
+
51
+ if (typeof args[index] === 'function') {
52
+ args.splice(index, 0, opts)
53
+ } else {
54
+ args[index] = opts
55
+ }
56
+ }
57
+
58
+ const handler = { start: onChildProcessStart }
59
+
60
+ function start (config) {
61
+ if (!config.telemetry?.enabled || subscribed) return
62
+ subscribed = true
63
+
64
+ rootSessionId = config.rootSessionId
65
+ runtimeId = config.tags['runtime-id']
66
+
67
+ childProcessChannel.subscribe(handler)
68
+ }
69
+
70
+ function stop () {
71
+ if (!subscribed) return
72
+ childProcessChannel.unsubscribe(handler)
73
+ subscribed = false
74
+ rootSessionId = undefined
75
+ runtimeId = undefined
76
+ }
77
+
78
+ module.exports = { start, stop, _onChildProcessStart: onChildProcessStart }
@@ -12,6 +12,7 @@ const endpoints = require('./endpoints')
12
12
  const { sendData } = require('./send-data')
13
13
  const { manager: metricsManager } = require('./metrics')
14
14
  const telemetryLogger = require('./logs')
15
+ const sessionPropagation = require('./session-propagation')
15
16
 
16
17
  /**
17
18
  * @typedef {Record<string, unknown>} TelemetryPayloadObject
@@ -370,6 +371,7 @@ function start (aConfig, thePluginManager) {
370
371
  dependencies.start(config, application, host, getRetryData, updateRetryData)
371
372
  telemetryLogger.start(config)
372
373
  endpoints.start(config, application, host, getRetryData, updateRetryData)
374
+ sessionPropagation.start(config)
373
375
 
374
376
  sendData(config, application, host, 'app-started', appStarted(config))
375
377
 
@@ -397,6 +399,7 @@ function stop () {
397
399
  telemetryStopChannel.publish(getTelemetryData())
398
400
 
399
401
  endpoints.stop()
402
+ sessionPropagation.stop()
400
403
  config = undefined
401
404
  }
402
405