dd-trace 5.124.0 → 5.125.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 +3 -1
- package/ci/init.js +8 -3
- package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
- package/ci/test-optimization-validation/ci-remediation.js +6 -6
- package/ci/test-optimization-validation/command-blocker.js +7 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
- package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
- package/ci/test-optimization-validation/package-check.js +1 -1
- package/ci/test-optimization-validation/report-writer.js +7 -5
- package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
- package/ci/vitest-no-worker-init-setup.mjs +13 -13
- package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
- package/package.json +13 -10
- package/packages/datadog-esbuild/index.js +3 -1
- package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
- package/packages/datadog-instrumentations/src/fs.js +24 -20
- package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +16 -16
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +265 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +251 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +11 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
- package/packages/datadog-instrumentations/src/jest.js +25 -20
- package/packages/datadog-instrumentations/src/knex.js +76 -0
- package/packages/datadog-instrumentations/src/mariadb.js +6 -0
- package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
- package/packages/datadog-instrumentations/src/net.js +14 -17
- package/packages/datadog-instrumentations/src/next.js +212 -62
- package/packages/datadog-instrumentations/src/openai.js +42 -40
- package/packages/datadog-instrumentations/src/pg.js +15 -0
- package/packages/datadog-instrumentations/src/playwright.js +109 -13
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
- package/packages/datadog-instrumentations/src/sequelize.js +94 -2
- package/packages/datadog-instrumentations/src/undici.js +53 -4
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +37 -3
- package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
- package/packages/datadog-instrumentations/src/vitest-worker.js +71 -14
- package/packages/datadog-instrumentations/src/webdriverio.js +965 -6
- package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/util.js +2 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
- package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
- package/packages/datadog-plugin-cucumber/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
- package/packages/datadog-plugin-graphql/src/execute.js +998 -196
- package/packages/datadog-plugin-graphql/src/index.js +27 -7
- package/packages/datadog-plugin-graphql/src/jit.js +136 -0
- package/packages/datadog-plugin-graphql/src/request.js +18 -27
- package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
- package/packages/datadog-plugin-graphql/src/utils.js +208 -120
- package/packages/datadog-plugin-graphql/src/validate.js +10 -23
- package/packages/datadog-plugin-jest/src/index.js +0 -6
- package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
- package/packages/datadog-plugin-mocha/src/index.js +403 -75
- package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
- package/packages/datadog-plugin-mysql/src/index.js +41 -15
- package/packages/datadog-plugin-net/src/tcp.js +3 -1
- package/packages/datadog-plugin-next/src/index.js +63 -29
- package/packages/datadog-plugin-playwright/src/index.js +167 -27
- package/packages/datadog-plugin-undici/src/index.js +27 -41
- package/packages/datadog-plugin-vitest/src/index.js +6 -12
- package/packages/dd-trace/src/aiguard/evaluation.js +2 -1
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
- package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
- package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
- package/packages/dd-trace/src/appsec/graphql.js +6 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/lambda.js +44 -26
- package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +5 -173
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +394 -225
- package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
- package/packages/dd-trace/src/ci-visibility/final-flush.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
- package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/index.js +73 -13
- package/packages/dd-trace/src/config/parsers.js +11 -1
- package/packages/dd-trace/src/config/stable.js +3 -3
- package/packages/dd-trace/src/config/supported-configurations.json +20 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
- package/packages/dd-trace/src/debugger/config.js +12 -1
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -40
- package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
- package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
- package/packages/dd-trace/src/debugger/index.js +23 -3
- package/packages/dd-trace/src/dogstatsd.js +510 -76
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
- package/packages/dd-trace/src/encode/0.4.js +5 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
- package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +24 -7
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
- package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
- package/packages/dd-trace/src/exporters/common/agents.js +28 -6
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
- package/packages/dd-trace/src/exporters/common/request.js +31 -11
- package/packages/dd-trace/src/exporters/common/url.js +40 -1
- package/packages/dd-trace/src/exporters/common/writer.js +9 -7
- package/packages/dd-trace/src/flush.js +6 -3
- package/packages/dd-trace/src/id.js +14 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +2 -1
- package/packages/dd-trace/src/llmobs/index.js +32 -32
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
- package/packages/dd-trace/src/llmobs/util.js +0 -33
- package/packages/dd-trace/src/llmobs/writers/base.js +49 -32
- package/packages/dd-trace/src/log/index.js +7 -1
- package/packages/dd-trace/src/{ci-visibility/log-submission → log-submission}/log-submission-plugin.js +48 -19
- package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -24
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +15 -22
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +10 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +93 -18
- package/packages/dd-trace/src/payload-tagging/index.js +2 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
- package/packages/dd-trace/src/plugins/tracing.js +12 -10
- package/packages/dd-trace/src/plugins/util/ci.js +3 -1
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
- package/packages/dd-trace/src/plugins/util/test.js +83 -20
- package/packages/dd-trace/src/plugins/util/url.js +2 -2
- package/packages/dd-trace/src/plugins/util/web.js +4 -9
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/oom.js +7 -2
- package/packages/dd-trace/src/profiling/profiler.js +23 -13
- package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
- package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
- package/packages/dd-trace/src/proxy.js +75 -5
- package/packages/dd-trace/src/remote_config/index.js +319 -101
- package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
- package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -1
- package/packages/dd-trace/src/sampling_rule.js +36 -9
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +4 -8
- package/packages/dd-trace/src/serverless/vercel.js +53 -20
- package/packages/dd-trace/src/serverless.js +10 -3
- package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
- package/packages/dd-trace/src/span_stats.js +24 -28
- package/packages/dd-trace/src/startup-log.js +3 -2
- package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
- package/packages/dd-trace/src/telemetry/send-data.js +18 -16
- package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
- package/packages/dd-trace/src/tracer.js +4 -5
- package/packages/dd-trace/src/tracer_metadata.js +11 -3
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
- package/packages/dd-trace/src/encode/agentless-json.js +0 -210
|
@@ -1,42 +1,55 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { createAgentlessExporter } = require('@datadog/libdatadog')
|
|
4
|
+
|
|
5
|
+
const { storage } = require('../../../../datadog-core')
|
|
3
6
|
const getConfig = require('../../config')
|
|
4
7
|
const log = require('../../log')
|
|
5
|
-
const request = require('../common/request')
|
|
6
8
|
const tracerVersion = require('../../../../../package.json').version
|
|
7
9
|
|
|
10
|
+
const { canSendApiKey } = require('../common/url')
|
|
8
11
|
const BaseWriter = require('../common/writer')
|
|
9
|
-
const {
|
|
12
|
+
const { AgentEncoder } = require('../../encode/0.4')
|
|
10
13
|
const { computeIntakeUrl, INTAKE_PATH } = require('./intake')
|
|
11
14
|
|
|
15
|
+
const legacyStorage = storage('legacy')
|
|
16
|
+
|
|
12
17
|
/**
|
|
13
18
|
* Writer for agentless APM trace intake.
|
|
14
|
-
*
|
|
19
|
+
* Encodes traces as v0.4 MessagePack and delegates transformation and delivery
|
|
20
|
+
* to the APM data pipeline.
|
|
15
21
|
*/
|
|
16
22
|
class AgentlessWriter extends BaseWriter {
|
|
17
23
|
#apiKeyMissing = false
|
|
24
|
+
#apiKeyUnsafeReceiver = false
|
|
25
|
+
#exporter
|
|
26
|
+
#exporterApiKey
|
|
27
|
+
#exporterEnv
|
|
28
|
+
#exporterRuntimeId
|
|
29
|
+
#metadata
|
|
18
30
|
#urlMissing = false
|
|
19
31
|
|
|
20
32
|
/**
|
|
21
33
|
* @param {object} options - Writer options
|
|
22
34
|
* @param {URL} [options.url] - The intake URL. If not provided, constructed from site.
|
|
23
35
|
* @param {string} [options.site] - The Datadog site
|
|
24
|
-
* @param {object} [options.metadata] - Metadata to pass to the
|
|
36
|
+
* @param {object} [options.metadata] - Metadata to pass to the data pipeline
|
|
25
37
|
*/
|
|
26
38
|
constructor ({ url, site = 'datadoghq.com', metadata = {} }) {
|
|
27
39
|
super({ url })
|
|
28
|
-
this
|
|
40
|
+
this.#metadata = metadata
|
|
41
|
+
this._encoder = new AgentEncoder(this)
|
|
29
42
|
|
|
30
43
|
if (!url) {
|
|
31
44
|
try {
|
|
32
45
|
this._url = new URL(computeIntakeUrl(site))
|
|
33
|
-
} catch (
|
|
46
|
+
} catch (error) {
|
|
34
47
|
log.error(
|
|
35
48
|
'Invalid site value for agentless intake: %s. Cannot construct URL. Error: %s',
|
|
36
49
|
site,
|
|
37
|
-
|
|
50
|
+
error.message
|
|
38
51
|
)
|
|
39
|
-
this._url =
|
|
52
|
+
this._url = undefined
|
|
40
53
|
}
|
|
41
54
|
}
|
|
42
55
|
|
|
@@ -46,59 +59,23 @@ class AgentlessWriter extends BaseWriter {
|
|
|
46
59
|
}
|
|
47
60
|
}
|
|
48
61
|
|
|
62
|
+
/**
|
|
63
|
+
* @param {URL} url - The new intake URL.
|
|
64
|
+
*/
|
|
49
65
|
setUrl (url) {
|
|
50
66
|
super.setUrl(url)
|
|
67
|
+
this.#closeExporter()
|
|
51
68
|
if (url) {
|
|
52
69
|
this.#urlMissing = false
|
|
53
70
|
}
|
|
54
71
|
}
|
|
55
72
|
|
|
56
73
|
/**
|
|
57
|
-
*
|
|
58
|
-
* @param {
|
|
59
|
-
|
|
60
|
-
flush (done = () => {}) {
|
|
61
|
-
if (!request.writable) {
|
|
62
|
-
const count = this._encoder.count()
|
|
63
|
-
if (count > 0) {
|
|
64
|
-
log.error('Maximum number of active requests reached. Dropping %d trace(s).', count)
|
|
65
|
-
}
|
|
66
|
-
this._encoder.reset()
|
|
67
|
-
done()
|
|
68
|
-
return
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const count = this._encoder.count()
|
|
72
|
-
|
|
73
|
-
if (count === 0) {
|
|
74
|
-
done()
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const payload = this._encoder.makePayload()
|
|
79
|
-
|
|
80
|
-
if (payload.length === 0) {
|
|
81
|
-
log.debug('Skipping send of empty payload')
|
|
82
|
-
done()
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
this._sendPayload(payload, count, done)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Sends the encoded payload to the intake endpoint.
|
|
91
|
-
* @param {Buffer} data - The encoded JSON payload
|
|
92
|
-
* @param {number} count - Number of traces in the payload
|
|
93
|
-
* @param {Function} done - Callback when complete
|
|
74
|
+
* @param {Buffer} data - v0.4 MessagePack payload.
|
|
75
|
+
* @param {number} count - Number of traces in the payload.
|
|
76
|
+
* @param {() => void} done - Callback invoked after delivery completes or fails.
|
|
94
77
|
*/
|
|
95
78
|
_sendPayload (data, count, done) {
|
|
96
|
-
if (!data || data.length === 0) {
|
|
97
|
-
log.debug('Skipping send of empty payload')
|
|
98
|
-
done()
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
79
|
if (!this._url) {
|
|
103
80
|
if (!this.#urlMissing) {
|
|
104
81
|
this.#urlMissing = true
|
|
@@ -121,81 +98,86 @@ class AgentlessWriter extends BaseWriter {
|
|
|
121
98
|
}
|
|
122
99
|
this.#apiKeyMissing = false
|
|
123
100
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
101
|
+
// The WASM transport performs its HTTP request in JavaScript. Keep that
|
|
102
|
+
// internal request out of the instrumented application's traces.
|
|
103
|
+
try {
|
|
104
|
+
legacyStorage.run({ noop: true }, () => {
|
|
105
|
+
const exporter = this.#applyConfiguration(DD_API_KEY)
|
|
106
|
+
if (exporter) {
|
|
107
|
+
exporter.sendV04(data, done, log)
|
|
108
|
+
} else {
|
|
109
|
+
done()
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
} catch (error) {
|
|
113
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
114
|
+
log.error('Failed to send %d trace(s) to the agentless intake: %s', count, message)
|
|
115
|
+
done()
|
|
138
116
|
}
|
|
117
|
+
}
|
|
139
118
|
|
|
140
|
-
|
|
119
|
+
/**
|
|
120
|
+
* @returns {string} The full agentless intake endpoint.
|
|
121
|
+
*/
|
|
122
|
+
#endpoint () {
|
|
123
|
+
const endpoint = new URL(this._url)
|
|
124
|
+
endpoint.pathname = INTAKE_PATH
|
|
125
|
+
endpoint.search = ''
|
|
126
|
+
endpoint.hash = ''
|
|
127
|
+
return endpoint.href
|
|
128
|
+
}
|
|
141
129
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
130
|
+
/**
|
|
131
|
+
* @param {string} apiKey - Datadog API key.
|
|
132
|
+
* @returns {import('@datadog/libdatadog').AgentlessExporter|undefined}
|
|
133
|
+
*/
|
|
134
|
+
#applyConfiguration (apiKey) {
|
|
135
|
+
if (!canSendApiKey(this._url.protocol, this._url.hostname)) {
|
|
136
|
+
if (!this.#apiKeyUnsafeReceiver) {
|
|
137
|
+
this.#apiKeyUnsafeReceiver = true
|
|
138
|
+
log.warn('DD_API_KEY will not be sent because the configured receiver is neither HTTPS nor loopback.')
|
|
147
139
|
}
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
this.#apiKeyUnsafeReceiver = false
|
|
143
|
+
|
|
144
|
+
const { env, runtimeID } = this.#metadata
|
|
145
|
+
if (
|
|
146
|
+
this.#exporter &&
|
|
147
|
+
this.#exporterApiKey === apiKey &&
|
|
148
|
+
this.#exporterEnv === env &&
|
|
149
|
+
this.#exporterRuntimeId === runtimeID
|
|
150
|
+
) {
|
|
151
|
+
return this.#exporter
|
|
152
|
+
}
|
|
148
153
|
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
this.#closeExporter()
|
|
155
|
+
const config = getConfig()
|
|
156
|
+
this.#exporter = createAgentlessExporter({
|
|
157
|
+
endpoint: this.#endpoint(),
|
|
158
|
+
apiKey,
|
|
159
|
+
hostname: this.#metadata.hostname,
|
|
160
|
+
env,
|
|
161
|
+
service: config.service,
|
|
162
|
+
version: config.version,
|
|
163
|
+
runtimeId: runtimeID,
|
|
164
|
+
containerId: this.#metadata.containerId,
|
|
165
|
+
tracerVersion,
|
|
166
|
+
languageVersion: process.version,
|
|
167
|
+
languageInterpreter: process.versions.bun ? 'JavaScriptCore' : 'v8',
|
|
151
168
|
})
|
|
169
|
+
this.#exporterApiKey = apiKey
|
|
170
|
+
this.#exporterEnv = env
|
|
171
|
+
this.#exporterRuntimeId = runtimeID
|
|
172
|
+
return this.#exporter
|
|
152
173
|
}
|
|
153
174
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
#logRequestError (err, statusCode, count) {
|
|
161
|
-
if (statusCode === 401 || statusCode === 403) {
|
|
162
|
-
log.error(
|
|
163
|
-
'Authentication failed sending %d trace(s) (status %s). Verify DD_API_KEY is valid.',
|
|
164
|
-
count,
|
|
165
|
-
statusCode
|
|
166
|
-
)
|
|
167
|
-
} else if (statusCode === 404) {
|
|
168
|
-
log.error(
|
|
169
|
-
'Trace intake endpoint not found (status %s). Verify DD_SITE is correctly configured. %d trace(s) dropped.',
|
|
170
|
-
statusCode,
|
|
171
|
-
count
|
|
172
|
-
)
|
|
173
|
-
} else if (statusCode === 429) {
|
|
174
|
-
log.error(
|
|
175
|
-
'Rate limited by trace intake (status 429). %d trace(s) dropped.',
|
|
176
|
-
count
|
|
177
|
-
)
|
|
178
|
-
} else if (statusCode >= 500) {
|
|
179
|
-
log.error(
|
|
180
|
-
'Trace intake server error (status %s). %d trace(s) dropped. This may be transient.',
|
|
181
|
-
statusCode,
|
|
182
|
-
count
|
|
183
|
-
)
|
|
184
|
-
} else if (statusCode) {
|
|
185
|
-
log.error(
|
|
186
|
-
'Error sending agentless payload (status %s): %s. %d trace(s) dropped.',
|
|
187
|
-
statusCode,
|
|
188
|
-
err.message,
|
|
189
|
-
count
|
|
190
|
-
)
|
|
191
|
-
} else {
|
|
192
|
-
log.error(
|
|
193
|
-
'Network error sending %d trace(s) to %s: %s',
|
|
194
|
-
count,
|
|
195
|
-
this._url?.hostname || 'unknown',
|
|
196
|
-
err.message
|
|
197
|
-
)
|
|
198
|
-
}
|
|
175
|
+
#closeExporter () {
|
|
176
|
+
this.#exporter?.close()
|
|
177
|
+
this.#exporter = undefined
|
|
178
|
+
this.#exporterApiKey = undefined
|
|
179
|
+
this.#exporterEnv = undefined
|
|
180
|
+
this.#exporterRuntimeId = undefined
|
|
199
181
|
}
|
|
200
182
|
}
|
|
201
183
|
|
|
@@ -7,9 +7,15 @@ const { storage } = require('../../../../datadog-core')
|
|
|
7
7
|
const legacyStorage = storage('legacy')
|
|
8
8
|
|
|
9
9
|
const keepAlive = true
|
|
10
|
-
const maxSockets = 1
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Creates an agent class that suppresses tracing around socket lifecycle operations.
|
|
13
|
+
*
|
|
14
|
+
* @param {typeof http.Agent|typeof https.Agent} BaseAgent
|
|
15
|
+
* @param {number} maxSockets
|
|
16
|
+
* @returns {typeof http.Agent|typeof https.Agent}
|
|
17
|
+
*/
|
|
18
|
+
function createAgentClass (BaseAgent, maxSockets) {
|
|
13
19
|
class CustomAgent extends BaseAgent {
|
|
14
20
|
constructor () {
|
|
15
21
|
super({ keepAlive, maxSockets })
|
|
@@ -35,10 +41,26 @@ function createAgentClass (BaseAgent) {
|
|
|
35
41
|
return CustomAgent
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Creates isolated HTTP and HTTPS agents with a bounded connection pool.
|
|
46
|
+
*
|
|
47
|
+
* @param {number} maxSockets
|
|
48
|
+
* @returns {{ httpAgent: http.Agent, httpsAgent: https.Agent }}
|
|
49
|
+
*/
|
|
50
|
+
function createAgents (maxSockets) {
|
|
51
|
+
const HttpAgent = createAgentClass(http.Agent, maxSockets)
|
|
52
|
+
const HttpsAgent = createAgentClass(https.Agent, maxSockets)
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
httpAgent: new HttpAgent(),
|
|
56
|
+
httpsAgent: new HttpsAgent(),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const { httpAgent, httpsAgent } = createAgents(1)
|
|
40
61
|
|
|
41
62
|
module.exports = {
|
|
42
|
-
|
|
43
|
-
|
|
63
|
+
createAgents,
|
|
64
|
+
httpAgent,
|
|
65
|
+
httpsAgent,
|
|
44
66
|
}
|
|
@@ -25,10 +25,10 @@ class BufferingExporter {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
_export (payload, writer = this._writer, timerKey = '_timer', deferImmediateFlush = false) {
|
|
28
|
-
|
|
28
|
+
const appended = writer.append(payload)
|
|
29
|
+
if (this._config.isCiVisibility && appended !== false) {
|
|
29
30
|
incrementCountMetric(TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION, {}, payload.length)
|
|
30
31
|
}
|
|
31
|
-
const appended = writer.append(payload)
|
|
32
32
|
|
|
33
33
|
const { flushInterval } = this._config
|
|
34
34
|
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Record<string, unknown> & { deadline?: number, signal?: AbortSignal }} RequestOptions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {object} FinalFlush
|
|
9
|
+
* @property {number} deadline
|
|
10
|
+
* @property {(error?: Error) => void} done
|
|
11
|
+
* @property {Error | undefined} error
|
|
12
|
+
* @property {Set<PendingRequest>} requests
|
|
13
|
+
* @property {ReturnType<typeof setTimeout> | undefined} timeoutId
|
|
14
|
+
* @property {boolean} writerDone
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {object} PendingRequest
|
|
19
|
+
* @property {AbortController} controller
|
|
20
|
+
* @property {Set<FinalFlush>} finalFlushes
|
|
21
|
+
* @property {RequestOptions} options
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
class FinalFlushRequestTracker {
|
|
25
|
+
#flush
|
|
26
|
+
#createTimeoutError
|
|
27
|
+
/** @type {Set<PendingRequest>} */
|
|
28
|
+
#pendingRequests = new Set()
|
|
29
|
+
/** @type {Set<FinalFlush>} */
|
|
30
|
+
#finalFlushes = new Set()
|
|
31
|
+
/** @type {FinalFlush | undefined} */
|
|
32
|
+
#activeFinalFlush
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {(done?: (error?: Error) => void, options?: { deadline?: number }) => void} flush
|
|
36
|
+
* @param {() => Error} createTimeoutError
|
|
37
|
+
*/
|
|
38
|
+
constructor (flush, createTimeoutError) {
|
|
39
|
+
this.#flush = flush
|
|
40
|
+
this.#createTimeoutError = createTimeoutError
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Flushes queued payloads and waits for requests that were already in flight.
|
|
45
|
+
*
|
|
46
|
+
* @param {(error?: Error) => void} [done]
|
|
47
|
+
* @param {{ deadline?: number }} [options]
|
|
48
|
+
* @returns {void}
|
|
49
|
+
*/
|
|
50
|
+
flush (done, options) {
|
|
51
|
+
if (options?.deadline === undefined) {
|
|
52
|
+
this.#flush(done, options)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @type {FinalFlush} */
|
|
57
|
+
const finalFlush = {
|
|
58
|
+
deadline: options.deadline,
|
|
59
|
+
done: done || (() => {}),
|
|
60
|
+
error: undefined,
|
|
61
|
+
requests: new Set(),
|
|
62
|
+
timeoutId: undefined,
|
|
63
|
+
writerDone: false,
|
|
64
|
+
}
|
|
65
|
+
this.#finalFlushes.add(finalFlush)
|
|
66
|
+
|
|
67
|
+
for (const pendingRequest of this.#pendingRequests) {
|
|
68
|
+
this.#attachRequest(finalFlush, pendingRequest)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const remaining = Math.max(0, options.deadline - Date.now())
|
|
72
|
+
finalFlush.timeoutId = setTimeout(() => {
|
|
73
|
+
const error = this.#createTimeoutError()
|
|
74
|
+
|
|
75
|
+
finalFlush.error ||= error
|
|
76
|
+
finalFlush.writerDone = true
|
|
77
|
+
|
|
78
|
+
for (const pendingRequest of finalFlush.requests) {
|
|
79
|
+
pendingRequest.finalFlushes.delete(finalFlush)
|
|
80
|
+
if (pendingRequest.finalFlushes.size === 0) {
|
|
81
|
+
pendingRequest.controller.abort(error)
|
|
82
|
+
this.#pendingRequests.delete(pendingRequest)
|
|
83
|
+
} else {
|
|
84
|
+
this.#updateRequestDeadline(pendingRequest)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
finalFlush.requests.clear()
|
|
88
|
+
this.#finishFinalFlush(finalFlush)
|
|
89
|
+
}, remaining)
|
|
90
|
+
|
|
91
|
+
const previousFinalFlush = this.#activeFinalFlush
|
|
92
|
+
this.#activeFinalFlush = finalFlush
|
|
93
|
+
try {
|
|
94
|
+
this.#flush((error) => {
|
|
95
|
+
finalFlush.error ||= error
|
|
96
|
+
finalFlush.writerDone = true
|
|
97
|
+
this.#finishFinalFlush(finalFlush)
|
|
98
|
+
}, options)
|
|
99
|
+
} finally {
|
|
100
|
+
this.#activeFinalFlush = previousFinalFlush
|
|
101
|
+
}
|
|
102
|
+
this.#finishFinalFlush(finalFlush)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Sends and tracks a request so a later final flush can wait for or abort it.
|
|
107
|
+
*
|
|
108
|
+
* @param {(data: Buffer|string|object, options: RequestOptions,
|
|
109
|
+
* callback: (error: Error|null, result?: string|null, statusCode?: number,
|
|
110
|
+
* headers?: import('node:http').IncomingHttpHeaders) => void) => void} request
|
|
111
|
+
* @param {Buffer|string|object} data
|
|
112
|
+
* @param {RequestOptions} options
|
|
113
|
+
* @param {(error: Error|null, result?: string|null, statusCode?: number,
|
|
114
|
+
* headers?: import('node:http').IncomingHttpHeaders) => void} callback
|
|
115
|
+
* @returns {void}
|
|
116
|
+
*/
|
|
117
|
+
send (request, data, options, callback) {
|
|
118
|
+
const controller = new AbortController()
|
|
119
|
+
const requestOptions = { ...options, signal: controller.signal }
|
|
120
|
+
const pendingRequest = { controller, finalFlushes: new Set(), options: requestOptions }
|
|
121
|
+
this.#pendingRequests.add(pendingRequest)
|
|
122
|
+
if (this.#activeFinalFlush) this.#attachRequest(this.#activeFinalFlush, pendingRequest)
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
request(data, requestOptions, (error, result, statusCode, headers) => {
|
|
126
|
+
if (error) {
|
|
127
|
+
for (const finalFlush of pendingRequest.finalFlushes) finalFlush.error ||= error
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
callback(error, result, statusCode, headers)
|
|
132
|
+
} finally {
|
|
133
|
+
this.#dropRequest(pendingRequest)
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
} catch (error) {
|
|
137
|
+
this.#dropRequest(pendingRequest)
|
|
138
|
+
throw error
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {PendingRequest} pendingRequest
|
|
144
|
+
* @returns {void}
|
|
145
|
+
*/
|
|
146
|
+
#dropRequest (pendingRequest) {
|
|
147
|
+
this.#pendingRequests.delete(pendingRequest)
|
|
148
|
+
for (const finalFlush of pendingRequest.finalFlushes) {
|
|
149
|
+
finalFlush.requests.delete(pendingRequest)
|
|
150
|
+
this.#finishFinalFlush(finalFlush)
|
|
151
|
+
}
|
|
152
|
+
pendingRequest.finalFlushes.clear()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @param {FinalFlush} finalFlush
|
|
157
|
+
* @param {PendingRequest} pendingRequest
|
|
158
|
+
* @returns {void}
|
|
159
|
+
*/
|
|
160
|
+
#attachRequest (finalFlush, pendingRequest) {
|
|
161
|
+
finalFlush.requests.add(pendingRequest)
|
|
162
|
+
pendingRequest.finalFlushes.add(finalFlush)
|
|
163
|
+
this.#updateRequestDeadline(pendingRequest)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {PendingRequest} pendingRequest
|
|
168
|
+
* @returns {void}
|
|
169
|
+
*/
|
|
170
|
+
#updateRequestDeadline (pendingRequest) {
|
|
171
|
+
let deadline = 0
|
|
172
|
+
for (const finalFlush of pendingRequest.finalFlushes) {
|
|
173
|
+
deadline = Math.max(deadline, finalFlush.deadline)
|
|
174
|
+
}
|
|
175
|
+
pendingRequest.options.deadline = deadline
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {FinalFlush} finalFlush
|
|
180
|
+
* @returns {void}
|
|
181
|
+
*/
|
|
182
|
+
#finishFinalFlush (finalFlush) {
|
|
183
|
+
if (!this.#finalFlushes.has(finalFlush) || !finalFlush.writerDone || finalFlush.requests.size !== 0) return
|
|
184
|
+
|
|
185
|
+
this.#finalFlushes.delete(finalFlush)
|
|
186
|
+
clearTimeout(finalFlush.timeoutId)
|
|
187
|
+
finalFlush.done(finalFlush.error)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = FinalFlushRequestTracker
|
|
@@ -10,7 +10,7 @@ const zlib = require('zlib')
|
|
|
10
10
|
|
|
11
11
|
const { storage } = require('../../../../datadog-core')
|
|
12
12
|
const log = require('../../log')
|
|
13
|
-
const {
|
|
13
|
+
const { canSendApiKey, parseUrl } = require('./url')
|
|
14
14
|
const docker = require('./docker')
|
|
15
15
|
const { httpAgent, httpsAgent } = require('./agents')
|
|
16
16
|
const {
|
|
@@ -30,7 +30,7 @@ let activeBufferSize = 0
|
|
|
30
30
|
* @param {Buffer|string|Readable|Array<Buffer|string>} data
|
|
31
31
|
* @param {object} options
|
|
32
32
|
* @param {(error: Error|null, result?: string|null, statusCode?: number,
|
|
33
|
-
* headers?: import('node:http').IncomingHttpHeaders) => void} callback
|
|
33
|
+
* headers?: import('node:http').IncomingHttpHeaders, dropped?: boolean) => void} callback
|
|
34
34
|
*/
|
|
35
35
|
function request (data, options, callback) {
|
|
36
36
|
if (!options.headers) {
|
|
@@ -54,7 +54,7 @@ function request (data, options, callback) {
|
|
|
54
54
|
// rather than drop the request: the agent proxies telemetry with its own key, while an https
|
|
55
55
|
// intake URL is required to authenticate agentless traffic.
|
|
56
56
|
const hasApiKey = options.headers['dd-api-key'] !== undefined || options.headers['DD-API-KEY'] !== undefined
|
|
57
|
-
if (hasApiKey && options.protocol
|
|
57
|
+
if (hasApiKey && !canSendApiKey(options.protocol, options.hostname)) {
|
|
58
58
|
log.error(
|
|
59
59
|
'Not sending the Datadog API key over a non-TLS connection to %s. Configure an https intake URL.',
|
|
60
60
|
options.hostname
|
|
@@ -89,7 +89,8 @@ function request (data, options, callback) {
|
|
|
89
89
|
if (!Array.isArray(data)) {
|
|
90
90
|
dataArray = [data]
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
const contentLength = byteLength(dataArray)
|
|
93
|
+
options.headers['Content-Length'] = contentLength
|
|
93
94
|
|
|
94
95
|
docker.inject(options.headers)
|
|
95
96
|
|
|
@@ -172,20 +173,23 @@ function request (data, options, callback) {
|
|
|
172
173
|
// outside AsyncContextFrame, so a synchronous re-entry would lose the store.
|
|
173
174
|
/** @param {number} attemptIndex */
|
|
174
175
|
const attempt = attemptIndex => {
|
|
175
|
-
if (
|
|
176
|
-
log.
|
|
177
|
-
|
|
176
|
+
if (activeBufferSize + contentLength > maxActiveBufferSize) {
|
|
177
|
+
const error = new log.NoTransmitError('Maximum active request buffer size reached: payload is discarded.')
|
|
178
|
+
error.code = 'ERR_DD_REQUEST_BUFFER_FULL'
|
|
179
|
+
log.debug(error.message)
|
|
180
|
+
return callback(error, undefined, undefined, undefined, true)
|
|
178
181
|
}
|
|
179
182
|
|
|
180
|
-
activeBufferSize +=
|
|
183
|
+
activeBufferSize += contentLength
|
|
181
184
|
|
|
182
185
|
legacyStorage.run({ noop: true }, () => {
|
|
183
186
|
let finished = false
|
|
184
187
|
let settled = false
|
|
188
|
+
let timeoutImmediate
|
|
185
189
|
const finalize = () => {
|
|
186
190
|
if (finished) return
|
|
187
191
|
finished = true
|
|
188
|
-
activeBufferSize -=
|
|
192
|
+
activeBufferSize -= contentLength
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
/**
|
|
@@ -197,6 +201,7 @@ function request (data, options, callback) {
|
|
|
197
201
|
const complete = (error, result, statusCode, headers) => {
|
|
198
202
|
if (settled) return
|
|
199
203
|
settled = true
|
|
204
|
+
clearImmediate(timeoutImmediate)
|
|
200
205
|
finalize()
|
|
201
206
|
callback(error, result, statusCode, headers)
|
|
202
207
|
}
|
|
@@ -206,6 +211,7 @@ function request (data, options, callback) {
|
|
|
206
211
|
*/
|
|
207
212
|
const handleError = (error) => {
|
|
208
213
|
if (settled) return
|
|
214
|
+
clearImmediate(timeoutImmediate)
|
|
209
215
|
|
|
210
216
|
if (options.retry !== false &&
|
|
211
217
|
attemptIndex < getMaxAttempts(options) &&
|
|
@@ -224,10 +230,11 @@ function request (data, options, callback) {
|
|
|
224
230
|
const req = client.request(connectionOptions, (res) => onResponse(res, complete, handleError))
|
|
225
231
|
|
|
226
232
|
req.once('close', finalize)
|
|
227
|
-
req.once('timeout', finalize)
|
|
233
|
+
if (!options.deferTimeoutAbort) req.once('timeout', finalize)
|
|
228
234
|
req.once('error', handleError)
|
|
229
235
|
|
|
230
|
-
|
|
236
|
+
const abortRequest = () => {
|
|
237
|
+
if (settled) return
|
|
231
238
|
try {
|
|
232
239
|
if (typeof req.abort === 'function') {
|
|
233
240
|
req.abort()
|
|
@@ -237,6 +244,19 @@ function request (data, options, callback) {
|
|
|
237
244
|
} catch {
|
|
238
245
|
// ignore
|
|
239
246
|
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
req.setTimeout(timeout, () => {
|
|
250
|
+
if (!options.deferTimeoutAbort) {
|
|
251
|
+
abortRequest()
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
timeoutImmediate = setImmediate(() => {
|
|
256
|
+
abortRequest()
|
|
257
|
+
finalize()
|
|
258
|
+
})
|
|
259
|
+
if (!options.keepProcessAlive) timeoutImmediate.unref?.()
|
|
240
260
|
})
|
|
241
261
|
|
|
242
262
|
for (const buffer of dataArray) req.write(buffer)
|