dd-trace 5.79.0 → 5.81.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 (242) hide show
  1. package/LICENSE-3rdparty.csv +79 -87
  2. package/ext/tags.d.ts +1 -0
  3. package/ext/tags.js +1 -0
  4. package/index.d.ts +46 -39
  5. package/initialize.mjs +10 -10
  6. package/loader-hook.mjs +10 -3
  7. package/package.json +23 -40
  8. package/packages/datadog-core/src/storage.js +4 -4
  9. package/packages/datadog-esbuild/index.js +36 -19
  10. package/packages/datadog-esbuild/src/utils.js +5 -1
  11. package/packages/datadog-instrumentations/index.js +1 -0
  12. package/packages/datadog-instrumentations/src/anthropic.js +12 -0
  13. package/packages/datadog-instrumentations/src/aws-sdk.js +13 -2
  14. package/packages/datadog-instrumentations/src/azure-service-bus.js +43 -36
  15. package/packages/datadog-instrumentations/src/cucumber.js +2 -2
  16. package/packages/datadog-instrumentations/src/find-my-way.js +6 -5
  17. package/packages/datadog-instrumentations/src/google-genai.js +120 -0
  18. package/packages/datadog-instrumentations/src/graphql.js +20 -0
  19. package/packages/datadog-instrumentations/src/helpers/hook.js +1 -0
  20. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  21. package/packages/datadog-instrumentations/src/helpers/instrument.js +12 -1
  22. package/packages/datadog-instrumentations/src/helpers/register.js +6 -1
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +27 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +152 -0
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +5 -0
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langchain.js +237 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.js +9 -0
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +11 -0
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +139 -0
  30. package/packages/datadog-instrumentations/src/jest.js +1 -1
  31. package/packages/datadog-instrumentations/src/langchain.js +3 -109
  32. package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
  33. package/packages/datadog-instrumentations/src/mysql2.js +1 -1
  34. package/packages/datadog-instrumentations/src/playwright.js +65 -16
  35. package/packages/datadog-instrumentations/src/router.js +1 -1
  36. package/packages/datadog-instrumentations/src/selenium.js +3 -1
  37. package/packages/datadog-instrumentations/src/ws.js +35 -17
  38. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +3 -2
  39. package/packages/datadog-plugin-azure-service-bus/src/producer.js +14 -5
  40. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
  41. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +23 -2
  42. package/packages/datadog-plugin-cypress/src/plugin.js +1 -1
  43. package/packages/datadog-plugin-cypress/src/support.js +73 -31
  44. package/packages/datadog-plugin-google-genai/src/index.js +17 -0
  45. package/packages/datadog-plugin-google-genai/src/tracing.js +41 -0
  46. package/packages/datadog-plugin-graphql/src/tools/transforms.js +5 -4
  47. package/packages/datadog-plugin-jest/src/util.js +4 -3
  48. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -1
  49. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -1
  50. package/packages/datadog-plugin-langchain/src/tracing.js +7 -3
  51. package/packages/datadog-plugin-next/src/index.js +11 -3
  52. package/packages/datadog-plugin-openai/src/stream-helpers.js +1 -1
  53. package/packages/datadog-shimmer/src/shimmer.js +2 -2
  54. package/packages/dd-trace/src/aiguard/sdk.js +29 -14
  55. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  56. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
  57. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -1
  58. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -2
  59. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  60. package/packages/dd-trace/src/appsec/reporter.js +0 -4
  61. package/packages/dd-trace/src/baggage.js +11 -0
  62. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +4 -8
  63. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +1 -1
  64. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
  65. package/packages/dd-trace/src/config.js +81 -7
  66. package/packages/dd-trace/src/config_defaults.js +15 -2
  67. package/packages/dd-trace/src/datastreams/encoding.js +23 -6
  68. package/packages/dd-trace/src/datastreams/pathway.js +40 -1
  69. package/packages/dd-trace/src/datastreams/processor.js +1 -1
  70. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -1
  71. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +15 -5
  72. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  73. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  74. package/packages/dd-trace/src/debugger/devtools_client/index.js +30 -15
  75. package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +2 -0
  76. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +24 -18
  77. package/packages/dd-trace/src/debugger/devtools_client/send.js +18 -8
  78. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +103 -15
  79. package/packages/dd-trace/src/debugger/devtools_client/snapshot/constants.js +25 -0
  80. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +56 -25
  81. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +64 -23
  82. package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +3 -1
  83. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +404 -0
  84. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  85. package/packages/dd-trace/src/debugger/devtools_client/state.js +7 -2
  86. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  87. package/packages/dd-trace/src/debugger/index.js +1 -1
  88. package/packages/dd-trace/src/encode/0.4.js +3 -3
  89. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +2 -2
  90. package/packages/dd-trace/src/encode/span-stats.js +7 -1
  91. package/packages/dd-trace/src/exporters/agent/writer.js +6 -13
  92. package/packages/dd-trace/src/histogram.js +1 -1
  93. package/packages/dd-trace/src/id.js +60 -0
  94. package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -3
  95. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  96. package/packages/dd-trace/src/llmobs/index.js +5 -5
  97. package/packages/dd-trace/src/llmobs/noop.js +6 -0
  98. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +1 -0
  99. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +104 -0
  100. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +486 -0
  101. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +2 -2
  102. package/packages/dd-trace/src/llmobs/plugins/{openai.js → openai/index.js} +87 -39
  103. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +114 -0
  104. package/packages/dd-trace/src/llmobs/sdk.js +10 -1
  105. package/packages/dd-trace/src/llmobs/span_processor.js +11 -6
  106. package/packages/dd-trace/src/llmobs/tagger.js +35 -17
  107. package/packages/dd-trace/src/msgpack/chunk.js +2 -2
  108. package/packages/dd-trace/src/msgpack/encoder.js +2 -3
  109. package/packages/dd-trace/src/msgpack/index.js +2 -2
  110. package/packages/dd-trace/src/openfeature/flagging_provider.js +5 -3
  111. package/packages/dd-trace/src/opentelemetry/logs/index.js +3 -3
  112. package/packages/dd-trace/src/opentelemetry/logs/logger.js +14 -8
  113. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +6 -4
  114. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +9 -17
  115. package/packages/dd-trace/src/opentelemetry/metrics/constants.js +34 -0
  116. package/packages/dd-trace/src/opentelemetry/metrics/index.js +81 -0
  117. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +225 -0
  118. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +171 -0
  119. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +54 -0
  120. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +62 -0
  121. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +251 -0
  122. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +532 -0
  123. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +10 -18
  124. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +36 -22
  125. package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +2 -2
  126. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  127. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -1
  128. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  129. package/packages/dd-trace/src/payload-tagging/index.js +2 -2
  130. package/packages/dd-trace/src/plugin_manager.js +4 -2
  131. package/packages/dd-trace/src/plugins/database.js +1 -0
  132. package/packages/dd-trace/src/plugins/index.js +1 -0
  133. package/packages/dd-trace/src/plugins/plugin.js +7 -9
  134. package/packages/dd-trace/src/plugins/util/test.js +3 -3
  135. package/packages/dd-trace/src/plugins/util/url.js +119 -1
  136. package/packages/dd-trace/src/plugins/util/web.js +10 -41
  137. package/packages/dd-trace/src/process-tags/index.js +81 -0
  138. package/packages/dd-trace/src/profiling/config.js +1 -1
  139. package/packages/dd-trace/src/profiling/exporter_cli.js +7 -6
  140. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  141. package/packages/dd-trace/src/profiling/profilers/events.js +10 -1
  142. package/packages/dd-trace/src/proxy.js +5 -0
  143. package/packages/dd-trace/src/rate_limiter.js +1 -1
  144. package/packages/dd-trace/src/remote_config/manager.js +1 -1
  145. package/packages/dd-trace/src/require-package-json.js +1 -1
  146. package/packages/dd-trace/src/ritm.js +1 -1
  147. package/packages/dd-trace/src/service-naming/index.js +31 -4
  148. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  149. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  150. package/packages/dd-trace/src/{format.js → span_format.js} +9 -4
  151. package/packages/dd-trace/src/span_processor.js +16 -11
  152. package/packages/dd-trace/src/span_stats.js +15 -4
  153. package/packages/dd-trace/src/spanleak.js +1 -1
  154. package/packages/dd-trace/src/supported-configurations.json +13 -0
  155. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  156. package/packages/dd-trace/src/telemetry/telemetry.js +11 -2
  157. package/vendor/dist/@datadog/sketches-js/LICENSE +39 -0
  158. package/vendor/dist/@datadog/sketches-js/index.js +1 -0
  159. package/vendor/dist/@datadog/source-map/LICENSE +28 -0
  160. package/vendor/dist/@datadog/source-map/index.js +1 -0
  161. package/vendor/dist/@isaacs/ttlcache/LICENSE +55 -0
  162. package/vendor/dist/@isaacs/ttlcache/index.js +1 -0
  163. package/vendor/dist/@opentelemetry/core/LICENSE +201 -0
  164. package/vendor/dist/@opentelemetry/core/index.js +1 -0
  165. package/vendor/dist/@opentelemetry/resources/LICENSE +201 -0
  166. package/vendor/dist/@opentelemetry/resources/index.js +1 -0
  167. package/vendor/dist/astring/LICENSE +19 -0
  168. package/vendor/dist/astring/index.js +1 -0
  169. package/vendor/dist/crypto-randomuuid/index.js +1 -0
  170. package/vendor/dist/escape-string-regexp/LICENSE +9 -0
  171. package/vendor/dist/escape-string-regexp/index.js +1 -0
  172. package/vendor/dist/esquery/LICENSE +24 -0
  173. package/vendor/dist/esquery/index.js +1 -0
  174. package/vendor/dist/ignore/LICENSE +21 -0
  175. package/vendor/dist/ignore/index.js +1 -0
  176. package/vendor/dist/istanbul-lib-coverage/LICENSE +24 -0
  177. package/vendor/dist/istanbul-lib-coverage/index.js +1 -0
  178. package/vendor/dist/jest-docblock/LICENSE +21 -0
  179. package/vendor/dist/jest-docblock/index.js +1 -0
  180. package/vendor/dist/jsonpath-plus/LICENSE +22 -0
  181. package/vendor/dist/jsonpath-plus/index.js +1 -0
  182. package/vendor/dist/limiter/LICENSE +19 -0
  183. package/vendor/dist/limiter/index.js +1 -0
  184. package/vendor/dist/lodash.sortby/LICENSE +47 -0
  185. package/vendor/dist/lodash.sortby/index.js +1 -0
  186. package/vendor/dist/lru-cache/LICENSE +15 -0
  187. package/vendor/dist/lru-cache/index.js +1 -0
  188. package/vendor/dist/meriyah/LICENSE +7 -0
  189. package/vendor/dist/meriyah/index.js +1 -0
  190. package/vendor/dist/module-details-from-path/LICENSE +21 -0
  191. package/vendor/dist/module-details-from-path/index.js +1 -0
  192. package/vendor/dist/mutexify/promise/LICENSE +21 -0
  193. package/vendor/dist/mutexify/promise/index.js +1 -0
  194. package/vendor/dist/opentracing/LICENSE +201 -0
  195. package/vendor/dist/opentracing/binary_carrier.d.ts +11 -0
  196. package/vendor/dist/opentracing/constants.d.ts +61 -0
  197. package/vendor/dist/opentracing/examples/demo/demo.d.ts +2 -0
  198. package/vendor/dist/opentracing/ext/tags.d.ts +90 -0
  199. package/vendor/dist/opentracing/functions.d.ts +20 -0
  200. package/vendor/dist/opentracing/global_tracer.d.ts +14 -0
  201. package/vendor/dist/opentracing/index.d.ts +12 -0
  202. package/vendor/dist/opentracing/index.js +1 -0
  203. package/vendor/dist/opentracing/mock_tracer/index.d.ts +5 -0
  204. package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +13 -0
  205. package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +16 -0
  206. package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +50 -0
  207. package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +26 -0
  208. package/vendor/dist/opentracing/noop.d.ts +8 -0
  209. package/vendor/dist/opentracing/reference.d.ts +33 -0
  210. package/vendor/dist/opentracing/span.d.ts +147 -0
  211. package/vendor/dist/opentracing/span_context.d.ts +26 -0
  212. package/vendor/dist/opentracing/test/api_compatibility.d.ts +16 -0
  213. package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +3 -0
  214. package/vendor/dist/opentracing/test/noop_implementation.d.ts +4 -0
  215. package/vendor/dist/opentracing/test/opentracing_api.d.ts +3 -0
  216. package/vendor/dist/opentracing/test/unittest.d.ts +2 -0
  217. package/vendor/dist/opentracing/tracer.d.ts +127 -0
  218. package/vendor/dist/path-to-regexp/LICENSE +21 -0
  219. package/vendor/dist/path-to-regexp/index.js +1 -0
  220. package/vendor/dist/pprof-format/LICENSE +8 -0
  221. package/vendor/dist/pprof-format/index.js +1 -0
  222. package/vendor/dist/protobufjs/LICENSE +39 -0
  223. package/vendor/dist/protobufjs/index.js +1 -0
  224. package/vendor/dist/protobufjs/minimal/LICENSE +39 -0
  225. package/vendor/dist/protobufjs/minimal/index.js +1 -0
  226. package/vendor/dist/retry/LICENSE +21 -0
  227. package/vendor/dist/retry/index.js +1 -0
  228. package/vendor/dist/rfdc/LICENSE +15 -0
  229. package/vendor/dist/rfdc/index.js +1 -0
  230. package/vendor/dist/semifies/LICENSE +201 -0
  231. package/vendor/dist/semifies/index.js +1 -0
  232. package/vendor/dist/shell-quote/LICENSE +24 -0
  233. package/vendor/dist/shell-quote/index.js +1 -0
  234. package/vendor/dist/source-map/LICENSE +28 -0
  235. package/vendor/dist/source-map/index.js +1 -0
  236. package/vendor/dist/source-map/lib/util/LICENSE +28 -0
  237. package/vendor/dist/source-map/lib/util/index.js +1 -0
  238. package/vendor/dist/source-map/mappings.wasm +0 -0
  239. package/vendor/dist/tlhunter-sorted-set/LICENSE +21 -0
  240. package/vendor/dist/tlhunter-sorted-set/index.js +1 -0
  241. package/vendor/dist/ttl-set/LICENSE +21 -0
  242. package/vendor/dist/ttl-set/index.js +1 -0
