dd-trace 5.55.0 → 5.57.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 (150) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +44 -2
  3. package/init.js +4 -1
  4. package/package.json +24 -23
  5. package/packages/datadog-core/src/utils/src/set.js +8 -10
  6. package/packages/datadog-esbuild/index.js +22 -0
  7. package/packages/datadog-instrumentations/src/cassandra-driver.js +43 -60
  8. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +12 -12
  9. package/packages/datadog-instrumentations/src/cucumber.js +4 -6
  10. package/packages/datadog-instrumentations/src/elasticsearch.js +16 -19
  11. package/packages/datadog-instrumentations/src/fastify.js +91 -9
  12. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +20 -5
  13. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +2 -2
  14. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  15. package/packages/datadog-instrumentations/src/helpers/register.js +17 -5
  16. package/packages/datadog-instrumentations/src/ioredis.js +8 -13
  17. package/packages/datadog-instrumentations/src/iovalkey.js +10 -14
  18. package/packages/datadog-instrumentations/src/jest.js +423 -325
  19. package/packages/datadog-instrumentations/src/memcached.js +17 -24
  20. package/packages/datadog-instrumentations/src/mocha/main.js +7 -6
  21. package/packages/datadog-instrumentations/src/moleculer/client.js +9 -10
  22. package/packages/datadog-instrumentations/src/moleculer/server.js +12 -13
  23. package/packages/datadog-instrumentations/src/openai.js +30 -2
  24. package/packages/datadog-instrumentations/src/playwright.js +4 -1
  25. package/packages/datadog-instrumentations/src/prisma.js +116 -0
  26. package/packages/datadog-instrumentations/src/redis.js +32 -43
  27. package/packages/datadog-instrumentations/src/router.js +1 -1
  28. package/packages/datadog-instrumentations/src/sharedb.js +10 -16
  29. package/packages/datadog-instrumentations/src/vitest.js +4 -4
  30. package/packages/datadog-plugin-aws-sdk/src/base.js +6 -1
  31. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +9 -4
  32. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +3 -2
  33. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -1
  34. package/packages/datadog-plugin-aws-sdk/src/util.js +2 -2
  35. package/packages/datadog-plugin-azure-functions/src/index.js +5 -4
  36. package/packages/datadog-plugin-bunyan/src/index.js +2 -2
  37. package/packages/datadog-plugin-cassandra-driver/src/index.js +6 -2
  38. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/batch-consumer.js +1 -1
  39. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/consumer.js +1 -1
  40. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +1 -1
  41. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/producer.js +1 -1
  42. package/packages/datadog-plugin-cucumber/src/index.js +4 -2
  43. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -5
  44. package/packages/datadog-plugin-elasticsearch/src/index.js +12 -4
  45. package/packages/datadog-plugin-http/src/client.js +1 -0
  46. package/packages/datadog-plugin-http/src/server.js +2 -1
  47. package/packages/datadog-plugin-http2/src/client.js +1 -0
  48. package/packages/datadog-plugin-http2/src/server.js +1 -0
  49. package/packages/datadog-plugin-jest/src/index.js +4 -3
  50. package/packages/datadog-plugin-memcached/src/index.js +6 -2
  51. package/packages/datadog-plugin-mocha/src/index.js +3 -2
  52. package/packages/datadog-plugin-moleculer/src/client.js +15 -9
  53. package/packages/datadog-plugin-moleculer/src/server.js +9 -5
  54. package/packages/datadog-plugin-next/src/index.js +2 -1
  55. package/packages/datadog-plugin-openai/src/tracing.js +127 -80
  56. package/packages/datadog-plugin-oracledb/src/index.js +2 -1
  57. package/packages/datadog-plugin-pino/src/index.js +2 -2
  58. package/packages/datadog-plugin-prisma/src/client.js +62 -0
  59. package/packages/datadog-plugin-prisma/src/engine.js +81 -0
  60. package/packages/datadog-plugin-prisma/src/index.js +22 -0
  61. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  62. package/packages/datadog-plugin-redis/src/index.js +9 -3
  63. package/packages/datadog-plugin-router/src/index.js +1 -0
  64. package/packages/datadog-plugin-sharedb/src/index.js +13 -5
  65. package/packages/datadog-plugin-winston/src/index.js +2 -2
  66. package/packages/dd-trace/src/appsec/channels.js +26 -21
  67. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  68. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +13 -20
  69. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +1 -1
  70. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +44 -1
  71. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -1
  72. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +7 -2
  73. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +0 -1
  74. package/packages/dd-trace/src/appsec/index.js +28 -2
  75. package/packages/dd-trace/src/appsec/rasp/utils.js +0 -5
  76. package/packages/dd-trace/src/appsec/reporter.js +6 -4
  77. package/packages/dd-trace/src/baggage.js +2 -2
  78. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +3 -3
  79. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  80. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  81. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  82. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
  83. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -6
  84. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -3
  85. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +3 -3
  86. package/packages/dd-trace/src/config.js +334 -312
  87. package/packages/dd-trace/src/constants.js +2 -1
  88. package/packages/dd-trace/src/crashtracking/crashtracker.js +12 -14
  89. package/packages/dd-trace/src/datastreams/context.js +1 -1
  90. package/packages/dd-trace/src/datastreams/processor.js +1 -1
  91. package/packages/dd-trace/src/datastreams/writer.js +3 -3
  92. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +6 -3
  93. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  94. package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -3
  95. package/packages/dd-trace/src/debugger/devtools_client/send.js +5 -1
  96. package/packages/dd-trace/src/debugger/devtools_client/state.js +7 -4
  97. package/packages/dd-trace/src/debugger/devtools_client/status.js +5 -1
  98. package/packages/dd-trace/src/dogstatsd.js +3 -3
  99. package/packages/dd-trace/src/exporters/agent/index.js +10 -5
  100. package/packages/dd-trace/src/exporters/agent/writer.js +4 -2
  101. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +2 -2
  102. package/packages/dd-trace/src/exporters/log/index.js +1 -1
  103. package/packages/dd-trace/src/exporters/span-stats/writer.js +2 -2
  104. package/packages/dd-trace/src/guardrails/index.js +3 -1
  105. package/packages/dd-trace/src/guardrails/telemetry.js +1 -1
  106. package/packages/dd-trace/src/llmobs/index.js +11 -5
  107. package/packages/dd-trace/src/llmobs/plugins/base.js +2 -2
  108. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  109. package/packages/dd-trace/src/llmobs/tagger.js +13 -13
  110. package/packages/dd-trace/src/llmobs/writers/base.js +2 -2
  111. package/packages/dd-trace/src/llmobs/writers/spans.js +2 -2
  112. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -1
  113. package/packages/dd-trace/src/opentracing/propagation/text_map.js +22 -28
  114. package/packages/dd-trace/src/opentracing/span.js +1 -0
  115. package/packages/dd-trace/src/plugin_manager.js +3 -3
  116. package/packages/dd-trace/src/plugins/cache.js +2 -2
  117. package/packages/dd-trace/src/plugins/ci_plugin.js +11 -7
  118. package/packages/dd-trace/src/plugins/database.js +3 -1
  119. package/packages/dd-trace/src/plugins/index.js +1 -0
  120. package/packages/dd-trace/src/plugins/log_plugin.js +5 -1
  121. package/packages/dd-trace/src/plugins/outbound.js +8 -6
  122. package/packages/dd-trace/src/plugins/structured_log_plugin.js +9 -0
  123. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  124. package/packages/dd-trace/src/plugins/util/ci.js +83 -30
  125. package/packages/dd-trace/src/plugins/util/git.js +1 -0
  126. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +3 -2
  127. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -0
  128. package/packages/dd-trace/src/plugins/util/tags.js +4 -1
  129. package/packages/dd-trace/src/plugins/util/test.js +80 -10
  130. package/packages/dd-trace/src/plugins/util/web.js +1 -0
  131. package/packages/dd-trace/src/profiler.js +0 -2
  132. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -3
  133. package/packages/dd-trace/src/profiling/profilers/events.js +10 -2
  134. package/packages/dd-trace/src/profiling/ssi-heuristics.js +18 -126
  135. package/packages/dd-trace/src/proxy.js +12 -27
  136. package/packages/dd-trace/src/runtime_metrics/index.js +1 -1
  137. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +14 -45
  138. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +2 -2
  139. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +4 -0
  140. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +2 -2
  141. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  142. package/packages/dd-trace/src/supported-configurations.json +13 -3
  143. package/packages/dd-trace/src/telemetry/telemetry.js +11 -4
  144. package/packages/dd-trace/src/tracer.js +11 -0
  145. package/packages/dd-trace/src/tracer_metadata.js +25 -0
  146. package/packages/dd-trace/src/util.js +11 -4
  147. package/version.js +3 -1
  148. package/packages/datadog-core/src/utils/src/get.js +0 -11
  149. package/packages/datadog-core/src/utils/src/has.js +0 -14
  150. package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +0 -30
