dd-trace 6.10.0 → 6.12.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.
- package/LICENSE-3rdparty.csv +5 -0
- package/README.md +0 -17
- package/index.d.ts +128 -6
- package/initialize.mjs +28 -17
- package/openfeature.js +2 -2
- package/package.json +27 -23
- package/packages/datadog-esbuild/index.js +0 -34
- package/packages/datadog-instrumentations/src/anthropic.js +210 -15
- package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
- package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
- package/packages/datadog-instrumentations/src/bunyan.js +3 -16
- package/packages/datadog-instrumentations/src/cucumber.js +30 -15
- package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
- package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
- package/packages/datadog-instrumentations/src/cypress.js +2 -0
- package/packages/datadog-instrumentations/src/fastify.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +9 -4
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
- package/packages/datadog-instrumentations/src/jest.js +25 -17
- package/packages/datadog-instrumentations/src/mariadb.js +278 -21
- package/packages/datadog-instrumentations/src/mocha/main.js +539 -55
- package/packages/datadog-instrumentations/src/mocha/utils.js +135 -19
- package/packages/datadog-instrumentations/src/playwright-reporter.js +247 -0
- package/packages/datadog-instrumentations/src/playwright.js +182 -35
- package/packages/datadog-instrumentations/src/router.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +6 -8
- package/packages/datadog-instrumentations/src/vitest-main.js +11 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -8
- package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
- package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
- package/packages/datadog-plugin-bunyan/src/index.js +5 -8
- package/packages/datadog-plugin-cucumber/src/index.js +11 -0
- package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
- package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +81 -29
- package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
- package/packages/datadog-plugin-cypress/src/index.js +12 -6
- package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
- package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
- package/packages/datadog-plugin-electron/src/index.js +2 -0
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +6 -0
- package/packages/datadog-plugin-mocha/src/index.js +56 -1
- package/packages/datadog-plugin-nats/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/tracing.js +41 -30
- package/packages/datadog-plugin-openai-agents/src/integration.js +1 -0
- package/packages/datadog-plugin-playwright/src/index.js +46 -13
- package/packages/datadog-plugin-vitest/src/index.js +6 -0
- package/packages/datadog-webpack/index.js +0 -11
- package/packages/dd-trace/index.js +0 -1
- package/packages/dd-trace/src/agent/info.js +6 -5
- package/packages/dd-trace/src/aiguard/client.js +100 -1
- package/packages/dd-trace/src/aiguard/errors.js +41 -0
- package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
- package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
- package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
- package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
- package/packages/dd-trace/src/aiguard/sdk.js +22 -278
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
- package/packages/dd-trace/src/appsec/reporter.js +2 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
- package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
- package/packages/dd-trace/src/carrier.js +107 -48
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +29 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +27 -9
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +26 -8
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -4
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +10 -4
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/index.js +13 -2
- package/packages/dd-trace/src/config/major-overrides.js +7 -0
- package/packages/dd-trace/src/config/supported-configurations.json +14 -0
- package/packages/dd-trace/src/datastreams/index.js +2 -0
- package/packages/dd-trace/src/datastreams/manager.js +9 -2
- package/packages/dd-trace/src/datastreams/processor.js +15 -13
- package/packages/dd-trace/src/datastreams/size.js +5 -0
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -11
- package/packages/dd-trace/src/debugger/index.js +4 -2
- package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
- package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
- package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
- package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
- package/packages/dd-trace/src/evp_proxy/path.js +22 -0
- package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
- package/packages/dd-trace/src/exporters/common/request.js +5 -2
- package/packages/dd-trace/src/exporters/common/writer.js +11 -6
- package/packages/dd-trace/src/llmobs/experiments/client.js +167 -5
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +484 -92
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +302 -29
- package/packages/dd-trace/src/llmobs/experiments/index.js +75 -64
- package/packages/dd-trace/src/llmobs/experiments/noop.js +135 -7
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +101 -9
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
- package/packages/dd-trace/src/noop/proxy.js +3 -4
- package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
- package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
- package/packages/dd-trace/src/openfeature/index.js +7 -4
- package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
- package/packages/dd-trace/src/openfeature/writers/exposures.js +62 -14
- package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
- package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +22 -13
- package/packages/dd-trace/src/opentracing/tracer.js +5 -2
- package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
- package/packages/dd-trace/src/plugin_manager.js +3 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/jest.js +4 -3
- package/packages/dd-trace/src/plugins/util/test.js +9 -9
- package/packages/dd-trace/src/plugins/util/web.js +3 -0
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
- package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
- package/packages/dd-trace/src/proxy.js +38 -29
- package/packages/dd-trace/src/ritm.js +9 -2
- package/packages/dd-trace/src/serverless.js +37 -0
- package/packages/dd-trace/src/span_format.js +1 -0
- package/packages/dd-trace/src/span_stats.js +30 -3
- package/packages/dd-trace/src/storage-channels.js +86 -0
- package/packages/dd-trace/src/telemetry/metrics.js +62 -8
- package/packages/dd-trace/src/telemetry/send-data.js +1 -0
- package/packages/dd-trace/src/tracer.js +9 -2
- package/packages/dd-trace/src/tracer_metadata.js +14 -1
- package/packages/dd-trace/src/web-tags-cache.js +132 -0
- package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/https-proxy-agent/LICENSE +22 -0
- package/vendor/dist/https-proxy-agent/index.js +4 -0
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/proxy-from-env/LICENSE +20 -0
- package/vendor/dist/proxy-from-env/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
- package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
- package/packages/dd-trace/src/feature-registry.js +0 -29
- package/packages/dd-trace/src/openfeature/register.js +0 -35
- package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
|
@@ -1,23 +1,53 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const log = require('../../log')
|
|
4
|
+
const { ExternalExperiment } = require('./experiment')
|
|
5
|
+
|
|
6
|
+
const NOOP_EXPERIMENT_ID = '00000000-0000-0000-0000-000000000000'
|
|
7
|
+
const NOOP_SPAN_ID = '0000000000000000'
|
|
8
|
+
const NOOP_TRACE_ID = '00000000000000000000000000000000'
|
|
4
9
|
|
|
5
10
|
class NoopDataset {
|
|
6
11
|
#name
|
|
12
|
+
#description
|
|
7
13
|
#records
|
|
14
|
+
#filterTags
|
|
8
15
|
|
|
9
16
|
constructor (name = '', options = {}) {
|
|
10
17
|
this.#name = name
|
|
18
|
+
this.#description = typeof options === 'string' ? options : (options.description ?? '')
|
|
19
|
+
this.#filterTags = typeof options === 'string' ? [] : [...(options.filterTags ?? [])]
|
|
11
20
|
this.#records = (typeof options === 'string' ? [] : (options.records ?? [])).map(record => ({
|
|
12
21
|
id: record.id ?? null,
|
|
13
22
|
input: record.inputData,
|
|
14
23
|
expectedOutput: record.expectedOutput ?? null,
|
|
15
24
|
metadata: record.metadata ?? {},
|
|
25
|
+
tags: [...(record.tags ?? [])],
|
|
16
26
|
}))
|
|
17
27
|
}
|
|
18
28
|
|
|
19
|
-
addRecord (input, expectedOutput, metadata) {
|
|
20
|
-
this.#records.push({
|
|
29
|
+
addRecord (input, expectedOutput, metadata, tags) {
|
|
30
|
+
this.#records.push({
|
|
31
|
+
id: null,
|
|
32
|
+
input,
|
|
33
|
+
expectedOutput: expectedOutput ?? null,
|
|
34
|
+
metadata: metadata ?? {},
|
|
35
|
+
tags: [...(tags ?? [])],
|
|
36
|
+
})
|
|
37
|
+
return this
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
update (index, fields) {
|
|
41
|
+
const record = this.#records[index]
|
|
42
|
+
if (record == null) return this
|
|
43
|
+
if (Object.hasOwn(fields, 'input')) record.input = fields.input
|
|
44
|
+
if (Object.hasOwn(fields, 'expectedOutput')) record.expectedOutput = fields.expectedOutput ?? null
|
|
45
|
+
if (Object.hasOwn(fields, 'metadata')) record.metadata = fields.metadata ?? {}
|
|
46
|
+
return this
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
delete (index) {
|
|
50
|
+
this.#records.splice(index, 1)
|
|
21
51
|
return this
|
|
22
52
|
}
|
|
23
53
|
|
|
@@ -25,10 +55,57 @@ class NoopDataset {
|
|
|
25
55
|
return Promise.resolve({ pushedCount: 0, totalCount: 0 })
|
|
26
56
|
}
|
|
27
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Add tags to a dataset record.
|
|
60
|
+
* @param {number} index Dataset record index.
|
|
61
|
+
* @param {string[]} tags Tags in key:value format.
|
|
62
|
+
* @returns {NoopDataset} This dataset for chaining.
|
|
63
|
+
*/
|
|
64
|
+
addTags (index, tags) {
|
|
65
|
+
const record = this.#records[index]
|
|
66
|
+
if (!record) return this
|
|
67
|
+
const normalizedTags = tags ?? []
|
|
68
|
+
record.tags = [...new Set([...(record.tags ?? []), ...normalizedTags])].sort()
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Remove tags from a dataset record.
|
|
74
|
+
* @param {number} index Dataset record index.
|
|
75
|
+
* @param {string[]} tags Tags in key:value format.
|
|
76
|
+
* @returns {NoopDataset} This dataset for chaining.
|
|
77
|
+
*/
|
|
78
|
+
removeTags (index, tags) {
|
|
79
|
+
const record = this.#records[index]
|
|
80
|
+
if (!record) return this
|
|
81
|
+
const normalizedTags = tags ?? []
|
|
82
|
+
const removed = new Set(normalizedTags)
|
|
83
|
+
record.tags = (record.tags ?? []).filter(tag => !removed.has(tag)).sort()
|
|
84
|
+
return this
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Replace all tags on a dataset record.
|
|
89
|
+
* @param {number} index Dataset record index.
|
|
90
|
+
* @param {string[]} tags Tags in key:value format.
|
|
91
|
+
* @returns {NoopDataset} This dataset for chaining.
|
|
92
|
+
*/
|
|
93
|
+
replaceTags (index, tags) {
|
|
94
|
+
const record = this.#records[index]
|
|
95
|
+
if (!record) return this
|
|
96
|
+
const normalizedTags = tags ?? []
|
|
97
|
+
record.tags = [...normalizedTags]
|
|
98
|
+
return this
|
|
99
|
+
}
|
|
100
|
+
|
|
28
101
|
name () {
|
|
29
102
|
return this.#name
|
|
30
103
|
}
|
|
31
104
|
|
|
105
|
+
description () {
|
|
106
|
+
return this.#description
|
|
107
|
+
}
|
|
108
|
+
|
|
32
109
|
id () {
|
|
33
110
|
return null
|
|
34
111
|
}
|
|
@@ -45,6 +122,14 @@ class NoopDataset {
|
|
|
45
122
|
return null
|
|
46
123
|
}
|
|
47
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Return the tags used to filter this dataset.
|
|
127
|
+
* @returns {string[]} Dataset record filter tags.
|
|
128
|
+
*/
|
|
129
|
+
filterTags () {
|
|
130
|
+
return [...this.#filterTags]
|
|
131
|
+
}
|
|
132
|
+
|
|
48
133
|
records () {
|
|
49
134
|
return [...this.#records]
|
|
50
135
|
}
|
|
@@ -60,9 +145,11 @@ class NoopDataset {
|
|
|
60
145
|
|
|
61
146
|
class NoopExperiment {
|
|
62
147
|
#name
|
|
148
|
+
#external
|
|
63
149
|
|
|
64
|
-
constructor (name = '') {
|
|
150
|
+
constructor (name = '', external = false) {
|
|
65
151
|
this.#name = name
|
|
152
|
+
this.#external = external
|
|
66
153
|
}
|
|
67
154
|
|
|
68
155
|
name () {
|
|
@@ -70,7 +157,7 @@ class NoopExperiment {
|
|
|
70
157
|
}
|
|
71
158
|
|
|
72
159
|
experimentId () {
|
|
73
|
-
return null
|
|
160
|
+
return this.#external ? NOOP_EXPERIMENT_ID : null
|
|
74
161
|
}
|
|
75
162
|
|
|
76
163
|
url () {
|
|
@@ -80,6 +167,32 @@ class NoopExperiment {
|
|
|
80
167
|
run () {
|
|
81
168
|
return Promise.resolve({ experimentId: null, rows: [], url: null })
|
|
82
169
|
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @returns {Promise<{experimentId: string, spanId: string, traceId: string, url: null}>}
|
|
173
|
+
*/
|
|
174
|
+
submitSpan () {
|
|
175
|
+
return Promise.resolve({
|
|
176
|
+
experimentId: NOOP_EXPERIMENT_ID,
|
|
177
|
+
spanId: NOOP_SPAN_ID,
|
|
178
|
+
traceId: NOOP_TRACE_ID,
|
|
179
|
+
url: null,
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @returns {Promise<void>}
|
|
185
|
+
*/
|
|
186
|
+
submitEvaluationMetrics () {
|
|
187
|
+
return Promise.resolve()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @returns {Promise<void>}
|
|
192
|
+
*/
|
|
193
|
+
close () {
|
|
194
|
+
return Promise.resolve()
|
|
195
|
+
}
|
|
83
196
|
}
|
|
84
197
|
|
|
85
198
|
// No-op Experiments used when LLM Observability is disabled or the API/APP keys
|
|
@@ -87,9 +200,11 @@ class NoopExperiment {
|
|
|
87
200
|
// throwing, so intentionally disabled experiments remain graceful.
|
|
88
201
|
class NoopExperiments {
|
|
89
202
|
#reason
|
|
203
|
+
#startExperiment
|
|
90
204
|
|
|
91
|
-
constructor (reason) {
|
|
205
|
+
constructor (reason, options = {}) {
|
|
92
206
|
this.#reason = reason || 'LLMObs experiments are not available'
|
|
207
|
+
this.#startExperiment = options.startExperiment
|
|
93
208
|
}
|
|
94
209
|
|
|
95
210
|
#warn () {
|
|
@@ -101,15 +216,28 @@ class NoopExperiments {
|
|
|
101
216
|
return new NoopDataset(name, options)
|
|
102
217
|
}
|
|
103
218
|
|
|
104
|
-
pullDataset (name) {
|
|
219
|
+
pullDataset (name, options = {}) {
|
|
105
220
|
this.#warn()
|
|
106
|
-
return Promise.resolve(new NoopDataset(name))
|
|
221
|
+
return Promise.resolve(new NoopDataset(name, { filterTags: options.tags }))
|
|
107
222
|
}
|
|
108
223
|
|
|
109
224
|
experiment (options = {}) {
|
|
110
225
|
this.#warn()
|
|
111
226
|
return new NoopExperiment(options.name)
|
|
112
227
|
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {object} options
|
|
231
|
+
* @returns {Promise<ExternalExperiment>}
|
|
232
|
+
*/
|
|
233
|
+
startExperiment (options = {}) {
|
|
234
|
+
if (this.#startExperiment !== undefined && options.projectName) {
|
|
235
|
+
return this.#startExperiment(options)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this.#warn()
|
|
239
|
+
return Promise.resolve(new ExternalExperiment(new NoopExperiment(options.name, true)))
|
|
240
|
+
}
|
|
113
241
|
}
|
|
114
242
|
|
|
115
243
|
module.exports = NoopExperiments
|
|
@@ -13,6 +13,7 @@ class Row {
|
|
|
13
13
|
this.expectedOutput = fields.expectedOutput
|
|
14
14
|
this.errorType = fields.errorType
|
|
15
15
|
this.errorMessage = fields.errorMessage
|
|
16
|
+
this.errorStack = fields.errorStack
|
|
16
17
|
this.evaluations = fields.evaluations
|
|
17
18
|
this.evaluationErrors = fields.evaluationErrors
|
|
18
19
|
}
|
|
@@ -22,6 +22,26 @@ function hasEntries (value) {
|
|
|
22
22
|
return false
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function validateTagsList (tags) {
|
|
26
|
+
if (tags == null) return []
|
|
27
|
+
if (!Array.isArray(tags)) throw new TypeError('Tags must be an array of strings')
|
|
28
|
+
for (const tag of tags) {
|
|
29
|
+
if (typeof tag !== 'string') throw new TypeError('Each tag must be a string')
|
|
30
|
+
if (tag.indexOf(':') <= 0) {
|
|
31
|
+
throw new Error(`Tag '${tag}' is malformed. Tags must be in 'key:value' format (e.g., 'env:prod').`)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return [...tags]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function tagOperationsAreEmpty (operations) {
|
|
38
|
+
return operations == null || (
|
|
39
|
+
!Object.hasOwn(operations, 'replace') &&
|
|
40
|
+
!Object.hasOwn(operations, 'add') &&
|
|
41
|
+
!Object.hasOwn(operations, 'remove')
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
25
45
|
/**
|
|
26
46
|
* @param {string} name
|
|
27
47
|
*/
|
|
@@ -165,25 +185,58 @@ function stringify (value) {
|
|
|
165
185
|
* @returns {string[]}
|
|
166
186
|
*/
|
|
167
187
|
function buildTags (userTags, autoTags) {
|
|
168
|
-
const
|
|
188
|
+
const tagsByKey = new Map()
|
|
169
189
|
if ((userTags) != null) {
|
|
170
190
|
for (const [key, value] of Object.entries(userTags)) {
|
|
171
|
-
|
|
191
|
+
const values = Array.isArray(value) ? value : [value]
|
|
192
|
+
tagsByKey.set(key, values.map(item => `${key}:${item}`))
|
|
172
193
|
}
|
|
173
194
|
}
|
|
174
195
|
for (const [key, value] of Object.entries(autoTags)) {
|
|
175
|
-
if (value !== undefined && value !== null && value !== '')
|
|
196
|
+
if (value !== undefined && value !== null && value !== '') tagsByKey.set(key, [`${key}:${value}`])
|
|
176
197
|
}
|
|
177
|
-
|
|
198
|
+
|
|
199
|
+
const tags = []
|
|
200
|
+
for (const values of tagsByKey.values()) tags.push(...values)
|
|
201
|
+
return tags
|
|
178
202
|
}
|
|
179
203
|
|
|
180
204
|
/**
|
|
181
|
-
* @param {Record<string, unknown> | undefined}
|
|
182
|
-
* @param {Record<string, unknown>}
|
|
205
|
+
* @param {Record<string, unknown> | undefined} baseTags
|
|
206
|
+
* @param {Record<string, unknown> | undefined} overrideTags
|
|
183
207
|
* @returns {Record<string, unknown>}
|
|
184
208
|
*/
|
|
185
|
-
function
|
|
186
|
-
return
|
|
209
|
+
function mergeTags (baseTags, overrideTags) {
|
|
210
|
+
return { ...baseTags, ...overrideTags }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @param {string[] | undefined} tags
|
|
215
|
+
* @returns {Record<string, string | string[]>}
|
|
216
|
+
*/
|
|
217
|
+
function recordTagsToObject (tags) {
|
|
218
|
+
const result = {}
|
|
219
|
+
if (!Array.isArray(tags)) return result
|
|
220
|
+
for (const tag of tags) {
|
|
221
|
+
const separator = tag.indexOf(':')
|
|
222
|
+
if (separator <= 0) continue
|
|
223
|
+
|
|
224
|
+
const key = tag.slice(0, separator)
|
|
225
|
+
const value = tag.slice(separator + 1)
|
|
226
|
+
if (!Object.hasOwn(result, key)) {
|
|
227
|
+
Object.defineProperty(result, key, {
|
|
228
|
+
configurable: true,
|
|
229
|
+
enumerable: true,
|
|
230
|
+
value,
|
|
231
|
+
writable: true,
|
|
232
|
+
})
|
|
233
|
+
} else if (Array.isArray(result[key])) {
|
|
234
|
+
result[key].push(value)
|
|
235
|
+
} else {
|
|
236
|
+
result[key] = [result[key], value]
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return result
|
|
187
240
|
}
|
|
188
241
|
|
|
189
242
|
/**
|
|
@@ -195,6 +248,40 @@ function sleep (ms) {
|
|
|
195
248
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
196
249
|
}
|
|
197
250
|
|
|
251
|
+
/**
|
|
252
|
+
* @param {unknown} value
|
|
253
|
+
* @param {number} fallback
|
|
254
|
+
* @returns {number}
|
|
255
|
+
*/
|
|
256
|
+
function timestampMs (value, fallback = Date.now()) {
|
|
257
|
+
if (value === null || value === undefined) return fallback
|
|
258
|
+
if (value instanceof Date) {
|
|
259
|
+
const timestamp = value.getTime()
|
|
260
|
+
return Number.isFinite(timestamp) ? timestamp : fallback
|
|
261
|
+
}
|
|
262
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value
|
|
263
|
+
const parsed = Date.parse(String(value))
|
|
264
|
+
return Number.isFinite(parsed) ? parsed : fallback
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @param {{ durationMs?: unknown, completedAt?: unknown }} row
|
|
269
|
+
* @param {number} startMs
|
|
270
|
+
* @returns {number}
|
|
271
|
+
*/
|
|
272
|
+
function durationNs (row, startMs) {
|
|
273
|
+
if (typeof row.durationMs === 'number' && Number.isFinite(row.durationMs)) {
|
|
274
|
+
return Math.max(0, Math.round(row.durationMs * 1e6))
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (row.completedAt !== undefined) {
|
|
278
|
+
const completedMs = timestampMs(row.completedAt, startMs)
|
|
279
|
+
return Math.max(0, Math.round((completedMs - startMs) * 1e6))
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return 0
|
|
283
|
+
}
|
|
284
|
+
|
|
198
285
|
/**
|
|
199
286
|
* @param {Record<string, unknown> | undefined} recordMetadata
|
|
200
287
|
* @param {Record<string, unknown>} config
|
|
@@ -207,14 +294,19 @@ function buildSpanMetadata (recordMetadata, config) {
|
|
|
207
294
|
}
|
|
208
295
|
|
|
209
296
|
module.exports = {
|
|
210
|
-
buildExperimentTagObject,
|
|
211
297
|
buildSpanMetadata,
|
|
212
298
|
buildTags,
|
|
299
|
+
durationNs,
|
|
213
300
|
hasEntries,
|
|
214
301
|
inferMetricType,
|
|
302
|
+
mergeTags,
|
|
215
303
|
normalizeEvaluators,
|
|
216
304
|
normalizeJsonMetricValue,
|
|
305
|
+
recordTagsToObject,
|
|
217
306
|
sleep,
|
|
218
307
|
stringify,
|
|
308
|
+
tagOperationsAreEmpty,
|
|
309
|
+
timestampMs,
|
|
219
310
|
validateEvaluatorName,
|
|
311
|
+
validateTagsList,
|
|
220
312
|
}
|
|
@@ -228,6 +228,7 @@ function getJsonStringValue (str, defaultValue) {
|
|
|
228
228
|
function getModelMetadata (tags) {
|
|
229
229
|
/** @type {Record<string, unknown>} */
|
|
230
230
|
const modelMetadata = {}
|
|
231
|
+
let hasModelMetadata = false
|
|
231
232
|
for (const tag of Object.keys(tags)) {
|
|
232
233
|
const isModelMetadata = tag.startsWith(VERCEL_AI_MODEL_METADATA_PREFIX)
|
|
233
234
|
if (isModelMetadata) {
|
|
@@ -235,6 +236,7 @@ function getModelMetadata (tags) {
|
|
|
235
236
|
const metadataKey = lastCommaPosition === -1 ? tag : tag.slice(lastCommaPosition + 1)
|
|
236
237
|
if (metadataKey && MODEL_METADATA_KEYS.has(metadataKey)) {
|
|
237
238
|
modelMetadata[metadataKey] = tags[tag]
|
|
239
|
+
hasModelMetadata = true
|
|
238
240
|
}
|
|
239
241
|
} else {
|
|
240
242
|
const isTelemetryMetadata = tag.startsWith(VERCEL_AI_TELEMETRY_METADATA_PREFIX)
|
|
@@ -242,12 +244,13 @@ function getModelMetadata (tags) {
|
|
|
242
244
|
const metadataKey = tag.slice(VERCEL_AI_TELEMETRY_METADATA_PREFIX.length)
|
|
243
245
|
if (metadataKey) {
|
|
244
246
|
modelMetadata[metadataKey] = tags[tag]
|
|
247
|
+
hasModelMetadata = true
|
|
245
248
|
}
|
|
246
249
|
}
|
|
247
250
|
}
|
|
248
251
|
}
|
|
249
252
|
|
|
250
|
-
return
|
|
253
|
+
return hasModelMetadata ? modelMetadata : null
|
|
251
254
|
}
|
|
252
255
|
|
|
253
256
|
/**
|
|
@@ -259,6 +262,7 @@ function getModelMetadata (tags) {
|
|
|
259
262
|
function getGenerationMetadata (tags) {
|
|
260
263
|
/** @type {Record<string, unknown>} */
|
|
261
264
|
const metadata = {}
|
|
265
|
+
let hasMetadata = false
|
|
262
266
|
|
|
263
267
|
for (const tag of Object.keys(tags)) {
|
|
264
268
|
const isGenerationMetadata = tag.startsWith(VERCEL_AI_GENERATION_METADATA_PREFIX)
|
|
@@ -270,18 +274,20 @@ function getGenerationMetadata (tags) {
|
|
|
270
274
|
|
|
271
275
|
const settingValue = tags[tag]
|
|
272
276
|
metadata[settingKey] = settingValue
|
|
277
|
+
hasMetadata = true
|
|
273
278
|
} else {
|
|
274
279
|
const isTelemetryMetadata = tag.startsWith(VERCEL_AI_TELEMETRY_METADATA_PREFIX)
|
|
275
280
|
if (isTelemetryMetadata) {
|
|
276
281
|
const metadataKey = tag.slice(VERCEL_AI_TELEMETRY_METADATA_PREFIX.length)
|
|
277
282
|
if (metadataKey) {
|
|
278
283
|
metadata[metadataKey] = tags[tag]
|
|
284
|
+
hasMetadata = true
|
|
279
285
|
}
|
|
280
286
|
}
|
|
281
287
|
}
|
|
282
288
|
}
|
|
283
289
|
|
|
284
|
-
return
|
|
290
|
+
return hasMetadata ? metadata : null
|
|
285
291
|
}
|
|
286
292
|
|
|
287
293
|
/**
|
|
@@ -307,6 +313,7 @@ function getGenerationMetadataFromEvent (event) {
|
|
|
307
313
|
metadata[transformedKey] = value
|
|
308
314
|
}
|
|
309
315
|
|
|
316
|
+
// eslint-disable-next-line no-restricted-syntax -- manual tracking would duplicate Object.assign semantics
|
|
310
317
|
return Object.keys(metadata).length ? metadata : null
|
|
311
318
|
}
|
|
312
319
|
|
|
@@ -432,6 +439,7 @@ function getLlmObsSpanName (operation, functionId) {
|
|
|
432
439
|
*/
|
|
433
440
|
function getTelemetryMetadata (tags) {
|
|
434
441
|
const metadata = {}
|
|
442
|
+
let hasMetadata = false
|
|
435
443
|
|
|
436
444
|
for (const tag of Object.keys(tags)) {
|
|
437
445
|
if (!tag.startsWith(VERCEL_AI_TELEMETRY_METADATA_PREFIX)) continue
|
|
@@ -439,10 +447,11 @@ function getTelemetryMetadata (tags) {
|
|
|
439
447
|
const metadataKey = tag.slice(VERCEL_AI_TELEMETRY_METADATA_PREFIX.length)
|
|
440
448
|
if (metadataKey) {
|
|
441
449
|
metadata[metadataKey] = tags[tag]
|
|
450
|
+
hasMetadata = true
|
|
442
451
|
}
|
|
443
452
|
}
|
|
444
453
|
|
|
445
|
-
return
|
|
454
|
+
return hasMetadata ? metadata : null
|
|
446
455
|
}
|
|
447
456
|
|
|
448
457
|
module.exports = {
|
|
@@ -84,9 +84,11 @@ class LLMObsPlugin extends TracingPlugin {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
configure (config) {
|
|
87
|
-
// we do not want to enable any LLMObs plugins if it is disabled on the tracer
|
|
87
|
+
// we do not want to enable any LLMObs plugins if it is disabled on the tracer, or if the
|
|
88
|
+
// integration opted out via `tracer.use(<name>, { llmobs: false })`. Opting out only disables
|
|
89
|
+
// the LLMObs layer: the integration keeps emitting APM spans and propagating trace context.
|
|
88
90
|
const llmobsEnabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
|
|
89
|
-
if (llmobsEnabled === false) {
|
|
91
|
+
if (llmobsEnabled === false || config?.llmobs === false) {
|
|
90
92
|
config = typeof config === 'boolean' ? false : { ...config, enabled: false } // override to false
|
|
91
93
|
}
|
|
92
94
|
super.configure(config)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { features } = require('../feature-registry')
|
|
4
3
|
const NoopAppsecSdk = require('../appsec/sdk/noop')
|
|
5
4
|
const NoopLLMObsSDK = require('../llmobs/noop')
|
|
6
5
|
const NoopAIGuardSDK = require('../aiguard/noop')
|
|
6
|
+
const NoopFlaggingProvider = require('../openfeature/noop')
|
|
7
7
|
const NoopDogStatsDClient = require('./dogstatsd')
|
|
8
8
|
const NoopTracer = require('./tracer')
|
|
9
9
|
|
|
@@ -12,6 +12,7 @@ const noopAppsec = new NoopAppsecSdk()
|
|
|
12
12
|
const noopDogStatsDClient = new NoopDogStatsDClient()
|
|
13
13
|
const noopLLMObs = new NoopLLMObsSDK(noop)
|
|
14
14
|
const noopAIGuard = new NoopAIGuardSDK()
|
|
15
|
+
const noopFlaggingProviderInstance = new NoopFlaggingProvider()
|
|
15
16
|
const noopProfiling = {
|
|
16
17
|
setCustomLabelKeys () {},
|
|
17
18
|
runWithLabels (labels, fn) { return fn() },
|
|
@@ -25,9 +26,7 @@ class NoopProxy {
|
|
|
25
26
|
this.dogstatsd = noopDogStatsDClient
|
|
26
27
|
this.llmobs = noopLLMObs
|
|
27
28
|
this.aiguard = noopAIGuard
|
|
28
|
-
|
|
29
|
-
this[name] = noop
|
|
30
|
-
}
|
|
29
|
+
this.openfeature = noopFlaggingProviderInstance
|
|
31
30
|
this.setBaggageItem = (key, value) => {}
|
|
32
31
|
this.getBaggageItem = (key) => {}
|
|
33
32
|
this.getAllBaggageItems = () => {}
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
|
-
/**
|
|
5
|
-
* @constant
|
|
6
|
-
* @type {string} Base path for EVP proxy agent endpoint
|
|
7
|
-
*/
|
|
8
|
-
EVP_PROXY_AGENT_BASE_PATH: '/evp_proxy/v2/',
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @constant
|
|
12
|
-
* @type {string} HTTP header name for EVP subdomain routing
|
|
13
|
-
*/
|
|
14
|
-
EVP_SUBDOMAIN_HEADER_NAME: 'X-Datadog-EVP-Subdomain',
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @constant
|
|
18
|
-
* @type {string} EVP subdomain value for event platform intake
|
|
19
|
-
*/
|
|
20
|
-
EVP_SUBDOMAIN_VALUE: 'event-platform-intake',
|
|
21
|
-
|
|
22
4
|
/**
|
|
23
5
|
* @constant
|
|
24
6
|
* @type {string} API endpoint for exposure events EVP track
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
|
+
|
|
5
|
+
const { DatadogNodeServerProvider } = require('../../../../vendor/dist/@datadog/openfeature-node-server')
|
|
4
6
|
const log = require('../log')
|
|
5
7
|
const configurationSource = require('./configuration_source')
|
|
6
8
|
const { EXPOSURE_CHANNEL } = require('./constants/constants')
|
|
7
9
|
const EvalMetricsHook = require('./eval-metrics-hook')
|
|
8
10
|
const SpanEnrichmentHook = require('./span-enrichment-hook')
|
|
9
11
|
|
|
10
|
-
const { DatadogNodeServerProvider } = require('./require-provider')
|
|
11
|
-
|
|
12
12
|
/**
|
|
13
13
|
* OpenFeature provider that integrates with Datadog's feature flagging system.
|
|
14
14
|
* Extends DatadogNodeServerProvider to add tracer integration and configuration management.
|
|
@@ -48,6 +48,22 @@ class FlaggingProvider extends DatadogNodeServerProvider {
|
|
|
48
48
|
this.#configurationSource?.start()
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* @param {import('@openfeature/core').EvaluationContext} [context]
|
|
53
|
+
* @returns {Promise<void>}
|
|
54
|
+
*/
|
|
55
|
+
initialize (context) {
|
|
56
|
+
const promise = super.initialize(context)
|
|
57
|
+
|
|
58
|
+
// `DatadogNodeServerProvider#initialize` starts a timer that is never unref'd, which would
|
|
59
|
+
// otherwise keep an idle process (a short script, a serverless handler) alive for up to
|
|
60
|
+
// `initializationTimeoutMs` while waiting for configuration to arrive.
|
|
61
|
+
// TODO: remove once `@datadog/openfeature-node-server` unrefs this timer itself.
|
|
62
|
+
this.initController?.timeoutId?.unref?.()
|
|
63
|
+
|
|
64
|
+
return promise
|
|
65
|
+
}
|
|
66
|
+
|
|
51
67
|
/**
|
|
52
68
|
* Called when the provider is shut down.
|
|
53
69
|
* Cleans up resources including channel subscriptions.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
4
|
const log = require('../log')
|
|
5
5
|
const ExposuresWriter = require('./writers/exposures')
|
|
6
|
-
const {
|
|
6
|
+
const { setExposureDeliveryStrategy } = require('./writers/util')
|
|
7
7
|
|
|
8
8
|
const exposureSubmitCh = channel('ffe:exposure:submit')
|
|
9
9
|
const flushCh = channel('ffe:writers:flush')
|
|
@@ -40,12 +40,15 @@ function enable (config) {
|
|
|
40
40
|
return
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
const writer = new ExposuresWriter(config)
|
|
44
|
+
exposuresWriter = writer
|
|
44
45
|
exposureSubmitCh.subscribe(_handleExposureSubmit)
|
|
45
46
|
flushCh.subscribe(_handleFlush)
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
exposuresWriter
|
|
48
|
+
setExposureDeliveryStrategy(config, (enabled, route) => {
|
|
49
|
+
if (exposuresWriter !== writer) return
|
|
50
|
+
|
|
51
|
+
writer.setEnabled(enabled, route)
|
|
49
52
|
})
|
|
50
53
|
}
|
|
51
54
|
|
|
@@ -6,7 +6,7 @@ const RemoteConfigCapabilities = require('../remote_config/capabilities')
|
|
|
6
6
|
* Configures remote config handlers for openfeature feature flagging
|
|
7
7
|
*
|
|
8
8
|
* @param {import('../remote_config')} rc - RemoteConfig instance
|
|
9
|
-
* @param {() => import('./flagging_provider')} getOpenfeatureProxy
|
|
9
|
+
* @param {() => InstanceType<ReturnType<typeof import('./flagging_provider')>>} getOpenfeatureProxy
|
|
10
10
|
* @param {boolean} subscribe - Whether Agent Remote Config owns UFC delivery
|
|
11
11
|
*/
|
|
12
12
|
function enable (rc, getOpenfeatureProxy, subscribe) {
|