dd-trace 5.80.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 (213) hide show
  1. package/LICENSE-3rdparty.csv +79 -88
  2. package/ext/tags.d.ts +1 -0
  3. package/ext/tags.js +1 -0
  4. package/index.d.ts +35 -35
  5. package/loader-hook.mjs +10 -3
  6. package/package.json +22 -40
  7. package/packages/datadog-esbuild/index.js +36 -19
  8. package/packages/datadog-instrumentations/index.js +1 -0
  9. package/packages/datadog-instrumentations/src/anthropic.js +12 -0
  10. package/packages/datadog-instrumentations/src/aws-sdk.js +5 -1
  11. package/packages/datadog-instrumentations/src/cucumber.js +2 -2
  12. package/packages/datadog-instrumentations/src/find-my-way.js +6 -5
  13. package/packages/datadog-instrumentations/src/google-genai.js +120 -0
  14. package/packages/datadog-instrumentations/src/graphql.js +20 -0
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +10 -0
  17. package/packages/datadog-instrumentations/src/helpers/register.js +6 -1
  18. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +27 -0
  19. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +152 -0
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +5 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langchain.js +237 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.js +9 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +11 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +139 -0
  25. package/packages/datadog-instrumentations/src/langchain.js +3 -109
  26. package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
  27. package/packages/datadog-instrumentations/src/mysql2.js +1 -1
  28. package/packages/datadog-instrumentations/src/playwright.js +45 -16
  29. package/packages/datadog-instrumentations/src/router.js +1 -1
  30. package/packages/datadog-instrumentations/src/selenium.js +3 -1
  31. package/packages/datadog-instrumentations/src/ws.js +35 -17
  32. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
  33. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +23 -2
  34. package/packages/datadog-plugin-cypress/src/plugin.js +1 -1
  35. package/packages/datadog-plugin-cypress/src/support.js +73 -31
  36. package/packages/datadog-plugin-google-genai/src/index.js +17 -0
  37. package/packages/datadog-plugin-google-genai/src/tracing.js +41 -0
  38. package/packages/datadog-plugin-graphql/src/tools/transforms.js +5 -4
  39. package/packages/datadog-plugin-jest/src/util.js +1 -1
  40. package/packages/datadog-plugin-langchain/src/tracing.js +7 -3
  41. package/packages/datadog-plugin-next/src/index.js +11 -3
  42. package/packages/dd-trace/src/aiguard/sdk.js +18 -10
  43. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  44. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
  45. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -1
  46. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -2
  47. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  48. package/packages/dd-trace/src/appsec/reporter.js +0 -4
  49. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +4 -8
  50. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
  51. package/packages/dd-trace/src/config.js +81 -7
  52. package/packages/dd-trace/src/config_defaults.js +14 -2
  53. package/packages/dd-trace/src/datastreams/encoding.js +23 -6
  54. package/packages/dd-trace/src/datastreams/pathway.js +40 -1
  55. package/packages/dd-trace/src/datastreams/processor.js +1 -1
  56. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -1
  57. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +15 -5
  58. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  59. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  60. package/packages/dd-trace/src/debugger/devtools_client/index.js +30 -15
  61. package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +2 -0
  62. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +24 -18
  63. package/packages/dd-trace/src/debugger/devtools_client/send.js +18 -8
  64. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +103 -15
  65. package/packages/dd-trace/src/debugger/devtools_client/snapshot/constants.js +25 -0
  66. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +56 -25
  67. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +64 -23
  68. package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +3 -1
  69. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +404 -0
  70. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  71. package/packages/dd-trace/src/debugger/devtools_client/state.js +7 -2
  72. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  73. package/packages/dd-trace/src/debugger/index.js +1 -1
  74. package/packages/dd-trace/src/encode/span-stats.js +7 -1
  75. package/packages/dd-trace/src/histogram.js +1 -1
  76. package/packages/dd-trace/src/id.js +60 -0
  77. package/packages/dd-trace/src/lambda/runtime/ritm.js +1 -1
  78. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  79. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +104 -0
  80. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +486 -0
  81. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +2 -2
  82. package/packages/dd-trace/src/llmobs/plugins/{openai.js → openai/index.js} +48 -6
  83. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +114 -0
  84. package/packages/dd-trace/src/llmobs/sdk.js +5 -0
  85. package/packages/dd-trace/src/llmobs/span_processor.js +6 -1
  86. package/packages/dd-trace/src/llmobs/tagger.js +4 -0
  87. package/packages/dd-trace/src/opentelemetry/logs/index.js +2 -2
  88. package/packages/dd-trace/src/opentelemetry/logs/logger.js +3 -2
  89. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +5 -3
  90. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +8 -8
  91. package/packages/dd-trace/src/opentelemetry/metrics/constants.js +34 -0
  92. package/packages/dd-trace/src/opentelemetry/metrics/index.js +81 -0
  93. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +225 -0
  94. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +171 -0
  95. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +54 -0
  96. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +62 -0
  97. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +251 -0
  98. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +532 -0
  99. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +10 -18
  100. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +36 -22
  101. package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +1 -1
  102. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  103. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -1
  104. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  105. package/packages/dd-trace/src/payload-tagging/index.js +2 -2
  106. package/packages/dd-trace/src/plugin_manager.js +4 -2
  107. package/packages/dd-trace/src/plugins/index.js +1 -0
  108. package/packages/dd-trace/src/plugins/util/test.js +3 -3
  109. package/packages/dd-trace/src/plugins/util/url.js +119 -1
  110. package/packages/dd-trace/src/plugins/util/web.js +10 -41
  111. package/packages/dd-trace/src/process-tags/index.js +81 -0
  112. package/packages/dd-trace/src/profiling/config.js +1 -1
  113. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  114. package/packages/dd-trace/src/profiling/profilers/events.js +10 -1
  115. package/packages/dd-trace/src/proxy.js +5 -0
  116. package/packages/dd-trace/src/rate_limiter.js +1 -1
  117. package/packages/dd-trace/src/remote_config/manager.js +1 -1
  118. package/packages/dd-trace/src/ritm.js +1 -1
  119. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  120. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  121. package/packages/dd-trace/src/span_format.js +9 -4
  122. package/packages/dd-trace/src/span_processor.js +8 -3
  123. package/packages/dd-trace/src/span_stats.js +15 -4
  124. package/packages/dd-trace/src/spanleak.js +1 -1
  125. package/packages/dd-trace/src/supported-configurations.json +13 -0
  126. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  127. package/packages/dd-trace/src/telemetry/telemetry.js +11 -2
  128. package/vendor/dist/@datadog/sketches-js/LICENSE +39 -0
  129. package/vendor/dist/@datadog/sketches-js/index.js +1 -0
  130. package/vendor/dist/@datadog/source-map/LICENSE +28 -0
  131. package/vendor/dist/@datadog/source-map/index.js +1 -0
  132. package/vendor/dist/@isaacs/ttlcache/LICENSE +55 -0
  133. package/vendor/dist/@isaacs/ttlcache/index.js +1 -0
  134. package/vendor/dist/@opentelemetry/core/LICENSE +201 -0
  135. package/vendor/dist/@opentelemetry/core/index.js +1 -0
  136. package/vendor/dist/@opentelemetry/resources/LICENSE +201 -0
  137. package/vendor/dist/@opentelemetry/resources/index.js +1 -0
  138. package/vendor/dist/astring/LICENSE +19 -0
  139. package/vendor/dist/astring/index.js +1 -0
  140. package/vendor/dist/crypto-randomuuid/index.js +1 -0
  141. package/vendor/dist/escape-string-regexp/LICENSE +9 -0
  142. package/vendor/dist/escape-string-regexp/index.js +1 -0
  143. package/vendor/dist/esquery/LICENSE +24 -0
  144. package/vendor/dist/esquery/index.js +1 -0
  145. package/vendor/dist/ignore/LICENSE +21 -0
  146. package/vendor/dist/ignore/index.js +1 -0
  147. package/vendor/dist/istanbul-lib-coverage/LICENSE +24 -0
  148. package/vendor/dist/istanbul-lib-coverage/index.js +1 -0
  149. package/vendor/dist/jest-docblock/LICENSE +21 -0
  150. package/vendor/dist/jest-docblock/index.js +1 -0
  151. package/vendor/dist/jsonpath-plus/LICENSE +22 -0
  152. package/vendor/dist/jsonpath-plus/index.js +1 -0
  153. package/vendor/dist/limiter/LICENSE +19 -0
  154. package/vendor/dist/limiter/index.js +1 -0
  155. package/vendor/dist/lodash.sortby/LICENSE +47 -0
  156. package/vendor/dist/lodash.sortby/index.js +1 -0
  157. package/vendor/dist/lru-cache/LICENSE +15 -0
  158. package/vendor/dist/lru-cache/index.js +1 -0
  159. package/vendor/dist/meriyah/LICENSE +7 -0
  160. package/vendor/dist/meriyah/index.js +1 -0
  161. package/vendor/dist/module-details-from-path/LICENSE +21 -0
  162. package/vendor/dist/module-details-from-path/index.js +1 -0
  163. package/vendor/dist/mutexify/promise/LICENSE +21 -0
  164. package/vendor/dist/mutexify/promise/index.js +1 -0
  165. package/vendor/dist/opentracing/LICENSE +201 -0
  166. package/vendor/dist/opentracing/binary_carrier.d.ts +11 -0
  167. package/vendor/dist/opentracing/constants.d.ts +61 -0
  168. package/vendor/dist/opentracing/examples/demo/demo.d.ts +2 -0
  169. package/vendor/dist/opentracing/ext/tags.d.ts +90 -0
  170. package/vendor/dist/opentracing/functions.d.ts +20 -0
  171. package/vendor/dist/opentracing/global_tracer.d.ts +14 -0
  172. package/vendor/dist/opentracing/index.d.ts +12 -0
  173. package/vendor/dist/opentracing/index.js +1 -0
  174. package/vendor/dist/opentracing/mock_tracer/index.d.ts +5 -0
  175. package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +13 -0
  176. package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +16 -0
  177. package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +50 -0
  178. package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +26 -0
  179. package/vendor/dist/opentracing/noop.d.ts +8 -0
  180. package/vendor/dist/opentracing/reference.d.ts +33 -0
  181. package/vendor/dist/opentracing/span.d.ts +147 -0
  182. package/vendor/dist/opentracing/span_context.d.ts +26 -0
  183. package/vendor/dist/opentracing/test/api_compatibility.d.ts +16 -0
  184. package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +3 -0
  185. package/vendor/dist/opentracing/test/noop_implementation.d.ts +4 -0
  186. package/vendor/dist/opentracing/test/opentracing_api.d.ts +3 -0
  187. package/vendor/dist/opentracing/test/unittest.d.ts +2 -0
  188. package/vendor/dist/opentracing/tracer.d.ts +127 -0
  189. package/vendor/dist/path-to-regexp/LICENSE +21 -0
  190. package/vendor/dist/path-to-regexp/index.js +1 -0
  191. package/vendor/dist/pprof-format/LICENSE +8 -0
  192. package/vendor/dist/pprof-format/index.js +1 -0
  193. package/vendor/dist/protobufjs/LICENSE +39 -0
  194. package/vendor/dist/protobufjs/index.js +1 -0
  195. package/vendor/dist/protobufjs/minimal/LICENSE +39 -0
  196. package/vendor/dist/protobufjs/minimal/index.js +1 -0
  197. package/vendor/dist/retry/LICENSE +21 -0
  198. package/vendor/dist/retry/index.js +1 -0
  199. package/vendor/dist/rfdc/LICENSE +15 -0
  200. package/vendor/dist/rfdc/index.js +1 -0
  201. package/vendor/dist/semifies/LICENSE +201 -0
  202. package/vendor/dist/semifies/index.js +1 -0
  203. package/vendor/dist/shell-quote/LICENSE +24 -0
  204. package/vendor/dist/shell-quote/index.js +1 -0
  205. package/vendor/dist/source-map/LICENSE +28 -0
  206. package/vendor/dist/source-map/index.js +1 -0
  207. package/vendor/dist/source-map/lib/util/LICENSE +28 -0
  208. package/vendor/dist/source-map/lib/util/index.js +1 -0
  209. package/vendor/dist/source-map/mappings.wasm +0 -0
  210. package/vendor/dist/tlhunter-sorted-set/LICENSE +21 -0
  211. package/vendor/dist/tlhunter-sorted-set/index.js +1 -0
  212. package/vendor/dist/ttl-set/LICENSE +21 -0
  213. 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
