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,532 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ METRIC_TYPES, TEMPORALITY, DEFAULT_HISTOGRAM_BUCKETS, DEFAULT_MAX_MEASUREMENT_QUEUE_SIZE
5
+ } = require('./constants')
6
+ const log = require('../../log')
7
+ const { stableStringify } = require('../otlp/otlp_transformer_base')
8
+
9
+ /**
10
+ * @typedef {import('@opentelemetry/api').Attributes} Attributes
11
+ * @typedef {import('@opentelemetry/core').InstrumentationScope} InstrumentationScope
12
+ * @typedef {import('./instruments').Measurement} Measurement
13
+ */
14
+
15
+ /**
16
+ * @typedef {Object} NumberDataPoint
17
+ * @property {Attributes} attributes - Number data point metric attributes
18
+ * @property {string} attrKey - Stable stringified key for attributes
19
+ * @property {number} timeUnixNano - Timestamp in nanoseconds
20
+ * @property {number} startTimeUnixNano - Start timestamp for cumulative metrics
21
+ * @property {number} value - Metric value
22
+ */
23
+
24
+ /**
25
+ * @typedef {Object} HistogramDataPoint
26
+ * @property {Attributes} attributes - Histogram data point metric attributes
27
+ * @property {string} attrKey - Stable stringified key for attributes
28
+ * @property {number} timeUnixNano - Timestamp in nanoseconds
29
+ * @property {number} startTimeUnixNano - Start timestamp
30
+ * @property {number} count - Number of observations
31
+ * @property {number} sum - Sum of all observations
32
+ * @property {number} min - Minimum value observed
33
+ * @property {number} max - Maximum value observed
34
+ * @property {number[]} bucketCounts - Count per histogram bucket
35
+ * @property {number[]} explicitBounds - Histogram bucket boundaries
36
+ */
37
+
38
+ /**
39
+ * @typedef {Object} AggregatedMetricDataPoint
40
+ * @property {Attributes} attributes - Aggregated metric data point metric attributes
41
+ * @property {string} attrKey - Stable stringified key for attributes
42
+ * @property {number} timeUnixNano - Timestamp in nanoseconds
43
+ * @property {number} startTimeUnixNano - Start timestamp
44
+ * @property {number} count - Number of observations
45
+ * @property {number} sum - Sum of all observations
46
+ * @property {number} min - Minimum value observed
47
+ * @property {number} max - Maximum value observed
48
+ * @property {number[]} bucketCounts - Count per histogram bucket
49
+ * @property {number[]} explicitBounds - Histogram bucket boundaries
50
+ */
51
+
52
+ /**
53
+ * @typedef {Object} AggregatedMetric
54
+ * @property {string} name - Metric name
55
+ * @property {string} description - Metric description
56
+ * @property {string} unit - Metric unit
57
+ * @property {string} type - Metric type from METRIC_TYPES
58
+ * @property {InstrumentationScope} instrumentationScope - Instrumentation scope
59
+ * @property {string} temporality - Temporality from TEMPORALITY constants
60
+ * @property {Map<string, AggregatedMetricDataPoint>} dataPointMap - Map of attribute keys to data points
61
+ */
62
+
63
+ /**
64
+ * PeriodicMetricReader collects and exports metrics at a regular interval.
65
+ *
66
+ * This implementation follows the OpenTelemetry JavaScript SDK MetricReader pattern:
67
+ * https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk-metrics.PeriodicExportingMetricReader.html
68
+ *
69
+ * @class PeriodicMetricReader
70
+ */
71
+ class PeriodicMetricReader {
72
+ #measurements = []
73
+ #cumulativeState = new Map()
74
+ #lastExportedState = new Map()
75
+ #droppedCount = 0
76
+ #timer = null
77
+ #isShutdown = false
78
+ #exportInterval
79
+ #aggregator
80
+
81
+ /**
82
+ * Creates a new PeriodicMetricReader instance.
83
+ *
84
+ * @param {OtlpHttpMetricExporter} exporter - Metric exporter for sending to Datadog Agent
85
+ * @param {number} exportInterval - Export interval in milliseconds
86
+ * @param {string} temporalityPreference - Temporality preference: DELTA, CUMULATIVE, or LOWMEMORY
87
+ * @param {number} maxBatchedQueueSize - Maximum number of measurements to queue before dropping
88
+ */
89
+ constructor (exporter, exportInterval, temporalityPreference, maxBatchedQueueSize) {
90
+ this.exporter = exporter
91
+ this.observableInstruments = new Set()
92
+ this.#exportInterval = exportInterval
93
+ this.#aggregator = new MetricAggregator(temporalityPreference, maxBatchedQueueSize)
94
+ this.#startTimer()
95
+ }
96
+
97
+ /**
98
+ * Records a measurement from a synchronous instrument.
99
+ *
100
+ * @param {Measurement} measurement - The measurement data
101
+ */
102
+ record (measurement) {
103
+ if (this.#measurements.length >= DEFAULT_MAX_MEASUREMENT_QUEUE_SIZE || this.#isShutdown) {
104
+ this.#droppedCount++
105
+ return
106
+ }
107
+ this.#measurements.push(measurement)
108
+ }
109
+
110
+ /**
111
+ * Forces an immediate collection and export of all metrics.
112
+ * @returns {void}
113
+ */
114
+ forceFlush () {
115
+ if (this.#isShutdown) {
116
+ log.warn(`PeriodicMetricReader is shutdown. ${this.#droppedCount} measurement(s) were dropped`)
117
+ return
118
+ }
119
+ this.#collectAndExport()
120
+ }
121
+
122
+ /**
123
+ * Shuts down the reader and stops periodic collection.
124
+ * @returns {void}
125
+ */
126
+ shutdown () {
127
+ if (this.#isShutdown) {
128
+ log.warn('PeriodicMetricReader is already shutdown')
129
+ return
130
+ }
131
+ this.#isShutdown = true
132
+ this.#clearTimer()
133
+ this.forceFlush()
134
+ }
135
+
136
+ /**
137
+ * Starts the periodic export timer.
138
+ *
139
+ */
140
+ #startTimer () {
141
+ if (this.#timer) return
142
+
143
+ this.#timer = setInterval(() => {
144
+ this.#collectAndExport()
145
+ }, this.#exportInterval).unref()
146
+ }
147
+
148
+ /**
149
+ * Clears the periodic export timer.
150
+ *
151
+ */
152
+ #clearTimer () {
153
+ if (this.#timer) {
154
+ clearInterval(this.#timer)
155
+ this.#timer = null
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Collects measurements and exports metrics.
161
+ *
162
+ * @param {Function} [callback] - Called after export completes
163
+ */
164
+ #collectAndExport (callback = () => {}) {
165
+ // Atomically drain measurements for export. New measurements can be recorded
166
+ // during export without interfering with this batch.
167
+ const allMeasurements = this.#measurements.splice(0)
168
+
169
+ for (const instrument of this.observableInstruments) {
170
+ const observableMeasurements = instrument.collect()
171
+
172
+ if (allMeasurements.length >= DEFAULT_MAX_MEASUREMENT_QUEUE_SIZE) {
173
+ this.#droppedCount += observableMeasurements.length
174
+ continue
175
+ }
176
+
177
+ const remainingCapacity = DEFAULT_MAX_MEASUREMENT_QUEUE_SIZE - allMeasurements.length
178
+
179
+ if (observableMeasurements.length <= remainingCapacity) {
180
+ allMeasurements.push(...observableMeasurements)
181
+ } else {
182
+ allMeasurements.push(...observableMeasurements.slice(0, remainingCapacity))
183
+ this.#droppedCount += observableMeasurements.length - remainingCapacity
184
+ }
185
+ }
186
+
187
+ if (this.#droppedCount > 0) {
188
+ log.warn(
189
+ `Metric queue exceeded limit (max: ${DEFAULT_MAX_MEASUREMENT_QUEUE_SIZE}). ` +
190
+ `Dropping ${this.#droppedCount} measurements. `
191
+ )
192
+ this.#droppedCount = 0
193
+ }
194
+
195
+ if (allMeasurements.length === 0) {
196
+ callback()
197
+ return
198
+ }
199
+
200
+ const metrics = this.#aggregator.aggregate(
201
+ allMeasurements,
202
+ this.#cumulativeState,
203
+ this.#lastExportedState
204
+ )
205
+
206
+ this.exporter.export(metrics, callback)
207
+ }
208
+ }
209
+
210
+ /**
211
+ * MetricAggregator aggregates individual measurements into metric data points.
212
+ *
213
+ */
214
+ class MetricAggregator {
215
+ #startTime = Number(process.hrtime.bigint())
216
+ #temporalityPreference
217
+ #maxBatchedQueueSize
218
+
219
+ constructor (temporalityPreference, maxBatchedQueueSize) {
220
+ this.#temporalityPreference = temporalityPreference
221
+ this.#maxBatchedQueueSize = maxBatchedQueueSize
222
+ }
223
+
224
+ /**
225
+ * Gets the temporality for a given metric type.
226
+ *
227
+ * @param {string} type - Metric type from METRIC_TYPES
228
+ * @returns {string} Temporality from TEMPORALITY
229
+ */
230
+ #getTemporality (type) {
231
+ // UpDownCounter and Observable UpDownCounter always use CUMULATIVE
232
+ if (type === METRIC_TYPES.UPDOWNCOUNTER || type === METRIC_TYPES.OBSERVABLEUPDOWNCOUNTER) {
233
+ return TEMPORALITY.CUMULATIVE
234
+ }
235
+
236
+ // Gauge always uses last-value aggregation
237
+ if (type === METRIC_TYPES.GAUGE) {
238
+ return TEMPORALITY.GAUGE
239
+ }
240
+
241
+ switch (this.#temporalityPreference) {
242
+ case TEMPORALITY.CUMULATIVE:
243
+ return TEMPORALITY.CUMULATIVE
244
+ case TEMPORALITY.LOWMEMORY:
245
+ // LOWMEMORY: only synchronous Counter and Histogram use DELTA, Observable Counter uses CUMULATIVE
246
+ return (type === METRIC_TYPES.COUNTER || type === METRIC_TYPES.HISTOGRAM)
247
+ ? TEMPORALITY.DELTA
248
+ : TEMPORALITY.CUMULATIVE
249
+ default:
250
+ return TEMPORALITY.DELTA
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Aggregates measurements into metrics.
256
+ *
257
+ * @param {Measurement[]} measurements - The measurements to aggregate
258
+ * @param {Map<string, any>} cumulativeState - The cumulative state of the metrics
259
+ * @param {Map<string, any>} lastExportedState - The last exported state of the metrics
260
+ * @returns {Iterable<AggregatedMetric>} The aggregated metrics
261
+ */
262
+ aggregate (measurements, cumulativeState, lastExportedState) {
263
+ const metricsMap = new Map()
264
+
265
+ for (const measurement of measurements) {
266
+ const {
267
+ name,
268
+ description,
269
+ unit,
270
+ type,
271
+ instrumentationScope,
272
+ value,
273
+ attributes,
274
+ timestamp
275
+ } = measurement
276
+
277
+ const scopeKey = this.#getScopeKey(instrumentationScope)
278
+ const metricKey = `${scopeKey}:${name}:${type}`
279
+ const attrKey = stableStringify(attributes)
280
+ const stateKey = this.#getStateKey(scopeKey, name, type, attrKey)
281
+
282
+ let metric = metricsMap.get(metricKey)
283
+ if (!metric) {
284
+ if (metricsMap.size >= this.#maxBatchedQueueSize) {
285
+ log.warn(
286
+ `Metric queue exceeded limit (max: ${this.#maxBatchedQueueSize}). ` +
287
+ `Dropping metric: ${metricKey}, value: ${value}. ` +
288
+ 'Consider increasing OTEL_BSP_MAX_QUEUE_SIZE or decreasing OTEL_METRIC_EXPORT_INTERVAL.'
289
+ )
290
+ continue
291
+ }
292
+ metric = {
293
+ name,
294
+ description,
295
+ unit,
296
+ type,
297
+ instrumentationScope,
298
+ temporality: this.#getTemporality(type),
299
+ dataPointMap: new Map()
300
+ }
301
+ metricsMap.set(metricKey, metric)
302
+ }
303
+
304
+ if (type === METRIC_TYPES.COUNTER || type === METRIC_TYPES.UPDOWNCOUNTER) {
305
+ this.#aggregateSum(metric, value, attributes, attrKey, timestamp, stateKey, cumulativeState)
306
+ } else if (type === METRIC_TYPES.HISTOGRAM) {
307
+ this.#aggregateHistogram(metric, value, attributes, attrKey, timestamp, stateKey, cumulativeState)
308
+ } else {
309
+ this.#aggregateLastValue(metric, value, attributes, attrKey, timestamp)
310
+ }
311
+ }
312
+
313
+ this.#applyDeltaTemporality(metricsMap, lastExportedState)
314
+ return metricsMap
315
+ }
316
+
317
+ /**
318
+ * Gets unique identifier for a given instrumentation scope.
319
+ *
320
+ * @param {InstrumentationScope} instrumentationScope - The instrumentation scope
321
+ * @returns {string} - The scope identifier
322
+ */
323
+ #getScopeKey (instrumentationScope) {
324
+ return `${instrumentationScope.name}@${instrumentationScope.version}@${instrumentationScope.schemaUrl}`
325
+ }
326
+
327
+ /**
328
+ * Gets unique identifier for a given metric.
329
+ *
330
+ * @param {string} scopeKey - The scope identifier
331
+ * @param {string} name - The metric name
332
+ * @param {string} type - The metric type from METRIC_TYPES
333
+ * @param {string} attrKey - The attribute key
334
+ * @returns {string} - The metric identifier
335
+ */
336
+ #getStateKey (scopeKey, name, type, attrKey) {
337
+ return `${scopeKey}:${name}:${type}:${attrKey}`
338
+ }
339
+
340
+ /**
341
+ * Checks if a given metric type is a delta type.
342
+ *
343
+ * @param {string} type - The metric type from METRIC_TYPES
344
+ * @returns {boolean} - True if the metric type is a delta type
345
+ */
346
+ #isDeltaType (type) {
347
+ return type === METRIC_TYPES.COUNTER ||
348
+ type === METRIC_TYPES.OBSERVABLECOUNTER ||
349
+ type === METRIC_TYPES.HISTOGRAM
350
+ }
351
+
352
+ /**
353
+ * Applies delta temporality to the metrics.
354
+ *
355
+ * @param {Iterable<AggregatedMetric>} metrics - The metrics to apply delta temporality to
356
+ * @param {Map<string, any>} lastExportedState - The last exported state of the metrics
357
+ * @returns {void}
358
+ */
359
+ #applyDeltaTemporality (metrics, lastExportedState) {
360
+ for (const metric of metrics) {
361
+ if (metric.temporality === TEMPORALITY.DELTA && this.#isDeltaType(metric.type)) {
362
+ const scopeKey = this.#getScopeKey(metric.instrumentationScope)
363
+
364
+ for (const dataPoint of metric.dataPointMap.values()) {
365
+ const stateKey = this.#getStateKey(scopeKey, metric.name, metric.type, dataPoint.attrKey)
366
+
367
+ if (metric.type === METRIC_TYPES.COUNTER || metric.type === METRIC_TYPES.OBSERVABLECOUNTER) {
368
+ const lastValue = lastExportedState.get(stateKey) || 0
369
+ const currentValue = dataPoint.value
370
+ dataPoint.value = currentValue - lastValue
371
+ lastExportedState.set(stateKey, currentValue)
372
+ } else if (metric.type === METRIC_TYPES.HISTOGRAM) {
373
+ const lastState = lastExportedState.get(stateKey) || {
374
+ count: 0,
375
+ sum: 0,
376
+ bucketCounts: new Array(dataPoint.bucketCounts.length).fill(0)
377
+ }
378
+ const currentState = {
379
+ count: dataPoint.count,
380
+ sum: dataPoint.sum,
381
+ min: dataPoint.min,
382
+ max: dataPoint.max,
383
+ bucketCounts: [...dataPoint.bucketCounts]
384
+ }
385
+ dataPoint.count = currentState.count - lastState.count
386
+ dataPoint.sum = currentState.sum - lastState.sum
387
+ dataPoint.bucketCounts = currentState.bucketCounts.map(
388
+ (count, idx) => count - (lastState.bucketCounts[idx] || 0)
389
+ )
390
+ lastExportedState.set(stateKey, currentState)
391
+ }
392
+ }
393
+ }
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Finds or creates a data point for a given metric.
399
+ *
400
+ * @param {AggregatedMetric} metric - The metric to find or create a data point for
401
+ * @param {Attributes} attributes - The attributes of the metric
402
+ * @param {string} attrKey - The attribute key
403
+ * @param {Function} createInitialDataPoint - Function to create an initial data point
404
+ * @returns {NumberDataPoint|HistogramDataPoint} - The data point
405
+ */
406
+ #findOrCreateDataPoint (metric, attributes, attrKey, createInitialDataPoint) {
407
+ let dataPoint = metric.dataPointMap.get(attrKey)
408
+
409
+ if (!dataPoint) {
410
+ dataPoint = { attributes, attrKey, ...createInitialDataPoint() }
411
+ metric.dataPointMap.set(attrKey, dataPoint)
412
+ }
413
+
414
+ return dataPoint
415
+ }
416
+
417
+ /**
418
+ * Records the sum of all values for a given metric.
419
+ * Creates a new data point if it doesn't exist.
420
+ *
421
+ * @param {AggregatedMetric} metric - The metric to aggregate a sum for
422
+ * @param {number} value - The value to aggregate
423
+ * @param {Attributes} attributes - The attributes of the metric
424
+ * @param {string} attrKey - The attribute key
425
+ * @param {number} timestamp - The timestamp of the measurement
426
+ * @param {string} stateKey - The state key
427
+ * @param {Map<string, any>} cumulativeState - The cumulative state of the metrics
428
+ */
429
+ #aggregateSum (metric, value, attributes, attrKey, timestamp, stateKey, cumulativeState) {
430
+ if (!cumulativeState.has(stateKey)) {
431
+ cumulativeState.set(stateKey, {
432
+ value: 0,
433
+ startTime: metric.temporality === TEMPORALITY.CUMULATIVE ? this.#startTime : timestamp
434
+ })
435
+ }
436
+
437
+ const state = cumulativeState.get(stateKey)
438
+ state.value += value
439
+
440
+ const dataPoint = this.#findOrCreateDataPoint(metric, attributes, attrKey, () => ({
441
+ startTimeUnixNano: state.startTime,
442
+ timeUnixNano: timestamp,
443
+ value: 0
444
+ }))
445
+
446
+ dataPoint.value = state.value
447
+ dataPoint.timeUnixNano = timestamp
448
+ }
449
+
450
+ /**
451
+ * Overwrites the last recorded value for a given metric or
452
+ * creates a new data point if it doesn't exist.
453
+ *
454
+ * @param {AggregatedMetric} metric - The metric to aggregate a last value for
455
+ * @param {number} value - The value to aggregate
456
+ * @param {Attributes} attributes - The attributes of the metric
457
+ * @param {string} attrKey - The attribute key
458
+ * @param {number} timestamp - The timestamp of the measurement
459
+ */
460
+ #aggregateLastValue (metric, value, attributes, attrKey, timestamp) {
461
+ const dataPoint = this.#findOrCreateDataPoint(metric, attributes, attrKey, () => ({
462
+ timeUnixNano: timestamp,
463
+ value: 0
464
+ }))
465
+
466
+ dataPoint.value = value
467
+ dataPoint.timeUnixNano = timestamp
468
+ }
469
+
470
+ /**
471
+ * Aggregates histogram values by distributing them into buckets.
472
+ * Tracks count, sum, min, max, and per-bucket counts and creates
473
+ * a new data point if it doesn't exist.
474
+ *
475
+ * @param {AggregatedMetric} metric - The metric to aggregate a histogram for
476
+ * @param {number} value - The value to aggregate
477
+ * @param {Attributes} attributes - The attributes of the metric
478
+ * @param {string} attrKey - The attribute key
479
+ * @param {number} timestamp - The timestamp of the measurement
480
+ * @param {string} stateKey - The state key
481
+ * @param {Map<string, any>} cumulativeState - The cumulative state of the metrics
482
+ * @returns {void}
483
+ */
484
+ #aggregateHistogram (metric, value, attributes, attrKey, timestamp, stateKey, cumulativeState) {
485
+ if (!cumulativeState.has(stateKey)) {
486
+ cumulativeState.set(stateKey, {
487
+ count: 0,
488
+ sum: 0,
489
+ min: Infinity,
490
+ max: -Infinity,
491
+ bucketCounts: new Array(DEFAULT_HISTOGRAM_BUCKETS.length + 1).fill(0),
492
+ startTime: metric.temporality === TEMPORALITY.CUMULATIVE ? this.#startTime : timestamp
493
+ })
494
+ }
495
+
496
+ const state = cumulativeState.get(stateKey)
497
+
498
+ let bucketIndex = DEFAULT_HISTOGRAM_BUCKETS.length
499
+ for (let i = 0; i < DEFAULT_HISTOGRAM_BUCKETS.length; i++) {
500
+ if (value <= DEFAULT_HISTOGRAM_BUCKETS[i]) {
501
+ bucketIndex = i
502
+ break
503
+ }
504
+ }
505
+
506
+ state.bucketCounts[bucketIndex]++
507
+ state.count++
508
+ state.sum += value
509
+ state.min = Math.min(state.min, value)
510
+ state.max = Math.max(state.max, value)
511
+
512
+ const dataPoint = this.#findOrCreateDataPoint(metric, attributes, attrKey, () => ({
513
+ startTimeUnixNano: state.startTime,
514
+ timeUnixNano: timestamp,
515
+ count: 0,
516
+ sum: 0,
517
+ min: Infinity,
518
+ max: -Infinity,
519
+ bucketCounts: new Array(DEFAULT_HISTOGRAM_BUCKETS.length + 1).fill(0),
520
+ explicitBounds: DEFAULT_HISTOGRAM_BUCKETS
521
+ }))
522
+
523
+ dataPoint.count = state.count
524
+ dataPoint.sum = state.sum
525
+ dataPoint.min = state.min
526
+ dataPoint.max = state.max
527
+ dataPoint.bucketCounts = [...state.bucketCounts]
528
+ dataPoint.timeUnixNano = timestamp
529
+ }
530
+ }
531
+
532
+ module.exports = PeriodicMetricReader
@@ -10,14 +10,12 @@ const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
10
10
  /**
11
11
  * Base class for OTLP HTTP exporters.
12
12
  *
13
- * This implementation follows the OTLP HTTP specification:
13
+ * This implementation follows the OTLP HTTP v1.7.0 specification:
14
14
  * https://opentelemetry.io/docs/specs/otlp/#otlphttp
15
15
  *
16
16
  * @class OtlpHttpExporterBase
17
17
  */
