dd-trace 5.103.0 → 5.105.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 (213) hide show
  1. package/LICENSE-3rdparty.csv +90 -102
  2. package/index.d.ts +107 -6
  3. package/package.json +18 -17
  4. package/packages/datadog-core/src/storage.js +1 -1
  5. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  6. package/packages/datadog-instrumentations/src/ai.js +8 -7
  7. package/packages/datadog-instrumentations/src/aws-sdk.js +15 -2
  8. package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  10. package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
  11. package/packages/datadog-instrumentations/src/cucumber.js +181 -35
  12. package/packages/datadog-instrumentations/src/dns.js +54 -18
  13. package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
  14. package/packages/datadog-instrumentations/src/fastify.js +142 -82
  15. package/packages/datadog-instrumentations/src/graphql.js +188 -67
  16. package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
  17. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
  18. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  20. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
  21. package/packages/datadog-instrumentations/src/helpers/kafka.js +17 -0
  22. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
  23. package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
  24. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -6
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +31 -229
  32. package/packages/datadog-instrumentations/src/hono.js +54 -3
  33. package/packages/datadog-instrumentations/src/http/client.js +2 -2
  34. package/packages/datadog-instrumentations/src/http/server.js +9 -4
  35. package/packages/datadog-instrumentations/src/ioredis.js +3 -3
  36. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
  37. package/packages/datadog-instrumentations/src/jest.js +390 -183
  38. package/packages/datadog-instrumentations/src/kafkajs.js +140 -17
  39. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  40. package/packages/datadog-instrumentations/src/memcached.js +2 -1
  41. package/packages/datadog-instrumentations/src/mocha/main.js +399 -107
  42. package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
  43. package/packages/datadog-instrumentations/src/mongodb-core.js +1 -1
  44. package/packages/datadog-instrumentations/src/mongoose.js +10 -12
  45. package/packages/datadog-instrumentations/src/mysql.js +2 -2
  46. package/packages/datadog-instrumentations/src/mysql2.js +1 -1
  47. package/packages/datadog-instrumentations/src/nats.js +182 -0
  48. package/packages/datadog-instrumentations/src/nyc.js +38 -1
  49. package/packages/datadog-instrumentations/src/openai.js +33 -18
  50. package/packages/datadog-instrumentations/src/oracledb.js +6 -1
  51. package/packages/datadog-instrumentations/src/pg.js +1 -1
  52. package/packages/datadog-instrumentations/src/pino.js +17 -5
  53. package/packages/datadog-instrumentations/src/playwright.js +537 -297
  54. package/packages/datadog-instrumentations/src/router.js +80 -34
  55. package/packages/datadog-instrumentations/src/stripe.js +1 -1
  56. package/packages/datadog-instrumentations/src/vitest.js +246 -149
  57. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
  58. package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
  59. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
  60. package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
  61. package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
  62. package/packages/datadog-plugin-bunyan/src/index.js +28 -0
  63. package/packages/datadog-plugin-cucumber/src/index.js +17 -3
  64. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +223 -45
  65. package/packages/datadog-plugin-cypress/src/support.js +69 -1
  66. package/packages/datadog-plugin-dns/src/lookup.js +8 -6
  67. package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
  68. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
  69. package/packages/datadog-plugin-graphql/src/execute.js +2 -0
  70. package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
  71. package/packages/datadog-plugin-graphql/src/utils.js +4 -1
  72. package/packages/datadog-plugin-http/src/server.js +40 -15
  73. package/packages/datadog-plugin-jest/src/index.js +11 -3
  74. package/packages/datadog-plugin-jest/src/util.js +15 -8
  75. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
  76. package/packages/datadog-plugin-kafkajs/src/producer.js +35 -0
  77. package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
  78. package/packages/datadog-plugin-mocha/src/index.js +19 -4
  79. package/packages/datadog-plugin-mongodb-core/src/index.js +311 -35
  80. package/packages/datadog-plugin-nats/src/consumer.js +43 -0
  81. package/packages/datadog-plugin-nats/src/index.js +20 -0
  82. package/packages/datadog-plugin-nats/src/producer.js +62 -0
  83. package/packages/datadog-plugin-nats/src/util.js +33 -0
  84. package/packages/datadog-plugin-next/src/index.js +5 -3
  85. package/packages/datadog-plugin-openai/src/tracing.js +15 -2
  86. package/packages/datadog-plugin-oracledb/src/index.js +13 -2
  87. package/packages/datadog-plugin-pino/src/index.js +42 -0
  88. package/packages/datadog-plugin-playwright/src/index.js +4 -4
  89. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  90. package/packages/datadog-plugin-redis/src/index.js +37 -2
  91. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  92. package/packages/datadog-plugin-router/src/index.js +33 -44
  93. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  94. package/packages/datadog-plugin-undici/src/index.js +19 -0
  95. package/packages/datadog-plugin-vitest/src/index.js +24 -20
  96. package/packages/datadog-plugin-winston/src/index.js +30 -0
  97. package/packages/datadog-shimmer/src/shimmer.js +49 -21
  98. package/packages/dd-trace/src/aiguard/index.js +1 -1
  99. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  100. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  101. package/packages/dd-trace/src/appsec/blocking.js +2 -2
  102. package/packages/dd-trace/src/appsec/index.js +11 -4
  103. package/packages/dd-trace/src/appsec/reporter.js +24 -11
  104. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  105. package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
  106. package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
  107. package/packages/dd-trace/src/baggage.js +7 -1
  108. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
  109. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
  110. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
  111. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
  112. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
  113. package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -2
  114. package/packages/dd-trace/src/config/supported-configurations.json +36 -8
  115. package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
  116. package/packages/dd-trace/src/datastreams/context.js +4 -2
  117. package/packages/dd-trace/src/datastreams/writer.js +2 -4
  118. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
  119. package/packages/dd-trace/src/encode/0.4.js +124 -108
  120. package/packages/dd-trace/src/encode/0.5.js +114 -26
  121. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +57 -42
  122. package/packages/dd-trace/src/encode/agentless-json.js +4 -2
  123. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
  124. package/packages/dd-trace/src/encode/span-stats.js +16 -16
  125. package/packages/dd-trace/src/encode/tags-processors.js +16 -0
  126. package/packages/dd-trace/src/exporters/common/agents.js +3 -1
  127. package/packages/dd-trace/src/exporters/common/request.js +3 -1
  128. package/packages/dd-trace/src/id.js +17 -4
  129. package/packages/dd-trace/src/lambda/handler.js +2 -4
  130. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  131. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  132. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  133. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
  134. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  135. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
  136. package/packages/dd-trace/src/llmobs/sdk.js +10 -16
  137. package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
  138. package/packages/dd-trace/src/llmobs/tagger.js +9 -1
  139. package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
  140. package/packages/dd-trace/src/llmobs/util.js +66 -3
  141. package/packages/dd-trace/src/log/index.js +1 -1
  142. package/packages/dd-trace/src/log/writer.js +3 -1
  143. package/packages/dd-trace/src/msgpack/chunk.js +394 -10
  144. package/packages/dd-trace/src/msgpack/index.js +96 -2
  145. package/packages/dd-trace/src/noop/span.js +3 -1
  146. package/packages/dd-trace/src/openfeature/encoding.js +70 -0
  147. package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
  148. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
  149. package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
  150. package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
  151. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  152. package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
  153. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  154. package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
  155. package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
  156. package/packages/dd-trace/src/opentracing/span.js +59 -19
  157. package/packages/dd-trace/src/opentracing/span_context.js +49 -0
  158. package/packages/dd-trace/src/plugins/apollo.js +3 -1
  159. package/packages/dd-trace/src/plugins/ci_plugin.js +23 -33
  160. package/packages/dd-trace/src/plugins/database.js +7 -6
  161. package/packages/dd-trace/src/plugins/index.js +4 -0
  162. package/packages/dd-trace/src/plugins/log_injection.js +56 -0
  163. package/packages/dd-trace/src/plugins/log_plugin.js +3 -46
  164. package/packages/dd-trace/src/plugins/outbound.js +1 -1
  165. package/packages/dd-trace/src/plugins/plugin.js +15 -17
  166. package/packages/dd-trace/src/plugins/tracing.js +48 -8
  167. package/packages/dd-trace/src/plugins/util/git.js +3 -1
  168. package/packages/dd-trace/src/plugins/util/test.js +318 -13
  169. package/packages/dd-trace/src/plugins/util/web.js +89 -64
  170. package/packages/dd-trace/src/priority_sampler.js +2 -2
  171. package/packages/dd-trace/src/profiling/profiler.js +2 -2
  172. package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
  173. package/packages/dd-trace/src/sampling_rule.js +7 -7
  174. package/packages/dd-trace/src/scope.js +7 -5
  175. package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
  176. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
  177. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  178. package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
  179. package/packages/dd-trace/src/span_format.js +190 -58
  180. package/packages/dd-trace/src/spanleak.js +1 -1
  181. package/packages/dd-trace/src/standalone/index.js +3 -3
  182. package/packages/dd-trace/src/tagger.js +0 -2
  183. package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
  184. package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
  185. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  186. package/vendor/dist/protobufjs/index.js +1 -1
  187. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  188. package/packages/dd-trace/src/msgpack/encoder.js +0 -308
  189. package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
  190. package/vendor/dist/opentracing/LICENSE +0 -201
  191. package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
  192. package/vendor/dist/opentracing/constants.d.ts +0 -61
  193. package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
  194. package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
  195. package/vendor/dist/opentracing/functions.d.ts +0 -20
  196. package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
  197. package/vendor/dist/opentracing/index.d.ts +0 -12
  198. package/vendor/dist/opentracing/index.js +0 -1
  199. package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
  200. package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
  201. package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
  202. package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
  203. package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
  204. package/vendor/dist/opentracing/noop.d.ts +0 -8
  205. package/vendor/dist/opentracing/reference.d.ts +0 -33
  206. package/vendor/dist/opentracing/span.d.ts +0 -147
  207. package/vendor/dist/opentracing/span_context.d.ts +0 -26
  208. package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
  209. package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
  210. package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
  211. package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
  212. package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
  213. package/vendor/dist/opentracing/tracer.d.ts +0 -127