@@ -53,5 +53,6 @@ module.exports = {
53
53
  SPAN_POINTER_DIRECTION: Object.freeze({
54
54
  UPSTREAM: 'u',
55
55
  DOWNSTREAM: 'd'
56
- })
56
+ }),
57
+ INSTRUMENTED_BY_SSI: Symbol('_dd.instrumented.by.ssi')
57
58
  }
@@ -9,31 +9,29 @@ const { URL } = require('url')
9
9
  const pkg = require('../../../../package.json')
10
10
 
11
11
  class Crashtracker {
12
- constructor () {
13
- this._started = false
14
- }
12
+ #started = false
15
13
 
16
14
  configure (config) {
17
- if (!this._started) return
15
+ if (!this.#started) return
18
16
 
19
17
  try {
20
- binding.updateConfig(this._getConfig(config))
21
- binding.updateMetadata(this._getMetadata(config))
18
+ binding.updateConfig(this.#getConfig(config))
19
+ binding.updateMetadata(this.#getMetadata(config))
22
20
  } catch (e) {
23
21
  log.error('Error configuring crashtracker', e)
24
22
  }
25
23
  }
26
24
 
27
25
  start (config) {
28
- if (this._started) return this.configure(config)
26
+ if (this.#started) return this.configure(config)
29
27
 
30
- this._started = true
28
+ this.#started = true
31
29
 
32
30
  try {
33
31
  binding.init(
34
- this._getConfig(config),
35
- this._getReceiverConfig(config),
36
- this._getMetadata(config)
32
+ this.#getConfig(config),
33
+ this.#getReceiverConfig(),
34
+ this.#getMetadata(config)
37
35
  )
38
36
  } catch (e) {
39
37
  log.error('Error initialising crashtracker', e)
@@ -50,7 +48,7 @@ class Crashtracker {
50
48
  }
51
49
 
52
50
  // TODO: Send only configured values when defaults are fixed.
53
- _getConfig (config) {
51
+ #getConfig (config) {
54
52
  const { hostname = '127.0.0.1', port = 8126 } = config
55
53
  const url = config.url || new URL(`http://${hostname}:${port}`)
56
54
 
@@ -75,7 +73,7 @@ class Crashtracker {
75
73
  }
76
74
  }
77
75
 
78
- _getMetadata (config) {
76
+ #getMetadata (config) {
79
77
  const tags = Object.keys(config.tags).map(key => `${key}:${config.tags[key]}`)
80
78
 
81
79
  return {
@@ -94,7 +92,7 @@ class Crashtracker {
94
92
  }
95
93
  }
96
94
 
97
- _getReceiverConfig () {
95
+ #getReceiverConfig () {
98
96
  return {
99
97
  args: [],
100
98
  env: [],
@@ -7,7 +7,7 @@ function getDataStreamsContext () {
7
7
  }
8
8
 
9
9
  function setDataStreamsContext (dataStreamsContext) {
10
- log.debug(() => `Setting new DSM Context: ${JSON.stringify(dataStreamsContext)}.`)
10
+ log.debug('Setting new DSM Context: %j.', dataStreamsContext)
11
11
 
12
12
  if (dataStreamsContext) storage('legacy').enterWith({ ...(storage('legacy').getStore()), dataStreamsContext })
13
13
  }
@@ -204,7 +204,7 @@ class DataStreamsProcessor {
204
204
  let closestOppositeDirectionHash = ENTRY_PARENT_HASH
205
205
  let closestOppositeDirectionEdgeStart = nowNs
206
206
  if (ctx == null) {
207
- log.debug(() => 'Setting DSM Checkpoint with empty parent context.')
207
+ log.debug('Setting DSM Checkpoint with empty parent context.')
208
208
  } else {
209
209
  pathwayStartNs = ctx.pathwayStartNs
210
210
  edgeStartNs = ctx.edgeStartNs
@@ -20,7 +20,7 @@ function makeRequest (data, url, cb) {
20
20
  url
21
21
  }
22
22
 
23
- log.debug(() => `Request to the intake: ${JSON.stringify(options)}`)
23
+ log.debug('Request to the intake: %j', options)
24
24
 
25
25
  request(data, options, (err, res) => {
26
26
  cb(err, res)
@@ -39,7 +39,7 @@ class DataStreamsWriter {
39
39
 
40
40
  flush (payload) {
41
41
  if (!request.writable) {
42
- log.debug(() => `Maximum number of active requests reached. Payload discarded: ${JSON.stringify(payload)}`)
42
+ log.debug('Maximum number of active requests reached. Payload discarded: %j', payload)
43
43
  return
44
44
  }
45
45
  const encodedPayload = msgpack.encode(payload)
@@ -50,7 +50,7 @@ class DataStreamsWriter {
50
50
  return
51
51
  }
52
52
  makeRequest(compressedData, this._url, (err, res) => {
53
- log.debug(`Response from the agent: ${res}`)
53
+ log.debug('Response from the agent:', res)
54
54
  if (err) {
55
55
  log.error('Error sending datastream', err)
56
56
  }
@@ -87,7 +87,10 @@ async function addBreakpoint (probe) {
87
87
  try {
88
88
  probe.condition = probe.when?.json && compileCondition(probe.when.json)
89
89
  } catch (err) {
90
- throw new Error(`Cannot compile expression: ${probe.when.dsl}`, { cause: err })
90
+ throw new Error(
91
+ `Cannot compile expression: ${probe.when.dsl} (probe: ${probe.id}, version: ${probe.version})`,
92
+ { cause: err }
93
+ )
91
94
  }
92
95
 
93
96
  const locationKey = generateLocationKey(scriptId, lineNumber, columnNumber)
@@ -115,7 +118,7 @@ async function addBreakpoint (probe) {
115
118
  condition: probe.condition
116
119
  })
117
120
  } catch (err) {
118
- throw new Error(`Error setting breakpoint for probe ${probe.id}`, { cause: err })
121
+ throw new Error(`Error setting breakpoint for probe ${probe.id} (version: ${probe.version})`, { cause: err })
119
122
  }
120
123
  probeToLocation.set(probe.id, locationKey)
121
124
  locationToBreakpoint.set(locationKey, { id: result.breakpointId, location, locationKey })
@@ -192,7 +195,7 @@ async function updateBreakpointInternal (breakpoint, probe) {
192
195
  condition
193
196
  })
194
197
  } catch (err) {
195
- throw new Error(`Error setting breakpoint for probe ${probe.id}`, { cause: err })
198
+ throw new Error(`Error setting breakpoint for probe ${probe.id} (version: ${probe.version})`, { cause: err })
196
199
  }
197
200
  breakpointToProbes.set(result.breakpointId, probesAtLocation)
198
201
  }
@@ -278,7 +278,7 @@ function guardAgainstCoercionSideEffects (variable) {
278
278
 
279
279
  function assertString (variable) {
280
280
  return `((val) => {
281
- if (typeof val === 'string' || val instanceof String) {
281
+ if (${isString('val')}) {
282
282
  return val
283
283
  } else {
284
284
  throw new TypeError('Variable is not a string')
@@ -160,10 +160,9 @@ session.on('Debugger.paused', async ({ params }) => {
160
160
  await session.post('Debugger.resume')
161
161
  const diff = process.hrtime.bigint() - start // TODO: Recored as telemetry (DEBUG-2858)
162
162
 
163
- log.debug(
164
- '[debugger:devtools_client] Finished processing breakpoints - main thread paused for: %d ms',
163
+ log.debug(() => `[debugger:devtools_client] Finished processing breakpoints - main thread paused for: ${
165
164
  Number(diff) / 1_000_000
166
- )
165
+ } ms`)
167
166
 
168
167
  const logger = {
169
168
  // We can safely use `location.file` from the first probe in the array, since all probes hit by `hitBreakpoints`
@@ -32,7 +32,11 @@ const ddtags = [
32
32
 
33
33
  const path = `/debugger/v1/input?${stringify({ ddtags })}`
34
34
 
35
- const jsonBuffer = new JSONBuffer({ size: config.maxTotalPayloadSize, timeout: 1000, onFlush })
35
+ const jsonBuffer = new JSONBuffer({
36
+ size: config.maxTotalPayloadSize,
37
+ timeout: config.dynamicInstrumentation.uploadIntervalSeconds * 1000,
38
+ onFlush
39
+ })
36
40
 
37
41
  function send (message, logger, dd, snapshot) {
38
42
  const payload = {
@@ -165,9 +165,12 @@ session.on('Debugger.scriptParsed', ({ params }) => {
165
165
  loadedScripts.push(params)
166
166
  }
167
167
 
168
- clearTimeout(reEvaluateProbesTimer)
169
- reEvaluateProbesTimer = setTimeout(() => {
170
- session.emit('scriptLoadingStabilized')
171
- }, 500)
168
+ if (reEvaluateProbesTimer === null) {
169
+ reEvaluateProbesTimer = setTimeout(() => {
170
+ session.emit('scriptLoadingStabilized')
171
+ }, 500).unref()
172
+ } else {
173
+ reEvaluateProbesTimer.refresh()
174
+ }
172
175
  }
173
176
  })
@@ -20,7 +20,11 @@ const runtimeId = config.runtimeId
20
20
 
21
21
  const cache = new TTLSet(60 * 60 * 1000) // 1 hour
22
22
 
23
- const jsonBuffer = new JSONBuffer({ size: config.maxTotalPayloadSize, timeout: 1000, onFlush })
23
+ const jsonBuffer = new JSONBuffer({
24
+ size: config.maxTotalPayloadSize,
25
+ timeout: config.dynamicInstrumentation.uploadIntervalSeconds * 1000,
26
+ onFlush
27
+ })
24
28
 
25
29
  const STATUSES = {
26
30
  RECEIVED: 'RECEIVED',
@@ -63,7 +63,7 @@ class DogStatsDClient {
63
63
  flush () {
64
64
  const queue = this._enqueue()
65
65
 
66
- log.debug(`Flushing ${queue.length} metrics via ${this._httpOptions ? 'HTTP' : 'UDP'}`)
66
+ log.debug('Flushing %s metrics via', queue.length, this._httpOptions ? 'HTTP' : 'UDP')
67
67
 
68
68
  if (this._queue.length === 0) return
69
69
 
@@ -108,7 +108,7 @@ class DogStatsDClient {
108
108
  const socket = family === 6 ? this._udp6 : this._udp4
109
109
 
110
110
  queue.forEach((buffer) => {
111
- log.debug(`Sending to DogStatsD: ${buffer}`)
111
+ log.debug('Sending to DogStatsD: %s', buffer)
112
112
  socket.send(buffer, 0, buffer.length, this._port, address)
113
113
  })
114
114
  }
@@ -165,7 +165,7 @@ class DogStatsDClient {
165
165
  .filter(key => {
166
166
  // Skip runtime-id unless enabled as cardinality may be too high
167
167
  if (key !== 'runtime-id') return true
168
- return (config.experimental && config.experimental.runtimeId)
168
+ return config.runtimeMetricsRuntimeId
169
169
  })
170
170
  .forEach(key => {
171
171
  // https://docs.datadoghq.com/tagging/#defining-tags
@@ -5,6 +5,8 @@ const log = require('../../log')
5
5
  const Writer = require('./writer')
6
6
 
7
7
  class AgentExporter {
8
+ #timer
9
+
8
10
  constructor (config, prioritySampler) {
9
11
  this._config = config
10
12
  const { url, hostname, port, lookup, protocolVersion, stats = {}, apmTracingEnabled } = config
@@ -28,8 +30,9 @@ class AgentExporter {
28
30
  config
29
31
  })
30
32
 
31
- this._timer = undefined
32
- process.once('beforeExit', () => this._writer.flush())
33
+ process.once('beforeExit', () => {
34
+ this.flush()
35
+ })
33
36
  }
34
37
 
35
38
  setUrl (url) {
@@ -49,15 +52,17 @@ class AgentExporter {
49
52
 
50
53
  if (flushInterval === 0) {
51
54
  this._writer.flush()
52
- } else if (flushInterval > 0 && !this._timer) {
53
- this._timer = setTimeout(() => {
55
+ } else if (this.#timer === undefined) {
56
+ this.#timer = setTimeout(() => {
54
57
  this._writer.flush()
55
- this._timer = clearTimeout(this._timer)
58
+ this.#timer = undefined
56
59
  }, flushInterval).unref()
57
60
  }
58
61
  }
59
62
 
60
63
  flush (done = () => {}) {
64
+ clearTimeout(this.#timer)
65
+ this.#timer = undefined
61
66
  this._writer.flush(done)
62
67
  }
63
68
  }
@@ -69,7 +69,9 @@ function setHeader (headers, key, value) {
69
69
  }
70
70
 
71
71
  function getEncoder (protocolVersion) {
72
- return require(`../../encode/${protocolVersion === '0.5' ? '0.5' : '0.4'}`).AgentEncoder
72
+ return protocolVersion === '0.5'
73
+ ? require('../../encode/0.5').AgentEncoder
74
+ : require('../../encode/0.4').AgentEncoder
73
75
  }
74
76
 
75
77
  function makeRequest (version, data, count, url, headers, lookup, needsStartupLog, cb) {
@@ -90,7 +92,7 @@ function makeRequest (version, data, count, url, headers, lookup, needsStartupLo
90
92
  setHeader(options.headers, 'Datadog-Meta-Lang-Version', process.version)
91
93
  setHeader(options.headers, 'Datadog-Meta-Lang-Interpreter', process.jsEngine || 'v8')
92
94
 
93
- log.debug(() => `Request to the agent: ${JSON.stringify(options)}`)
95
+ log.debug('Request to the agent: %j', options)
94
96
 
95
97
  request(data, options, (err, res, status) => {
96
98
  if (needsStartupLog) {
@@ -59,10 +59,10 @@ class AgentInfoExporter {
59
59
 
60
60
  if (flushInterval === 0) {
61
61
  writer.flush()
62
- } else if (flushInterval > 0 && !this[timerKey]) {
62
+ } else if (this[timerKey] === undefined) {
63
63
  this[timerKey] = setTimeout(() => {
64
64
  writer.flush()
65
- this[timerKey] = clearTimeout(this[timerKey])
65
+ this[timerKey] = undefined
66
66
  }, flushInterval).unref()
67
67
  }
68
68
  }
@@ -9,7 +9,7 @@ const MAX_SIZE = 64 * 1024 // 64kb
9
9
 
10
10
  class LogExporter {
11
11
  export (spans) {
12
- log.debug(() => `Adding trace to queue: ${JSON.stringify(spans)}`)
12
+ log.debug('Adding trace to queue: %j', spans)
13
13
 
14
14
  let size = TRACE_FORMAT_OVERHEAD
15
15
  let queue = []
@@ -20,7 +20,7 @@ class Writer extends BaseWriter {
20
20
  done()
21
21
  return
22
22
  }
23
- log.debug(`Response from the intake: ${res}`)
23
+ log.debug('Response from the intake:', res)
24
24
  done()
25
25
  })
26
26
  }
@@ -41,7 +41,7 @@ function makeRequest (data, url, cb) {
41
41
  options.hostname = url.hostname
42
42
  options.port = url.port
43
43
 
44
- log.debug(() => `Request to the intake: ${JSON.stringify(options)}`)
44
+ log.debug('Request to the intake: %j', options)
45
45
 
46
46
  request(data, options, (err, res) => {
47
47
  cb(err, res)
@@ -47,13 +47,15 @@ function guard (fn) {
47
47
  { name: 'abort.runtime', tags: [] }
48
48
  ])
49
49
  log.info('Aborting application instrumentation due to incompatible_runtime.')
50
- log.info('Found incompatible runtime nodejs ' + version + ', Supported runtimes: nodejs ' + engines.node + '.')
50
+ log.info('Found incompatible runtime nodejs %s, Supported runtimes: nodejs %s.', version, engines.node)
51
51
  if (forced) {
52
52
  log.info('DD_INJECT_FORCE enabled, allowing unsupported runtimes and continuing.')
53
53
  }
54
54
  }
55
55
 
56
56
  if (!clobberBailout && (!initBailout || forced)) {
57
+ // Ensure the instrumentation source is set for the current process and potential
58
+ // child processes.
57
59
  var result = fn()
58
60
  telemetry('complete', ['injection_forced:' + (forced && initBailout ? 'true' : 'false')])
59
61
  log.info('Application instrumentation bootstrapping complete')
@@ -65,7 +65,7 @@ function sendTelemetry (name, tags) {
65
65
  })
66
66
  proc.on('exit', function (code) {
67
67
  if (code !== 0) {
68
- log.error('Telemetry forwarder exited with code ' + code)
68
+ log.error('Telemetry forwarder exited with code', code)
69
69
  }
70
70
  })
71
71
  proc.stdin.on('error', function () {
@@ -22,6 +22,8 @@ const LLMObsTagger = require('./tagger')
22
22
  const LLMObsSpanWriter = require('./writers/spans')
23
23
  const { setAgentStrategy } = require('./writers/util')
24
24
 
25
+ const util = require('node:util')
26
+
25
27
  /**
26
28
  * Setting writers and processor globally when LLMObs is enabled
27
29
  * We're setting these in this module instead of on the SDK.
@@ -75,6 +77,7 @@ function enable (config) {
75
77
  spanWriter?.setAgentless(useAgentless)
76
78
 
77
79
  telemetry.recordLLMObsEnabled(startTime, config)
80
+ log.debug(`[LLMObs] Enabled LLM Observability with configuration: ${util.inspect(config.llmobs)}`)
78
81
  })
79
82
  }
80
83
 
@@ -90,6 +93,8 @@ function disable () {
90
93
 
91
94
  spanWriter = null
92
95
  evalWriter = null
96
+
97
+ log.debug('[LLMObs] Disabled LLM Observability')
93
98
  }
94
99
 
95
100
  // since LLMObs traces can extend between services and be the same trace,
@@ -116,7 +121,7 @@ function handleFlush () {
116
121
  evalWriter.flush()
117
122
  } catch (e) {
118
123
  err = 'writer_flush_error'
119
- log.warn(`Failed to flush LLMObs spans and evaluation metrics: ${e.message}`)
124
+ log.warn('Failed to flush LLMObs spans and evaluation metrics:', e.message)
120
125
  }
121
126
  telemetry.recordUserFlush(err)
122
127
  }
@@ -129,10 +134,11 @@ function handleEvalMetricAppend (payload) {
129
134
  try {
130
135
  evalWriter.append(payload)
131
136
  } catch (e) {
132
- log.warn(`
133
- Failed to append evaluation metric to LLM Observability writer, likely due to an unserializable property.
134
- Evaluation metrics won't be sent to LLM Observability: ${e.message}
135
- `)
137
+ log.warn(
138
+ // eslint-disable-next-line @stylistic/max-len
139
+ 'Failed to append evaluation metric to LLM Observability writer, likely due to an unserializable property. Evaluation metrics won\'t be sent to LLM Observability:',
140
+ e.message
141
+ )
136
142
  }
137
143
  }
138
144
 
@@ -70,8 +70,8 @@ class LLMObsPlugin extends TracingPlugin {
70
70
  const span = apmStore?.span
71
71
  if (!span) {
72
72
  log.debug(
73
- `Tried to start an LLMObs span for ${this.constructor.name} without an active APM span.
74
- Not starting LLMObs span.`
73
+ 'Tried to start an LLMObs span for %s without an active APM span. Not starting LLMObs span.',
74
+ this.constructor.name
75
75
  )
76
76
  return
77
77
  }
@@ -63,7 +63,7 @@ class BaseLangChainLLMObsPlugin extends LLMObsPlugin {
63
63
  const type = ctx.type = this.constructor.lcType // langchain operation type (oneof chain,chat_model,llm,embedding)
64
64
 
65
65
  if (!Object.keys(this._handlers).includes(type)) {
66
- log.warn(`Unsupported LangChain operation type: ${type}`)
66
+ log.warn('Unsupported LangChain operation type:', type)
67
67
  return
68
68
  }
69
69
 
@@ -61,19 +61,6 @@ class LLMObsTagger {
61
61
  if (!this._config.llmobs.enabled) return
62
62
  if (!kind) return // do not register it in the map if it doesn't have an llmobs span kind
63
63
 
64
- this._register(span)
65
-
66
- if (name) this._setTag(span, NAME, name)
67
-
68
- this._setTag(span, SPAN_KIND, kind)
69
- if (modelName) this._setTag(span, MODEL_NAME, modelName)
70
- if (modelProvider) this._setTag(span, MODEL_PROVIDER, modelProvider)
71
-
72
- sessionId = sessionId || registry.get(parent)?.[SESSION_ID]
73
- if (sessionId) this._setTag(span, SESSION_ID, sessionId)
74
- if (integration) this._setTag(span, INTEGRATION, integration)
75
- if (_decorator) this._setTag(span, DECORATOR, _decorator)
76
-
77
64
  const spanMlApp =
78
65
  mlApp ||
79
66
  registry.get(parent)?.[ML_APP] ||
@@ -87,8 +74,21 @@ class LLMObsTagger {
87
74
  )
88
75
  }
89
76
 
77
+ this._register(span)
78
+
90
79
  this._setTag(span, ML_APP, spanMlApp)
91
80
 
81
+ if (name) this._setTag(span, NAME, name)
82
+
83
+ this._setTag(span, SPAN_KIND, kind)
84
+ if (modelName) this._setTag(span, MODEL_NAME, modelName)
85
+ if (modelProvider) this._setTag(span, MODEL_PROVIDER, modelProvider)
86
+
87
+ sessionId = sessionId || registry.get(parent)?.[SESSION_ID]
88
+ if (sessionId) this._setTag(span, SESSION_ID, sessionId)
89
+ if (integration) this._setTag(span, INTEGRATION, integration)
90
+ if (_decorator) this._setTag(span, DECORATOR, _decorator)
91
+
92
92
  const parentId =
93
93
  parent?.context().toSpanId() ??
94
94
  span.context()._trace.tags[PROPAGATED_PARENT_ID_KEY] ??
@@ -60,7 +60,7 @@ class BaseLLMObsWriter {
60
60
  return
61
61
  }
62
62
 
63
- this._bufferSize += byteLength || Buffer.from(JSON.stringify(event)).byteLength
63
+ this._bufferSize += byteLength || Buffer.byteLength(JSON.stringify(event))
64
64
  this._buffer.push(event)
65
65
  }
66
66
 
@@ -76,7 +76,7 @@ class BaseLLMObsWriter {
76
76
  this._bufferSize = 0
77
77
  const payload = this._encode(this.makePayload(events))
78
78
 
79
- log.debug(`Encoded LLMObs payload: ${payload}`)
79
+ log.debug('Encoded LLMObs payload: %s', payload)
80
80
 
81
81
  const options = this._getOptions()
82
82
 
@@ -26,7 +26,7 @@ class LLMObsSpanWriter extends BaseWriter {
26
26
  }
27
27
 
28
28
  append (event) {
29
- const eventSizeBytes = Buffer.from(JSON.stringify(event)).byteLength
29
+ const eventSizeBytes = Buffer.byteLength(JSON.stringify(event))
30
30
  telemetry.recordLLMObsRawSpanSize(event, eventSizeBytes)
31
31
 
32
32
  const shouldTruncate = eventSizeBytes > EVP_EVENT_SIZE_LIMIT
@@ -35,7 +35,7 @@ class LLMObsSpanWriter extends BaseWriter {
35
35
  if (shouldTruncate) {
36
36
  logger.warn(`Dropping event input/output because its size (${eventSizeBytes}) exceeds the 1MB event size limit`)
37
37
  event = this._truncateSpanEvent(event)
38
- processedEventSizeBytes = Buffer.from(JSON.stringify(event)).byteLength
38
+ processedEventSizeBytes = Buffer.byteLength(JSON.stringify(event))
39
39
  }
40
40
 
41
41
  telemetry.recordLLMObsSpanSize(event, processedEventSizeBytes, shouldTruncate)
@@ -77,7 +77,7 @@ class Tracer {
77
77
  origin
78
78
  )
79
79
  } else {
80
- log.debug(`no dd list member in tracestate from incoming request: ${ts}`)
80
+ log.debug('no dd list member in tracestate from incoming request:', ts)
81
81
  }
82
82
  }
83
83
 
@@ -136,12 +136,12 @@ class TextMapPropagator {
136
136
  let itemCounter = 0
137
137
  let byteCounter = 0
138
138
 
139
- const baggageItems = spanContext ? spanContext._baggageItems : getAllBaggageItems()
139
+ const baggageItems = getAllBaggageItems()
140
140
  if (!baggageItems) return
141
141
  for (const [key, value] of Object.entries(baggageItems)) {
142
142
  const item = `${this._encodeOtelBaggageKey(String(key).trim())}=${encodeURIComponent(String(value).trim())},`
143
143
  itemCounter += 1
144
- byteCounter += item.length
144
+ byteCounter += Buffer.byteLength(item)
145
145
  if (itemCounter > this._config.baggageMaxItems || byteCounter > this._config.baggageMaxBytes) break
146
146
  baggage += item
147
147
  }
@@ -325,7 +325,7 @@ class TextMapPropagator {
325
325
  extractedContext = this._extractB3MultiContext(carrier)
326
326
  break
327
327
  default:
328
- if (extractor !== 'baggage') log.warn(`Unknown propagation style: ${extractor}`)
328
+ if (extractor !== 'baggage') log.warn('Unknown propagation style:', extractor)
329
329
  }
330
330
 
331
331
  if (extractedContext === null) { // If the current extractor was invalid, continue to the next extractor
@@ -355,19 +355,20 @@ class TextMapPropagator {
355
355
  }
356
356
  }
357
357
 
358
- this._extractBaggageItems(carrier, context)
359
-
360
358
  if (this._config.tracePropagationBehaviorExtract === 'ignore') {
361
359
  context._links = []
362
- } else if (this._config.tracePropagationBehaviorExtract === 'restart') {
363
- context._links = []
364
- context._links.push({
365
- context,
366
- attributes:
367
- {
368
- reason: 'propagation_behavior_extract', context_headers: style
369
- }
370
- })
360
+ } else {
361
+ if (this._config.tracePropagationBehaviorExtract === 'restart') {
362
+ context._links = []
363
+ context._links.push({
364
+ context,
365
+ attributes:
366
+ {
367
+ reason: 'propagation_behavior_extract', context_headers: style
368
+ }
369
+ })
370
+ }
371
+ this._extractBaggageItems(carrier, context)
371
372
  }
372
373
 
373
374
  return context || this._extractSqsdContext(carrier)
@@ -513,7 +514,7 @@ class TextMapPropagator {
513
514
  // If subkey is tid then do nothing because trace header tid should always be preserved
514
515
  if (subKey === 'tid') {
515
516
  if (!hex16.test(value) || spanContext._trace.tags['_dd.p.tid'] !== transformedValue) {
516
- log.error(`Invalid trace id ${value} in tracestate, skipping`)
517
+ log.error('Invalid trace id %s in tracestate, skipping', value)
517
518
  }
518
519
  continue
519
520
  }
@@ -628,33 +629,26 @@ class TextMapPropagator {
628
629
  _extractBaggageItems (carrier, spanContext) {
629
630
  if (!this._hasPropagationStyle('extract', 'baggage')) return
630
631
  if (!carrier || !carrier.baggage) return
631
- if (!spanContext) removeAllBaggageItems()
632
632
  const baggages = carrier.baggage.split(',')
633
633
  const keysToSpanTag = this._config.baggageTagKeys === '*'
634
634
  ? undefined
635
635
  : new Set(this._config.baggageTagKeys.split(','))
636
636
  for (const keyValue of baggages) {
637
637
  if (!keyValue.includes('=')) {
638
- if (spanContext) spanContext._baggageItems = {}
638
+ removeAllBaggageItems()
639
639
  return
640
640
  }
641
641
  let [key, value] = keyValue.split('=')
642
642
  key = this._decodeOtelBaggageKey(key.trim())
643
643
  value = decodeURIComponent(value.trim())
644
644
  if (!key || !value) {
645
- if (spanContext) spanContext._baggageItems = {}
645
+ removeAllBaggageItems()
646
646
  return
647
647
  }
648
- // the current code assumes precedence of ot-baggage- (legacy opentracing baggage) over baggage
649
- if (spanContext) {
650
- if (Object.hasOwn(spanContext._baggageItems, key)) continue
651
- spanContext._baggageItems[key] = value
652
- if (this._config.baggageTagKeys === '*' || keysToSpanTag.has(key)) {
653
- spanContext._trace.tags['baggage.' + key] = value
654
- }
655
- } else {
656
- setBaggageItem(key, value)
648
+ if (spanContext && (this._config.baggageTagKeys === '*' || keysToSpanTag.has(key))) {
649
+ spanContext._trace.tags['baggage.' + key] = value
657
650
  }
651
+ setBaggageItem(key, value)
658
652
  }
659
653
  }
660
654
 
@@ -689,7 +683,7 @@ class TextMapPropagator {
689
683
  }
690
684
  // Check if value is a valid 16 character lower-case hexadecimal encoded number as per spec
691
685
  if (key === '_dd.p.tid' && !(hex16.test(value))) {
692
- log.error(`Invalid _dd.p.tid tag ${value}, skipping`)
686
+ log.error('Invalid _dd.p.tid tag %s, skipping', value)
693
687
  continue
694
688
  }
695
689
  tags[key] = value