@@ -0,0 +1,114 @@
1
+ 'use strict'
2
+
3
+ const IMAGE_FALLBACK = '[image]'
4
+ const FILE_FALLBACK = '[file]'
5
+
6
+ const REGEX_SPECIAL_CHARS = /[.*+?^${}()|[\]\\]/g
7
+
8
+ /**
9
+ * Extracts chat templates from OpenAI response instructions by replacing variable values with placeholders.
10
+ *
11
+ * Performs reverse templating: reconstructs the template by replacing actual values with {{variable_name}}.
12
+ * For images/files: uses {{variable_name}} when values are available, falls back to [image]/[file] when stripped.
13
+ *
14
+ * @param {Array<Object>} instructions - From Response.instructions (array of ResponseInputMessageItem)
15
+ * @param {Object<string, string>} variables - Normalized variables (output of normalizePromptVariables)
16
+ * @returns {Array<{role: string, content: string}>} Chat template with placeholders
17
+ */
18
+ function extractChatTemplateFromInstructions (instructions, variables) {
19
+ if (!Array.isArray(instructions) || !variables) return []
20
+
21
+ const chatTemplate = []
22
+
23
+ // Build map of values to placeholders - exclude fallback markers so they remain as-is
24
+ const valueToPlaceholder = {}
25
+ for (const [varName, varValue] of Object.entries(variables)) {
26
+ // Exclude fallback markers - they should remain as [image]/[file] in the template
27
+ if (varValue && varValue !== IMAGE_FALLBACK && varValue !== FILE_FALLBACK) {
28
+ valueToPlaceholder[varValue] = `{{${varName}}}`
29
+ }
30
+ }
31
+
32
+ // Sort values by length (longest first) to handle overlapping values correctly
33
+ const sortedValues = Object.keys(valueToPlaceholder).sort((a, b) => b.length - a.length)
34
+
35
+ for (const instruction of instructions) {
36
+ const role = instruction.role
37
+ if (!role) continue
38
+
39
+ const contentItems = instruction.content
40
+ if (!Array.isArray(contentItems)) continue
41
+
42
+ // Extract text from all content items (uses actual values for images/files when available)
43
+ const textParts = contentItems
44
+ .map(extractTextFromContentItem)
45
+ .filter(Boolean)
46
+
47
+ if (textParts.length === 0) continue
48
+
49
+ // Combine text and replace variable values with placeholders (longest first)
50
+ let fullText = textParts.join('')
51
+ for (const valueStr of sortedValues) {
52
+ const placeholder = valueToPlaceholder[valueStr]
53
+ const escapedValue = valueStr.replaceAll(REGEX_SPECIAL_CHARS, String.raw`\$&`)
54
+ fullText = fullText.replaceAll(new RegExp(escapedValue, 'g'), placeholder)
55
+ }
56
+
57
+ chatTemplate.push({ role, content: fullText })
58
+ }
59
+
60
+ return chatTemplate
61
+ }
62
+
63
+ /**
64
+ * Extracts text content from a content item, using actual image_url/file_id values when available.
65
+ *
66
+ * Used for both input messages and chat template extraction. Falls back to [image]/[file] markers
67
+ * when the actual values are stripped (e.g., by OpenAI's default URL stripping behavior).
68
+ *
69
+ * @param {Object} contentItem - Content item from Response.instructions[].content (ResponseInputContentItem)
70
+ * @returns {string|null} Text content, URL/file reference, or [image]/[file] fallback marker
71
+ */
72
+ function extractTextFromContentItem (contentItem) {
73
+ if (!contentItem) return null
74
+
75
+ if (contentItem.text) {
76
+ return contentItem.text
77
+ }
78
+
79
+ // For image/file items, extract the actual reference value
80
+ if (contentItem.type === 'input_image') {
81
+ return contentItem.image_url || contentItem.file_id || IMAGE_FALLBACK
82
+ }
83
+
84
+ if (contentItem.type === 'input_file') {
85
+ return contentItem.file_id || contentItem.file_url || contentItem.filename || FILE_FALLBACK
86
+ }
87
+
88
+ return null
89
+ }
90
+
91
+ /**
92
+ * Normalizes prompt variables by extracting meaningful values from OpenAI SDK response objects.
93
+ *
94
+ * Converts ResponseInputText, ResponseInputImage, and ResponseInputFile objects to simple string values.
95
+ *
96
+ * @param {Object<string, string|Object>} variables - From ResponsePrompt.variables
97
+ * @returns {Object<string, string>} Normalized variables with simple string values
98
+ */
99
+ function normalizePromptVariables (variables) {
100
+ if (!variables) return {}
101
+
102
+ return Object.fromEntries(
103
+ Object.entries(variables).map(([key, value]) => [
104
+ key,
105
+ extractTextFromContentItem(value) ?? String(value ?? '')
106
+ ])
107
+ )
108
+ }
109
+
110
+ module.exports = {
111
+ extractChatTemplateFromInstructions,
112
+ normalizePromptVariables,
113
+ extractTextFromContentItem
114
+ }
@@ -369,7 +369,7 @@ class LLMObs extends NoopLLMObs {
369
369
  err = 'invalid_metric_label'
370
370
  throw new Error('label must be the specified name of the evaluation metric')
371
371
  }