@@ -44,7 +44,8 @@ class BaseLangChainLLMObsPlugin extends LLMObsPlugin {
44
44
 
45
45
  getLLMObsSpanRegisterOptions (ctx) {
46
46
  const span = ctx.currentStore?.span
47
- const tags = span?.context()._tags || {}
47
+ const spanContext = span?.context()
48
+ const tags = spanContext?.getTags() || {}
48
49
 
49
50
  const modelProvider = tags['langchain.request.provider'] // could be undefined
50
51
  const modelName = tags['langchain.request.model'] // could be undefined
@@ -76,7 +77,7 @@ class BaseLangChainLLMObsPlugin extends LLMObsPlugin {
76
77
  return
77
78
  }
78
79
 
79
- const provider = span?.context()._tags['langchain.request.provider']
80
+ const provider = span?.context()?.getTag('langchain.request.provider')
80
81
  const integrationName = this.getIntegrationName(type, provider)
81
82
  this.setMetadata(span, provider)
82
83
 
@@ -93,14 +94,15 @@ class BaseLangChainLLMObsPlugin extends LLMObsPlugin {
93
94
  const metadata = {}
94
95
 
95
96
  // these fields won't be set for non model-based operations
97
+ const spanContext = span?.context()
96
98
  const temperature =
97
- span?.context()._tags[`langchain.request.${provider}.parameters.temperature`] ||
98
- span?.context()._tags[`langchain.request.${provider}.parameters.model_kwargs.temperature`]
99
+ spanContext?.getTag(`langchain.request.${provider}.parameters.temperature`) ||
100
+ spanContext?.getTag(`langchain.request.${provider}.parameters.model_kwargs.temperature`)
99
101
 
100
102
  const maxTokens =
101
- span?.context()._tags[`langchain.request.${provider}.parameters.max_tokens`] ||
102
- span?.context()._tags[`langchain.request.${provider}.parameters.maxTokens`] ||
103
- span?.context()._tags[`langchain.request.${provider}.parameters.model_kwargs.max_tokens`]
103
+ spanContext?.getTag(`langchain.request.${provider}.parameters.max_tokens`) ||
104
+ spanContext?.getTag(`langchain.request.${provider}.parameters.maxTokens`) ||
105
+ spanContext?.getTag(`langchain.request.${provider}.parameters.model_kwargs.max_tokens`)
104
106
 
105
107
  if (temperature) {
106
108
  metadata.temperature = Number.parseFloat(temperature)
@@ -35,7 +35,7 @@ class PregelStreamLLMObsPlugin extends LLMObsPlugin {
35
35
 
36
36
  class NextStreamLLMObsPlugin extends LLMObsPlugin {
37
37
  static id = 'llmobs_langgraph_next_stream'
38
- static prefix = 'tracing:orchestrion:@langchain/langgraph:Pregel_stream_next'
38
+ static prefix = 'tracing:orchestrion:@langchain/langgraph:Pregel_stream:next'
39
39
 
40
40
  start () {} // no-op: span was already registered by PregelStreamLLMObsPlugin
41
41
 
@@ -63,7 +63,7 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
63
63
 
64
64
  const inputs = ctx.args[0] // completion, chat completion, and embeddings take one argument
65
65
  const response = ctx.result?.data // no result if error
66
- const error = !!span.context()._tags.error
66
+ const error = !!span.context().getTag('error')
67
67
 
68
68
  const operation = getOperation(methodName)
69
69
 
@@ -11,8 +11,6 @@ const {
11
11
  SPAN_KIND,
12
12
  OUTPUT_VALUE,
13
13
  INPUT_VALUE,
14
- LLMOBS_TRACE_ID_BRIDGE_KEY,
15
- LLMOBS_PARENT_ID_BRIDGE_KEY,
16
14
  } = require('./constants/tags')
17
15
  const {
18
16
  getFunctionArguments,
@@ -55,6 +53,11 @@ class LLMObs extends NoopLLMObs {
55
53
  }
56
54
 
57
55
  enable (options = {}) {
56
+ logger.warn(
57
+ 'Enabling LLM Observability via `llmobs.enable()` is deprecated and will be removed in dd-trace@7.0.0. ' +
58
+ 'Please instantiate LLM Observability via DD_LLMOBS_ENABLED or `tracer.init({ llmobs: ...options })`.'
59
+ )
60
+
58
61
  if (this.enabled) {
59
62
  logger.debug('LLMObs is already enabled.')
60
63
  return
@@ -79,6 +82,11 @@ class LLMObs extends NoopLLMObs {
79
82
  }
80
83
 
81
84
  disable () {
85
+ logger.warn(
86
+ 'Disabling LLM Observability via `llmobs.disable()` is deprecated and will be removed in dd-trace@7.0.0. ' +
87
+ 'Set DD_LLMOBS_ENABLED=false to disable LLM Observability.'
88
+ )
89
+
82
90
  if (!this.enabled) {
83
91
  logger.debug('LLMObs is already disabled.')
84
92
  return
@@ -543,20 +551,6 @@ class LLMObs extends NoopLLMObs {
543
551
  ...options,
544
552
  parent: parentStore?.span,
545
553
  })
546
-
547
- // Bridge tags read by the dd-go LLMObs trace-indexer to correlate OTel
548
- // gen_ai.* spans with SDK LLMObs spans. Written once per local trace,
549
- // on the first successful SDK LLMObs span registration. The shared
550
- // _trace.tags bag is serialized to the first span in every flushed
551
- // chunk's meta, so partial flush is covered automatically without a
552
- // separate flush-time processor. Writing only after registerLLMObsSpan
553
- // succeeds avoids poisoning _trace.tags with bridge tags pointing at a
554
- // span that will never produce an LLMObs event.
555
- const traceTags = span?.context?.()._trace?.tags
556
- if (this.enabled && traceTags && !traceTags[LLMOBS_TRACE_ID_BRIDGE_KEY]) {
557
- traceTags[LLMOBS_TRACE_ID_BRIDGE_KEY] = span.context().toTraceId(true)
558
- traceTags[LLMOBS_PARENT_ID_BRIDGE_KEY] = span.context().toSpanId()
559
- }
560
554
  }
561
555
 
562
556
  try {
@@ -107,7 +107,7 @@ class LLMObsSpanProcessor {
107
107
  // those cases avoids dd-go reparenting OTel children under a span that
108
108
  // has no corresponding LLMObs event.
109
109
  if (enqueued) {
110
- span.context()._tags[LLMOBS_SUBMITTED_TAG_KEY] = '1'
110
+ span.context().setTag(LLMOBS_SUBMITTED_TAG_KEY, '1')
111
111
  }
112
112
  } catch (e) {
113
113
  // this should be a rare case
@@ -123,7 +123,7 @@ class LLMObsSpanProcessor {
123
123
  format (span) {
124
124
  let inputType, outputType
125
125
 
126
- const spanTags = span.context()._tags
126
+ const spanTags = span.context().getTags()
127
127
  const mlObsTags = LLMObsTagger.tagMap.get(span)
128
128
 
129
129
  const spanKind = mlObsTags[SPAN_KIND]
@@ -318,7 +318,7 @@ class LLMObsSpanProcessor {
318
318
  language: 'javascript',
319
319
  }
320
320
 
321
- const errType = span.context()._tags[ERROR_TYPE] || error?.name
321
+ const errType = span.context().getTag(ERROR_TYPE) || error?.name
322
322
  if (errType) tags.error_type = errType
323
323
 
324
324
  if (sessionId) tags.session_id = sessionId
@@ -43,7 +43,7 @@ const {
43
43
  INSTRUMENTATION_METHOD_ANNOTATED,
44
44
  } = require('./constants/tags')
45
45
  const { storage } = require('./storage')
46
- const { validateCostTags } = require('./util')
46
+ const { findGenAIAncestorSpanId, validateCostTags, writeBridgeTags } = require('./util')
47
47
 
48
48
  // global registry of LLMObs spans
49
49
  // maps LLMObs spans to their annotations
@@ -97,6 +97,13 @@ class LLMObsTagger {
97
97
 
98
98
  this._register(span)
99
99
 
100
+ // When the registering span sits below an OTel `gen_ai.*` ancestor, use
101
+ // that ancestor as the parent_id fallback and suppress the bridge
102
+ // parent_id tag so the indexer doesn't invert the trace.
103
+ const genAIAncestorSpanId = findGenAIAncestorSpanId(span)
104
+
105
+ writeBridgeTags(span, { includeParentId: genAIAncestorSpanId === null })
106
+
100
107
  this._setTag(span, ML_APP, spanMlApp)
101
108
 
102
109
  if (name) this._setTag(span, NAME, name)
@@ -113,6 +120,7 @@ class LLMObsTagger {
113
120
  const parentId =
114
121
  parent?.context().toSpanId() ??
115
122
  span.context()._trace.tags[PROPAGATED_PARENT_ID_KEY] ??
123
+ genAIAncestorSpanId ??
116
124
  ROOT_PARENT_ID
117
125
  this._setTag(span, PARENT_ID_KEY, parentId)
118
126
 
@@ -45,7 +45,7 @@ function incrementLLMObsSpanStartCount (tags, value = 1) {
45
45
 
46
46
  function incrementLLMObsSpanFinishedCount (span, value = 1) {
47
47
  const mlObsTags = LLMObsTagger.tagMap.get(span)
48
- const spanTags = span.context()._tags
48
+ const spanTags = span.context().getTags()
49
49
 
50
50
  const isRootSpan = mlObsTags[PARENT_ID_KEY] === ROOT_PARENT_ID
51
51
  const hasSessionId = mlObsTags[SESSION_ID] != null
@@ -1,7 +1,11 @@
1
1
  'use strict'
2
2
 
3
3
  const log = require('../log')
4
- const { SPAN_KINDS } = require('./constants/tags')
4
+ const {
5
+ LLMOBS_PARENT_ID_BRIDGE_KEY,
6
+ LLMOBS_TRACE_ID_BRIDGE_KEY,
7
+ SPAN_KINDS,
8
+ } = require('./constants/tags')
5
9
 
6
10
  // LLM I/O is overwhelmingly ASCII (English prompts and code). Walk once
7
11
  // looking for the first non-ASCII char; if there is none, hand the input
@@ -233,8 +237,8 @@ function getFunctionArguments (fn, args = []) {
233
237
  }
234
238
 
235
239
  function spanHasError (span) {
236
- const tags = span.context()._tags
237
- return !!(tags.error || tags['error.type'])
240
+ const spanContext = span.context()
241
+ return !!(spanContext.getTag('error') || spanContext.getTag('error.type'))
238
242
  }
239
243
 
240
244
  // LLM SDKs stream tool-call argument JSON across SSE chunks; a malformed
@@ -248,11 +252,70 @@ function safeJsonParse (value, fallback) {
248
252
  }
249
253
  }
250
254
 
255
+ // Bridge tags read by the trace-indexer to pull OTel `gen_ai.*` spans into
256
+ // the same LLMObs trace. Written once per local trace (first-writer wins on
257
+ // `_trace.tags`). Pass `includeParentId: false` when the span sits below an
258
+ // OTel `gen_ai.*` ancestor — without it the indexer treats this span as the
259
+ // LLMObs root and hoists the gen_ai ancestors under it, inverting the trace.
260
+ /**
261
+ * @param {import('../opentracing/span')} span
262
+ * @param {{ includeParentId?: boolean }} [opts]
263
+ */
264
+ function writeBridgeTags (span, { includeParentId = true } = {}) {
265
+ const traceTags = span?.context?.()._trace?.tags
266
+ if (!traceTags || traceTags[LLMOBS_TRACE_ID_BRIDGE_KEY]) return
267
+ traceTags[LLMOBS_TRACE_ID_BRIDGE_KEY] = span.context().toTraceId(true)
268
+ if (includeParentId) {
269
+ traceTags[LLMOBS_PARENT_ID_BRIDGE_KEY] = span.context().toSpanId()
270
+ }
271
+ }
272
+
273
+ // Walks the APM parent chain for the nearest ancestor with any `gen_ai.*`
274
+ // tag. Lets an auto-instrumented LLMObs span nested under a manual OTel
275
+ // workflow point its `parent_id` at the OTel parent so the SDK-emitted
276
+ // event renders under it instead of as a parallel root.
277
+ /**
278
+ * @param {import('../opentracing/span')} span
279
+ * @returns {string | null}
280
+ */
281
+ function findGenAIAncestorSpanId (span) {
282
+ const ctx = span?.context?.()
283
+ let parentId = ctx?._parentId?.toString(10)
284
+ if (!parentId || parentId === '0') return null
285
+
286
+ const started = ctx._trace?.started
287
+ if (!started || started.length === 0) return null
288
+
289
+ // Linear scan per hop — parent chains are short, avoids a per-call Map.
290
+ while (parentId && parentId !== '0') {
291
+ let parent = null
292
+ for (const s of started) {
293
+ if (s.context()._spanId.toString(10) === parentId) {
294
+ parent = s
295
+ break
296
+ }
297
+ }
298
+ if (!parent) return null
299
+
300
+ const tags = parent.context().getTags()
301
+ if (tags) {
302
+ for (const key of Object.keys(tags)) {
303
+ if (key.startsWith('gen_ai.')) return parentId
304
+ }
305
+ }
306
+
307
+ parentId = parent.context()._parentId?.toString(10)
308
+ }
309
+ return null
310
+ }
311
+
251
312
  module.exports = {
252
313
  encodeUnicode,
314
+ findGenAIAncestorSpanId,
253
315
  validateCostTags,
254
316
  validateKind,
255
317
  getFunctionArguments,
256
318
  safeJsonParse,
257
319
  spanHasError,
320
+ writeBridgeTags,
258
321
  }
@@ -107,7 +107,7 @@ function publishFormatted (ch, formatter, ...args) {
107
107
 
108
108
  function getErrorLog (err) {
109
109
  if (typeof err?.delegate === 'function') {
110
- const result = err.delegate()
110
+ const result = err.delegate(...err.args)
111
111
  return Array.isArray(result) ? Log.parse(...result) : Log.parse(result)
112
112
  }
113
113
  return err
@@ -3,6 +3,8 @@
3
3
  const { storage } = require('../../../datadog-core')
4
4
  const { LogChannel } = require('./channels')
5
5
 
6
+ const legacyStorage = storage('legacy')
7
+
6
8
  const defaultLogger = {
7
9
  debug: msg => console.debug(msg), /* eslint-disable-line no-console */
8
10
  info: msg => console.info(msg), /* eslint-disable-line no-console */
@@ -15,7 +17,7 @@ let logger = defaultLogger
15
17
  let logChannel = new LogChannel()
16
18
 
17
19
  function withNoop (fn) {
18
- storage('legacy').run({ noop: true }, fn)
20
+ legacyStorage.run({ noop: true }, fn)
19
21
  }
20
22
 
21
23
  function toggleSubscription (enable, level) {