18
18
  class OtlpHttpExporterBase {
19
- #telemetryTags
20
-
21
19
  /**
22
20
  * Creates a new OtlpHttpExporterBase instance.
23
21
  *
@@ -49,31 +47,25 @@ class OtlpHttpExporterBase {
49
47
  ...this.#parseAdditionalHeaders(headers)
50
48
  }
51
49
  }
52
- this.#telemetryTags = [
50
+ this.telemetryTags = [
53
51
  'protocol:http',
54
52
  `encoding:${isJson ? 'json' : 'protobuf'}`
55
53
  ]
56
54
  }
57
55
 
58
- /**
59
- * Gets the telemetry tags for this exporter.
60
- * @returns {Array<string>} Telemetry tags
61
- * @protected
62
- */
63
- _getTelemetryTags () {
64
- return this.#telemetryTags
65
- }
66
-
67
56
  /**
68
57
  * Records telemetry metrics for exported data.
69
58
  * @param {string} metricName - Name of the metric to record
70
59
  * @param {number} count - Count to increment
71
- * @param {Array<string>} [tags] - Optional custom tags (defaults to this exporter's tags)
60
+ * @param {Array<string>} [additionalTags] - Optional custom tags (defaults to this exporter's tags)
72
61
  * @protected
73
62
  */
74
- _recordTelemetry (metricName, count, tags) {
75
- const telemetryTags = tags || this.#telemetryTags
76
- tracerMetrics.count(metricName, telemetryTags).inc(count)
63
+ recordTelemetry (metricName, count, additionalTags) {
64
+ if (additionalTags?.length > 0) {
65
+ tracerMetrics.count(metricName, [...this.telemetryTags, ...additionalTags || []]).inc(count)
66
+ } else {
67
+ tracerMetrics.count(metricName, this.telemetryTags).inc(count)
68
+ }
77
69
  }
78
70
 
79
71
  /**
@@ -82,7 +74,7 @@ class OtlpHttpExporterBase {
82
74
  * @param {Function} resultCallback - Callback for the result
83
75
  * @protected
84
76
  */
85
- _sendPayload (payload, resultCallback) {
77
+ sendPayload (payload, resultCallback) {
86
78
  const options = {
87
79
  ...this.options,
88
80
  headers: {