372
- if (!metricType || !['categorical', 'score'].includes(metricType)) {
372
+ if (!metricType || !['categorical', 'score', 'boolean'].includes(metricType)) {
373
373
  err = 'invalid_metric_type'
374
374
  throw new Error('metricType must be one of "categorical" or "score"')
375
375
  }
@@ -381,6 +381,10 @@ class LLMObs extends NoopLLMObs {
381
381
  err = 'invalid_metric_value'
382
382
  throw new Error('value must be a number for a score metric.')
383
383
  }
384
+ if (metricType === 'boolean' && typeof value !== 'boolean') {
385
+ err = 'invalid_metric_value'
386
+ throw new Error('value must be a boolean for a boolean metric')
387
+ }
384
388
 
385
389
  const evaluationTags = {
386
390
  'ddtrace.version': tracerVersion,
@@ -406,6 +410,11 @@ class LLMObs extends NoopLLMObs {
406
410
  }
407
411
  }
408
412
 
413
+ // When OTel tracing is enabled, add source:otel tag to allow backend to wait for OTel span conversion
414
+ if (isTrue(getEnvironmentVariable('DD_TRACE_OTEL_ENABLED'))) {
415
+ evaluationTags.source = 'otel'
416
+ }
417
+
409
418
  const payload = {
410
419
  span_id: spanId,
411
420
  trace_id: traceId,
@@ -72,7 +72,7 @@ class LLMObsSpanProcessor {
72
72
  }
73
73
 
74
74
  // TODO: instead of relying on the tagger's weakmap registry, can we use some namespaced storage correlation?
75
- process ({ span }) {
75
+ process (span) {
76
76
  if (!this.#config.llmobs.enabled) return
77
77
  // if the span is not in our private tagger map, it is not an llmobs span
78
78
  if (!LLMObsTagger.tagMap.has(span)) return
@@ -126,6 +126,11 @@ class LLMObsSpanProcessor {
126
126
  inputType = 'value'
127
127
  }
128
128
 
129
+ // Handle prompt metadata for reusable prompts
130
+ if (mlObsTags['_ml_obs.meta.input.prompt']) {
131
+ input.prompt = mlObsTags['_ml_obs.meta.input.prompt']
132
+ }
133
+
129
134
  if (spanKind === 'llm' && mlObsTags[OUTPUT_MESSAGES]) {
130
135
  llmObsSpan.output = mlObsTags[OUTPUT_MESSAGES]
131
136
  outputType = 'messages'
@@ -155,7 +160,7 @@ class LLMObsSpanProcessor {
155
160
  llmObsSpan._tags = tags
156
161
 
157
162
  const processedSpan = this.#runProcessor(llmObsSpan)
158
- if (processedSpan === null) return null
163
+ if (processedSpan === undefined) return null
159
164
 
160
165
  if (processedSpan.input) {
161
166
  if (inputType === 'messages') {
@@ -207,7 +212,7 @@ class LLMObsSpanProcessor {
207
212
  const seenObjects = new WeakSet([obj])
208
213
 
209
214
  const isCircular = value => {
210
- if (typeof value !== 'object') return false
215
+ if (value == null || typeof value !== 'object') return false
211
216
  if (seenObjects.has(value)) return true
212
217
  seenObjects.add(value)
213
218
  return false
@@ -269,7 +274,7 @@ class LLMObsSpanProcessor {
269
274
  /**
270
275
  * Runs the user span processor, emitting telemetry and adding some guardrails against invalid return types
271
276
  * @param {LLMObservabilitySpan} span
272
- * @returns {LLMObservabilitySpan | null}
277
+ * @returns {LLMObservabilitySpan | undefined}
273
278
  */
274
279
  #runProcessor (span) {
275
280
  const processor = this.#userSpanProcessor
@@ -279,12 +284,12 @@ class LLMObsSpanProcessor {
279
284
 
280
285
  try {
281
286
  const processedLLMObsSpan = processor(span)
282
- if (processedLLMObsSpan === null) return null
287
+ if (processedLLMObsSpan === null) return
283
288
 
284
289
  if (!(processedLLMObsSpan instanceof LLMObservabilitySpan)) {
285
290
  error = true
286
291
  logger.warn('User span processor must return an instance of an LLMObservabilitySpan or null, dropping span.')
287
- return null
292
+ return
288
293
  }
289
294
 
290
295
  return processedLLMObsSpan
@@ -25,6 +25,7 @@ const {
25
25
  INPUT_TOKENS_METRIC_KEY,
26
26
  OUTPUT_TOKENS_METRIC_KEY,
27
27
  TOTAL_TOKENS_METRIC_KEY,
28
+ REASONING_OUTPUT_TOKENS_METRIC_KEY,
28
29
  INTEGRATION,
29
30
  DECORATOR,
30
31
  PROPAGATED_ML_APP_KEY
@@ -85,7 +86,7 @@ class LLMObsTagger {
85
86
  if (name) this._setTag(span, NAME, name)
86
87
 
87
88
  this._setTag(span, SPAN_KIND, kind)
88
- if (modelName) this._setTag(span, MODEL_NAME, modelName)
89
+ if (modelName) this.tagModelName(span, modelName)
89
90
  if (modelProvider) this._setTag(span, MODEL_PROVIDER, modelProvider)
90
91
 
91
92
  sessionId = sessionId || registry.get(parent)?.[SESSION_ID]
@@ -164,6 +165,9 @@ class LLMObsTagger {
164
165
  case 'cacheWriteTokens':
165
166
  processedKey = CACHE_WRITE_INPUT_TOKENS_METRIC_KEY
166
167
  break
168
+ case 'reasoningOutputTokens':
169
+ processedKey = REASONING_OUTPUT_TOKENS_METRIC_KEY
170
+ break
167
171
  }
168
172
 
169
173
  if (typeof value === 'number') {
@@ -194,6 +198,10 @@ class LLMObsTagger {
194
198
  this._setTag(span, SPAN_KIND, newKind)
195
199
  }
196
200
 
201
+ tagModelName (span, modelName) {
202
+ this._setTag(span, MODEL_NAME, modelName)
203
+ }
204
+
197
205
  #tagText (span, data, key) {
198
206
  if (data) {
199
207
  if (typeof data === 'string') {
@@ -324,7 +332,7 @@ class LLMObsTagger {
324
332
 
325
333
  for (const message of data) {
326
334
  if (typeof message === 'string') {
327
- messages.push({ content: message })
335
+ messages.push({ content: message, role: '' })
328
336
  continue
329
337
  }
330
338
  if (message == null || typeof message !== 'object') {
@@ -332,20 +340,30 @@ class LLMObsTagger {
332
340
  continue
333
341
  }
334
342
 
335
- const { content = '', role } = message
336
- const toolCalls = message.toolCalls
337
- const toolResults = message.toolResults
338
- const toolId = message.toolId
339
- const messageObj = { content }
343
+ const {
344
+ role = '',
345
+ content,
346
+ toolCalls,
347
+ toolResults,
348
+ toolId
349
+ } = message
350
+ const messageObj = {}
340
351
 
341
352
  let condition = this.#tagConditionalString(role, 'Message role', messageObj, 'role')
342
353
 
343
- const valid = typeof content === 'string'
344
- if (!valid) {
345
- this.#handleFailure('Message content must be a string.', 'invalid_io_messages')
354
+ if (
355
+ content == null &&
356
+ toolCalls == null &&
357
+ toolResults == null
358
+ ) {
359
+ messageObj.content = ''
360
+ }
361
+
362
+ if (content != null) {
363
+ condition = this.#tagConditionalString(content, 'Message content', messageObj, 'content') && condition
346
364
  }
347
365
 
348
- if (toolCalls) {
366
+ if (toolCalls != null) {
349
367
  const filteredToolCalls = this.#filterToolCalls(toolCalls)
350
368
 
351
369
  if (filteredToolCalls.length) {
@@ -353,7 +371,7 @@ class LLMObsTagger {
353
371
  }
354
372
  }
355
373
 
356
- if (toolResults) {
374
+ if (toolResults != null) {
357
375
  const filteredToolResults = this.#filterToolResults(toolResults)
358
376
 
359
377
  if (filteredToolResults.length) {
@@ -363,13 +381,13 @@ class LLMObsTagger {
363
381
 
364
382
  if (toolId) {
365
383
  if (role === 'tool') {
366
- condition = this.#tagConditionalString(toolId, 'Tool ID', messageObj, 'tool_id')
384
+ condition = this.#tagConditionalString(toolId, 'Tool ID', messageObj, 'tool_id') && condition
367
385
  } else {
368
386
  log.warn(`Tool ID for tool message not associated with a "tool" role, instead got "${role}"`)
369
387
  }
370
388
  }
371
389
 
372
- if (valid && condition) {
390
+ if (condition) {
373
391
  messages.push(messageObj)
374
392
  }
375
393
  }
@@ -380,7 +398,7 @@ class LLMObsTagger {
380
398
  }
381
399
 
382
400
  #tagConditionalString (data, type, carrier, key) {
383
- if (!data) return true
401
+ if (data == null) return true
384
402
  if (typeof data !== 'string') {
385
403
  this.#handleFailure(`"${type}" must be a string.`)
386
404
  return false
@@ -390,7 +408,7 @@ class LLMObsTagger {
390
408
  }
391
409
 
392
410
  #tagConditionalNumber (data, type, carrier, key) {
393
- if (!data) return true
411
+ if (data == null) return true
394
412
  if (typeof data !== 'number') {
395
413
  this.#handleFailure(`"${type}" must be a number.`)
396
414
  return false
@@ -400,7 +418,7 @@ class LLMObsTagger {
400
418
  }
401
419
 
402
420
  #tagConditionalObject (data, type, carrier, key) {
403
- if (!data) return true
421
+ if (data == null) return true
404
422
  if (typeof data !== 'object') {
405
423
  this.#handleFailure(`"${type}" must be an object.`)
406
424
  return false
@@ -7,7 +7,7 @@ const DEFAULT_MIN_SIZE = 2 * 1024 * 1024 // 2MB
7
7
  * interfaces so that it can be used seamlessly by any encoder code that expects
8
8
  * either.
9
9
  */
10
- class Chunk {
10
+ class MsgpackChunk {
11
11
  constructor (minSize = DEFAULT_MIN_SIZE) {
12
12
  this.buffer = Buffer.allocUnsafe(minSize)
13
13
  this.view = new DataView(this.buffer.buffer)
@@ -66,4 +66,4 @@ class Chunk {
66
66
  }
67
67
  }
68
68
 
69
- module.exports = Chunk
69
+ module.exports = MsgpackChunk
@@ -1,11 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const Chunk = require('./chunk')
3
+ const MsgpackChunk = require('./chunk')
4
4
 
5
5
  class MsgpackEncoder {
6
6
  encode (value) {
7
- const bytes = new Chunk()
8
-
7
+ const bytes = new MsgpackChunk()
9
8
  this.encodeValue(bytes, value)
10
9
 
11
10
  return bytes.buffer.subarray(0, bytes.length)
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const Chunk = require('./chunk')
3
+ const MsgpackChunk = require('./chunk')
4
4
  const { MsgpackEncoder } = require('./encoder')
5
5
 
6
- module.exports = { Chunk, MsgpackEncoder }
6
+ module.exports = { MsgpackChunk, MsgpackEncoder }
@@ -15,15 +15,17 @@ class FlaggingProvider extends DatadogNodeServerProvider {
15
15
  * @param {import('../config')} config - Tracer configuration object
16
16
  */
17
17
  constructor (tracer, config) {
18
- // Call parent constructor with required options
18
+ // Call parent constructor with required options and timeout
19
19
  super({
20
- exposureChannel: channel(EXPOSURE_CHANNEL)
20
+ exposureChannel: channel(EXPOSURE_CHANNEL),
21
+ initializationTimeoutMs: config.experimental.flaggingProvider.initializationTimeoutMs
21
22
  })
22
23
 
23
24
  this._tracer = tracer
24
25
  this._config = config
25
26
 
26
- log.debug(this.constructor.name + ' created')
27
+ log.debug(this.constructor.name + ' created with timeout: ' +
28
+ config.experimental.flaggingProvider.initializationTimeoutMs + 'ms')
27
29
  }
28
30
 
29
31
  /**
@@ -7,7 +7,7 @@ const os = require('os')
7
7
  */
8
8
 
9
9
  /**
10
- * @fileoverview OpenTelemetry Logs Implementation for dd-trace-js
10
+ * OpenTelemetry Logs Implementation for `dd-trace-js`
11
11
  *
12
12
  * This package provides a custom OpenTelemetry Logs implementation that integrates
13
13
  * with the Datadog tracing library. It includes all necessary components for
@@ -70,8 +70,8 @@ function initializeOpenTelemetryLogs (config) {
70
70
  // Create batch processor for exporting logs to Datadog Agent
71
71
  const processor = new BatchLogRecordProcessor(
72
72
  exporter,
73
- config.otelLogsBatchTimeout,
74
- config.otelLogsMaxExportBatchSize
73
+ config.otelBatchTimeout,
74
+ config.otelMaxExportBatchSize
75
75
  )
76
76
 
77
77
  // Create logger provider with processor for Datadog Agent export
@@ -1,14 +1,17 @@
1
1
  'use strict'
2
2
 
3
- const { sanitizeAttributes } = require('@opentelemetry/core')
3
+ const { sanitizeAttributes } = require('../../../../../vendor/dist/@opentelemetry/core')
4
4
  const { context } = require('@opentelemetry/api')
5
- const packageVersion = require('../../../../../package.json').version
5
+ const { VERSION: packageVersion } = require('../../../../../version')
6
+
6
7
  /**
7
8
  * @typedef {import('@opentelemetry/api-logs').LogRecord} LogRecord
9
+ * @typedef {import('@opentelemetry/api-logs').LoggerProvider} LoggerProvider
8
10
  * @typedef {import('@opentelemetry/api').SpanContext} SpanContext
9
11
  * @typedef {import('@opentelemetry/api').Attributes} Attributes
10
12
  * @typedef {import('@opentelemetry/resources').Resource} Resource
11
13
  * @typedef {import('@opentelemetry/core').InstrumentationScope} InstrumentationScope
14
+ * @typedef {import('@opentelemetry/core').InstrumentationLibrary} InstrumentationLibrary
12
15
  */
13
16
 
14
17
  /**
@@ -26,12 +29,15 @@ class Logger {
26
29
  * Creates a new Logger instance.
27
30
  *
28
31
  * @param {LoggerProvider} loggerProvider - Parent logger provider
29
- * @param {InstrumentationScope} [instrumentationScope] - Instrumentation scope information (newer API)
30
- * @param {Object} [instrumentationLibrary] - Instrumentation library information (legacy API) [DEPRECATED in v1.3.0]
31
- * @param {InstrumentationScope} [instrumentationScope.name] - Library name (defaults to 'dd-trace-js')
32
- * @param {InstrumentationScope} [instrumentationScope.version] - Library version (defaults to tracer version)
33
- * @param {string} [instrumentationLibrary.name] - Library name (legacy, defaults to 'dd-trace-js')
34
- * @param {string} [instrumentationLibrary.version] - Library version (legacy, defaults to tracer version)
32
+ * @param {InstrumentationScope} [instrumentationScope] - Instrumentation scope information (newer API).
33
+ * `name` defaults to 'dd-trace-js';
34
+ * `version` defaults to tracer version;
35
+ * `schemaUrl` defaults to '';
36
+ * @param {InstrumentationLibrary} [instrumentationLibrary]
37
+ * - Instrumentation library information (legacy API) [DEPRECATED in v1.3.0].
38
+ * `name` defaults to 'dd-trace-js';
39
+ * `version` defaults to tracer version;
40
+ * `schemaUrl` defaults to '';
35
41
  */
36
42
  constructor (loggerProvider, instrumentationScope, instrumentationLibrary) {
37
43
  this.loggerProvider = loggerProvider
@@ -11,11 +11,11 @@ const OtlpTransformer = require('./otlp_transformer')
11
11
  /**
12
12
  * OtlpHttpLogExporter exports log records via OTLP over HTTP.
13
13
  *
14
- * This implementation follows the OTLP HTTP specification:
14
+ * This implementation follows the OTLP HTTP v1.7.0 specification:
15
15
  * https://opentelemetry.io/docs/specs/otlp/#otlphttp
16
16
  *
17
17
  * @class OtlpHttpLogExporter
18
- * @extends OtlpHttpExporterBase
18
+ * @augments OtlpHttpExporterBase
19
19
  */
20
20
  class OtlpHttpLogExporter extends OtlpHttpExporterBase {
21
21
  /**
@@ -37,6 +37,8 @@ class OtlpHttpLogExporter extends OtlpHttpExporterBase {
37
37
  *
38
38
  * @param {LogRecord[]} logRecords - Array of enriched log records to export
39
39
  * @param {Function} resultCallback - Callback function for export result
40
+ *
41
+ * @returns {void}
40
42
  */
41
43
  export (logRecords, resultCallback) {
42
44
  if (logRecords.length === 0) {
@@ -45,8 +47,8 @@ class OtlpHttpLogExporter extends OtlpHttpExporterBase {
45
47
  }
46
48
 
47
49
  const payload = this.transformer.transformLogRecords(logRecords)
48
- this._sendPayload(payload, resultCallback)
49
- this._recordTelemetry('otel.log_records', logRecords.length)
50
+ this.sendPayload(payload, resultCallback)
51
+ this.recordTelemetry('otel.log_records', logRecords.length)
50
52
  }
51
53
  }
52
54
 
@@ -40,11 +40,11 @@ const SEVERITY_MAP = {
40
40
  /**
41
41
  * OtlpTransformer transforms log records to OTLP format.
42
42
  *
43
- * This implementation follows the OTLP Logs Data Model specification:
43
+ * This implementation follows the OTLP Logs v1.7.0 Data Model specification:
44
44
  * https://opentelemetry.io/docs/specs/otlp/#log-data-model
45
45
  *
46
46
  * @class OtlpTransformer
47
- * @extends OtlpTransformerBase
47
+ * @augments OtlpTransformerBase
48
48
  */
49
49
  class OtlpTransformer extends OtlpTransformerBase {
50
50
  /**
@@ -73,45 +73,42 @@ class OtlpTransformer extends OtlpTransformerBase {
73
73
  * Transforms log records to protobuf format.
74
74
  * @param {LogRecord[]} logRecords - Array of enriched log records to transform
75
75
  * @returns {Buffer} Protobuf-encoded log records
76
- * @private
77
76
  */
78
77
  #transformToProtobuf (logRecords) {
79
78
  const { protoLogsService } = getProtobufTypes()
80
79
 
81
80
  const logsData = {
82
81
  resourceLogs: [{
83
- resource: this._transformResource(),
82
+ resource: this.transformResource(),
84
83
  scopeLogs: this.#transformScope(logRecords),
85
84
  }]
86
85
  }
87
86
 
88
- return this._serializeToProtobuf(protoLogsService, logsData)
87
+ return this.serializeToProtobuf(protoLogsService, logsData)
89
88
  }
90
89
 
91
90
  /**
92
91
  * Transforms log records to JSON format.
93
92
  * @param {LogRecord[]} logRecords - Array of enriched log records to transform
94
93
  * @returns {Buffer} JSON-encoded log records
95
- * @private
96
94
  */
97
95
  #transformToJson (logRecords) {
98
96
  const logsData = {
99
97
  resourceLogs: [{
100
- resource: this._transformResource(),
98
+ resource: this.transformResource(),
101
99
  scopeLogs: this.#transformScope(logRecords)
102
100
  }]
103
101
  }
104
- return this._serializeToJson(logsData)
102
+ return this.serializeToJson(logsData)
105
103
  }
106
104
 
107
105
  /**
108
106
  * Creates scope logs grouped by instrumentation library.
109
107
  * @param {LogRecord[]} logRecords - Array of log records to transform
110
108
  * @returns {Object[]} Array of scope log objects
111
- * @private
112
109
  */
113
110
  #transformScope (logRecords) {
114
- const groupedRecords = this._groupByInstrumentationScope(logRecords)
111
+ const groupedRecords = this.groupByInstrumentationScope(logRecords)
115
112
  const scopeLogs = []
116
113
 
117
114
  for (const records of groupedRecords.values()) {
@@ -135,7 +132,6 @@ class OtlpTransformer extends OtlpTransformerBase {
135
132
  * Transforms a single log record to OTLP format.
136
133
  * @param {LogRecord} logRecord - Log record to transform
137
134
  * @returns {Object} OTLP log record object
138
- * @private
139
135
  */
140
136
  #transformLogRecord (logRecord) {
141
137
  const spanContext = this.#extractSpanContext(logRecord.context)
@@ -159,7 +155,7 @@ class OtlpTransformer extends OtlpTransformerBase {
159
155
  }
160
156
 
161
157
  if (logRecord.attributes) {
162
- result.attributes = this._transformAttributes(logRecord.attributes)
158
+ result.attributes = this.transformAttributes(logRecord.attributes)
163
159
  }
164
160
 
165
161
  if (spanContext?.traceFlags !== undefined) {
@@ -182,7 +178,6 @@ class OtlpTransformer extends OtlpTransformerBase {
182
178
  * Extracts span context from the log record's context.
183
179
  * @param {Object} logContext - The log record's context
184
180
  * @returns {Object|null} Span context or null if not available
185
- * @private
186
181
  */
187
182
  #extractSpanContext (logContext) {
188
183
  if (!logContext) return null
@@ -199,7 +194,6 @@ class OtlpTransformer extends OtlpTransformerBase {
199
194
  * Maps OpenTelemetry severity number to protobuf severity number.
200
195
  * @param {number} severityNumber - OpenTelemetry severity number
201
196
  * @returns {number} Protobuf severity number
202
- * @private
203
197
  */
204
198
  #mapSeverityNumber (severityNumber) {
205
199
  const { protoSeverityNumber } = getProtobufTypes()
@@ -211,7 +205,6 @@ class OtlpTransformer extends OtlpTransformerBase {
211
205
  * Converts a hex string to a Buffer.
212
206
  * @param {string} hexString - Hex string to convert
213
207
  * @returns {Buffer} Buffer containing the hex data
214
- * @private
215
208
  */
216
209
  #hexToBytes (hexString) {
217
210
  const cleanHex = hexString ? (hexString.startsWith('0x') ? hexString.slice(2) : hexString) : ''
@@ -223,7 +216,6 @@ class OtlpTransformer extends OtlpTransformerBase {
223
216
  * Transforms log body to OTLP AnyValue format.
224
217
  * @param {any} body - Log body to transform
225
218
  * @returns {Object} OTLP AnyValue object
226
- * @private
227
219
  */
228
220
  #transformBody (body) {
229
221
  if (typeof body === 'string') {
@@ -240,7 +232,7 @@ class OtlpTransformer extends OtlpTransformerBase {
240
232
  kvlistValue: {
241
233
  values: Object.entries(body).map(([key, value]) => ({
242
234
  key,
243
- value: this._transformAnyValue(value)
235
+ value: this.transformAnyValue(value)
244
236
  }))
245
237
  }
246
238
  }
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ // Metric type constants
4
+ const METRIC_TYPES = {
5
+ HISTOGRAM: 'histogram',
6
+ COUNTER: 'counter',
7
+ UPDOWNCOUNTER: 'updowncounter',
8
+ OBSERVABLECOUNTER: 'observable-counter',
9
+ OBSERVABLEUPDOWNCOUNTER: 'observable-updowncounter',
10
+ GAUGE: 'gauge'
11
+ }
12
+
13
+ // Temporality constants
14
+ const TEMPORALITY = {
15
+ DELTA: 'DELTA',
16
+ CUMULATIVE: 'CUMULATIVE',
17
+ GAUGE: 'GAUGE',
18
+ LOWMEMORY: 'LOWMEMORY'
19
+ }
20
+
21
+ // Default histogram bucket boundaries (in milliseconds for latency metrics)
22
+ const DEFAULT_HISTOGRAM_BUCKETS = [0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10_000]
23
+
24
+ // Maximum number of measurements to queue before dropping
25
+ // This limit corresponds to the maximum number of items
26
+ // that be reliably added to a single Array.
27
+ const DEFAULT_MAX_MEASUREMENT_QUEUE_SIZE = 65_536
28
+
29
+ module.exports = {
30
+ METRIC_TYPES,
31
+ TEMPORALITY,
32
+ DEFAULT_HISTOGRAM_BUCKETS,
33
+ DEFAULT_MAX_MEASUREMENT_QUEUE_SIZE
34
+ }