+ }
@@ -410,6 +410,11 @@ class LLMObs extends NoopLLMObs {
410
410
  }
411
411
  }
412
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
+
413
418
  const payload = {
414
419
  span_id: spanId,
415
420
  trace_id: traceId,
@@ -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'
@@ -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
@@ -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
@@ -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') {
@@ -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,8 +1,9 @@
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
8
9
  * @typedef {import('@opentelemetry/api-logs').LoggerProvider} LoggerProvider
@@ -11,7 +11,7 @@ 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
@@ -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,7 +40,7 @@ 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
@@ -79,12 +79,12 @@ class OtlpTransformer extends OtlpTransformerBase {
79
79
 
80
80
  const logsData = {
81
81
  resourceLogs: [{
82
- resource: this._transformResource(),
82
+ resource: this.transformResource(),
83
83
  scopeLogs: this.#transformScope(logRecords),
84
84
  }]
85
85
  }
86
86
 
87
- return this._serializeToProtobuf(protoLogsService, logsData)
87
+ return this.serializeToProtobuf(protoLogsService, logsData)
88
88
  }
89
89
 
90
90
  /**
@@ -95,11 +95,11 @@ class OtlpTransformer extends OtlpTransformerBase {
95
95
  #transformToJson (logRecords) {
96
96
  const logsData = {
97
97
  resourceLogs: [{
98
- resource: this._transformResource(),
98
+ resource: this.transformResource(),
99
99
  scopeLogs: this.#transformScope(logRecords)
100
100
  }]
101
101
  }
102
- return this._serializeToJson(logsData)
102
+ return this.serializeToJson(logsData)
103
103
  }
104
104
 
105
105
  /**
@@ -108,7 +108,7 @@ class OtlpTransformer extends OtlpTransformerBase {
108
108
  * @returns {Object[]} Array of scope log objects
109
109
  */
110
110
  #transformScope (logRecords) {
111
- const groupedRecords = this._groupByInstrumentationScope(logRecords)
111
+ const groupedRecords = this.groupByInstrumentationScope(logRecords)
112
112
  const scopeLogs = []
113
113
 
114
114
  for (const records of groupedRecords.values()) {
@@ -155,7 +155,7 @@ class OtlpTransformer extends OtlpTransformerBase {
155
155
  }
156
156
 
157
157
  if (logRecord.attributes) {
158
- result.attributes = this._transformAttributes(logRecord.attributes)
158
+ result.attributes = this.transformAttributes(logRecord.attributes)
159
159
  }
160
160
 
161
161
  if (spanContext?.traceFlags !== undefined) {
@@ -232,7 +232,7 @@ class OtlpTransformer extends OtlpTransformerBase {
232
232
  kvlistValue: {
233
233
  values: Object.entries(body).map(([key, value]) => ({
234
234
  key,
235
- value: this._transformAnyValue(value)
235
+ value: this.transformAnyValue(value)
236
236
  }))
237
237
  }
238
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
+ }
@@ -0,0 +1,81 @@
1
+ 'use strict'
2
+
3
+ const os = require('os')
4
+
5
+ /**
6
+ * @typedef {import('../../config')} Config
7
+ */
8
+
9
+ /**
10
+ * @file OpenTelemetry Metrics Implementation for dd-trace-js
11
+ *
12
+ * This package provides a custom OpenTelemetry Metrics implementation that integrates
13
+ * with the Datadog tracing library. It includes all necessary components for
14
+ * creating instruments, recording measurements, and exporting metrics via OTLP.
15
+ *
16
+ * Key Components:
17
+ * - MeterProvider: Main entry point for creating meters
18
+ * - Meter: Provides methods to create metric instruments
19
+ * - Instruments: Gauge, Counter, UpDownCounter, ObservableGauge, ObservableCounter, ObservableUpDownCounter, Histogram
20
+ * - PeriodicMetricReader: Collects and exports instruments (metrics) at regular intervals
21
+ * - OtlpHttpMetricExporter: Exports instruments (metrics) via OTLP over HTTP
22
+ * - OtlpTransformer: Transforms instruments (metrics) to OTLP format
23
+ *
24
+ * This is a custom implementation to avoid pulling in the full OpenTelemetry SDK,
25
+ * based on OTLP Protocol v1.7.0. It supports both protobuf and JSON serialization
26
+ * formats and integrates with Datadog's configuration system.
27
+ *
28
+ * @package
29
+ */
30
+
31
+ const { metrics } = require('@opentelemetry/api')
32
+ const MeterProvider = require('./meter_provider')
33
+ const PeriodicMetricReader = require('./periodic_metric_reader')
34
+ const OtlpHttpMetricExporter = require('./otlp_http_metric_exporter')
35
+
36
+ /**
37
+ * Initializes OpenTelemetry Metrics support
38
+ * @param {Config} config - Tracer configuration instance
39
+ */
40
+ function initializeOpenTelemetryMetrics (config) {
41
+ const resourceAttributes = {
42
+ 'service.name': config.service,
43
+ 'service.version': config.version,
44
+ 'deployment.environment': config.env
45
+ }
46
+
47
+ if (config.tags) {
48
+ const filteredTags = { ...config.tags }
49
+ delete filteredTags.service
50
+ delete filteredTags.version
51
+ delete filteredTags.env
52
+ Object.assign(resourceAttributes, filteredTags)
53
+ }
54
+
55
+ if (config.reportHostname) {
56
+ resourceAttributes['host.name'] = os.hostname()
57
+ }
58
+
59
+ const exporter = new OtlpHttpMetricExporter(
60
+ config.otelMetricsUrl,
61
+ config.otelMetricsHeaders,
62
+ config.otelMetricsTimeout,
63
+ config.otelMetricsProtocol,
64
+ resourceAttributes
65
+ )
66
+
67
+ const reader = new PeriodicMetricReader(
68
+ exporter,
69
+ config.otelMetricsExportInterval,
70
+ config.otelMetricsTemporalityPreference,
71
+ config.otelMaxQueueSize
72
+ )
73
+
74
+ const meterProvider = new MeterProvider({ reader })
75
+ metrics.setGlobalMeterProvider(meterProvider)
76
+ }
77
+
78
+ module.exports = {
79
+ MeterProvider,
80
+ initializeOpenTelemetryMetrics
81
+ }
@@ -0,0 +1,225 @@
1
+ 'use strict'
2
+
3
+ const { sanitizeAttributes } = require('../../../../../vendor/dist/@opentelemetry/core')
4
+ const { METRIC_TYPES } = require('./constants')
5
+
6
+ /**
7
+ * @typedef {import('@opentelemetry/api').Attributes} Attributes
8
+ * @typedef {import('@opentelemetry/core').InstrumentationScope} InstrumentationScope
9
+ */
10
+
11
+ /**
12
+ * @typedef {Object} Measurement
13
+ * @property {string} name - Instrument name
14
+ * @property {string} description - Instrument description
15
+ * @property {string} unit - Measurement unit
16
+ * @property {InstrumentationScope} instrumentationScope - Instrumentation scope
17
+ * @property {string} type - Metric type from METRIC_TYPES
18
+ * @property {number} value - Measured value
19
+ * @property {Attributes} attributes - Sanitized metric attributes
20
+ * @property {number} timestamp - Timestamp in nanoseconds
21
+ */
22
+
23
+ /**
24
+ * Base class for all metric instruments.
25
+ *
26
+ *
27
+ */
28
+ class Instrument {
29
+ /**
30
+ * Creates a new instrument instance.
31
+ *
32
+ * @param {string} name - Instrument name (e.g., 'http.request.duration')
33
+ * @param {Object} options - Instrument configuration options
34
+ * @param {string} [options.description] - Human-readable description of the instrument
35
+ * @param {string} [options.unit] - Unit of measurement (e.g., 'ms', 'bytes', '1')
36
+ * @param {InstrumentationScope} instrumentationScope - Instrumentation scope for this instrument
37
+ * @param {Object} reader - Metric reader for recording measurements
38
+ */
39
+ constructor (name, options, instrumentationScope, reader) {
40
+ this.name = name
41
+ this.description = options.description ?? ''
42
+ this.unit = options.unit ?? ''
43
+ this.valueType = options.valueType // currently ignored, TODO: add support for ValueType
44
+ this.advice = options.advice // currently ignored, TODO: add support for MetricAdvice
45
+ this.instrumentationScope = instrumentationScope
46
+ this.reader = reader
47
+ }
48
+
49
+ /**
50
+ * Creates a measurement object for recording metric values.
51
+ * @param {string} type - Metric type from METRIC_TYPES
52
+ * @param {number} value - Numeric value to record
53
+ * @param {Attributes} attributes - Key-value pairs for metric dimensions
54
+ * @returns {Measurement} Measurement object with metadata and timestamp
55
+ */
56
+ createMeasurement (type, value, attributes) {
57
+ return {
58
+ name: this.name,
59
+ description: this.description,
60
+ unit: this.unit,
61
+ instrumentationScope: this.instrumentationScope,
62
+ type,
63
+ value,
64
+ attributes: sanitizeAttributes(attributes),
65
+ timestamp: Number(process.hrtime.bigint())
66
+ }
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Implementation of the OpenTelemetry Counter interface:
72
+ * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.Counter.html
73
+ * @class Counter
74
+ */
75
+ class Counter extends Instrument {
76
+ add (value, attributes = {}) {
77
+ if (value < 0) return
78
+ this.reader?.record(this.createMeasurement(METRIC_TYPES.COUNTER, value, attributes))
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Implementation of the OpenTelemetry UpDownCounter interface:
84
+ * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.UpDownCounter.html
85
+ * @class UpDownCounter
86
+ */
87
+ class UpDownCounter extends Instrument {
88
+ add (value, attributes = {}) {
89
+ this.reader?.record(this.createMeasurement(METRIC_TYPES.UPDOWNCOUNTER, value, attributes))
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Implementation of the OpenTelemetry Histogram interface:
95
+ * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.Histogram.html
96
+ * @class Histogram
97
+ */
98
+ class Histogram extends Instrument {
99
+ record (value, attributes = {}) {
100
+ if (value < 0) return
101
+ this.reader?.record(this.createMeasurement(METRIC_TYPES.HISTOGRAM, value, attributes))
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Implementation of the OpenTelemetry Gauge interface:
107
+ * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.Gauge.html
108
+ * @class Gauge
109
+ */
110
+ class Gauge extends Instrument {
111
+ record (value, attributes = {}) {
112
+ this.reader?.record(this.createMeasurement(METRIC_TYPES.GAUGE, value, attributes))
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Base class for observable (asynchronous) instruments.
118
+ * Implementation of the OpenTelemetry Observable interface:
119
+ * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.Observable.html
120
+ *
121
+ */
122
+ class ObservableInstrument extends Instrument {
123
+ #callbacks = []
124
+ #type
125
+
126
+ constructor (name, options, instrumentationScope, reader, type) {
127
+ super(name, options, instrumentationScope, reader)
128
+ this.#type = type
129
+ }
130
+
131
+ /**
132
+ * Adds a callback to invoke during metric collection.
133
+ *
134
+ * @param {Function} callback - Receives an ObservableResult to record observations
135
+ */
136
+ addCallback (callback) {
137
+ if (typeof callback !== 'function') return
138
+ this.#callbacks.push(callback)
139
+ this.reader?.observableInstruments.add(this)
140
+ }
141
+
142
+ /**
143
+ * Removes a callback.
144
+ *
145
+ * @param {Function} callback - The callback to remove
146
+ */
147
+ removeCallback (callback) {
148
+ const index = this.#callbacks.indexOf(callback)
149
+ if (index !== -1) {
150
+ this.#callbacks.splice(index, 1)
151
+ if (this.#callbacks.length === 0) {
152
+ // Remove instrument from collection when no callbacks remain
153
+ this.reader?.observableInstruments.delete(this)
154
+ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Collects observations from all callbacks. Errors are silently ignored.
160
+ *
161
+ * @returns {Array<Measurement>} Array of measurements
162
+ */
163
+ collect () {
164
+ const observations = []
165
+ const observableResult = {
166
+ observe: (value, attributes = {}) => {
167
+ observations.push(this.createMeasurement(this.#type, value, attributes))
168
+ }
169
+ }
170
+
171
+ for (const callback of this.#callbacks) {
172
+ try {
173
+ callback(observableResult)
174
+ } catch {
175
+ // Ignore callback errors per OpenTelemetry spec to prevent disruption
176
+ // Errors are swallowed as callbacks should not break metric collection
177
+ }
178
+ }
179
+
180
+ return observations
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Implementation of the OpenTelemetry ObservableGauge interface:
186
+ * https://open-telemetry.github.io/opentelemetry-js/types/_opentelemetry_api._opentelemetry_api.ObservableGauge.html
187
+ * @class ObservableGauge
188
+ */
189
+ class ObservableGauge extends ObservableInstrument {
190
+ constructor (name, options, instrumentationScope, reader) {
191
+ super(name, options, instrumentationScope, reader, METRIC_TYPES.GAUGE)
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Implementation of the OpenTelemetry ObservableCounter interface:
197
+ * https://open-telemetry.github.io/opentelemetry-js/types/_opentelemetry_api._opentelemetry_api.ObservableCounter.html
198
+ * @class ObservableCounter
199
+ */
200
+ class ObservableCounter extends ObservableInstrument {
201
+ constructor (name, options, instrumentationScope, reader) {
202
+ super(name, options, instrumentationScope, reader, METRIC_TYPES.OBSERVABLECOUNTER)
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Implementation of the OpenTelemetry ObservableUpDownCounter interface:
208
+ * https://open-telemetry.github.io/opentelemetry-js/types/_opentelemetry_api._opentelemetry_api.ObservableUpDownCounter.html
209
+ * @class ObservableUpDownCounter
210
+ */
211
+ class ObservableUpDownCounter extends ObservableInstrument {
212
+ constructor (name, options, instrumentationScope, reader) {
213
+ super(name, options, instrumentationScope, reader, METRIC_TYPES.OBSERVABLEUPDOWNCOUNTER)
214
+ }
215
+ }
216
+
217
+ module.exports = {
218
+ Counter,
219
+ UpDownCounter,
220
+ Histogram,
221
+ Gauge,
222
+ ObservableGauge,
223
+ ObservableCounter,
224
+ ObservableUpDownCounter
225
+ }