dd-trace 6.21.0 → 6.22.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/ci/diagnose.js +9 -14
- package/ci/init.js +1 -2
- package/ci/test-optimization-validation/ci-discovery.js +9 -4
- package/ci/test-optimization-validation/executable.js +1 -5
- package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
- package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
- package/ci/test-optimization-validation/generated-files.js +6 -4
- package/ci/test-optimization-validation/generated-test-contract.js +7 -3
- package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
- package/ci/test-optimization-validation/manifest-schema.js +6 -7
- package/ci/test-optimization-validation/report-writer.js +6 -2
- package/ci/test-optimization-validation/runner-command.js +2 -4
- package/ci/test-optimization-validation/runner-contract.js +1 -2
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
- package/ci/vitest-no-worker-init-setup.mjs +10 -7
- package/index.d.ts +25 -14
- package/initialize.mjs +1 -3
- package/openfeature/rules-based.d.ts +11 -0
- package/openfeature/rules-based.js +7 -0
- package/package.json +12 -10
- package/packages/datadog-core/src/storage.js +16 -0
- package/packages/datadog-esbuild/index.js +18 -12
- package/packages/datadog-instrumentations/src/aerospike.js +3 -3
- package/packages/datadog-instrumentations/src/ai.js +3 -3
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
- package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
- package/packages/datadog-instrumentations/src/cucumber.js +48 -33
- package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
- package/packages/datadog-instrumentations/src/electron.js +6 -6
- package/packages/datadog-instrumentations/src/fastify.js +13 -4
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
- package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +36 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +87 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
- package/packages/datadog-instrumentations/src/ioredis.js +7 -5
- package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
- package/packages/datadog-instrumentations/src/jest.js +3 -10
- package/packages/datadog-instrumentations/src/knex.js +9 -5
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
- package/packages/datadog-instrumentations/src/mocha/utils.js +3 -2
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
- package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
- package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
- package/packages/datadog-instrumentations/src/nova-sonic/session.js +483 -0
- package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
- package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +5 -6
- package/packages/datadog-instrumentations/src/redis.js +27 -13
- package/packages/datadog-instrumentations/src/router.js +30 -0
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -5
- package/packages/datadog-instrumentations/src/vitest-main.js +9 -15
- package/packages/datadog-instrumentations/src/vitest-util.js +2 -2
- package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
- package/packages/datadog-instrumentations/src/vitest.js +2 -2
- package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
- package/packages/datadog-instrumentations/src/ws.js +1 -4
- package/packages/datadog-plugin-amqplib/src/client.js +1 -2
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -5
- package/packages/datadog-plugin-cypress/src/index.js +6 -6
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
- package/packages/datadog-plugin-cypress/src/support.js +1 -4
- package/packages/datadog-plugin-express/src/code_origin.js +1 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
- package/packages/datadog-plugin-graphql/src/execute.js +5 -5
- package/packages/datadog-plugin-grpc/src/util.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +7 -10
- package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
- package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/tracing.js +5 -2
- package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
- package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
- package/packages/datadog-plugin-postgres/src/index.js +24 -0
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
- package/packages/datadog-plugin-vitest/src/index.js +5 -2
- package/packages/datadog-shimmer/src/shimmer.js +5 -4
- package/packages/datadog-webpack/index.js +55 -43
- package/packages/datadog-webpack/src/loader.js +56 -8
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
- package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
- package/packages/dd-trace/src/aiguard/redaction.js +8 -2
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
- package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
- package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
- package/packages/dd-trace/src/appsec/lambda.js +1 -3
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
- package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
- package/packages/dd-trace/src/appsec/reporter.js +21 -1
- package/packages/dd-trace/src/appsec/stack_trace.js +1 -2
- package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
- package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
- package/packages/dd-trace/src/config/config-types.d.ts +2 -2
- package/packages/dd-trace/src/config/defaults.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -0
- package/packages/dd-trace/src/config/helper.js +34 -3
- package/packages/dd-trace/src/config/index.js +10 -9
- package/packages/dd-trace/src/config/normalize-service.js +1 -1
- package/packages/dd-trace/src/config/parsers.js +2 -2
- package/packages/dd-trace/src/config/supported-configurations.json +35 -0
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
- package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
- package/packages/dd-trace/src/dogstatsd.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -5
- package/packages/dd-trace/src/id.js +14 -1
- package/packages/dd-trace/src/lambda/handler.js +179 -19
- package/packages/dd-trace/src/lambda/index.js +15 -1
- package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
- package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +12 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +56 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -2
- package/packages/dd-trace/src/llmobs/tagger.js +2 -2
- package/packages/dd-trace/src/llmobs/util.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
- package/packages/dd-trace/src/log/log.js +1 -5
- package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -23
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
- package/packages/dd-trace/src/otel-sampling.js +4 -5
- package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
- package/packages/dd-trace/src/plugins/database.js +7 -16
- package/packages/dd-trace/src/plugins/util/git.js +9 -25
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
- package/packages/dd-trace/src/plugins/util/test.js +48 -4
- package/packages/dd-trace/src/priority_sampler.js +1 -2
- package/packages/dd-trace/src/profiling/profiler.js +4 -6
- package/packages/dd-trace/src/proxy.js +1 -2
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +5 -0
- package/packages/dd-trace/src/span_processor.js +13 -17
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
- package/packages/dd-trace/src/util.js +29 -0
- package/packages/dd-trace/src/web-tags-cache.js +6 -2
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
- 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/vendor/dist/shell-quote/index.js +1 -1
|
@@ -38,7 +38,7 @@ const id = require('../../id')
|
|
|
38
38
|
const DatadogSpanContext = require('../span_context')
|
|
39
39
|
const log = require('../../log')
|
|
40
40
|
const tags = require('../../../../../ext/tags')
|
|
41
|
-
const {
|
|
41
|
+
const { getValidatedConfiguredEnvName } = require('../../config/helper')
|
|
42
42
|
const { setAllBaggageItems, getAllBaggageItems, removeAllBaggageItems } = require('../../baggage')
|
|
43
43
|
const { hasTraceSourcePropagationTag } = require('../../standalone/tracesource')
|
|
44
44
|
const telemetryMetrics = require('../../telemetry/metrics')
|
|
@@ -134,8 +134,7 @@ function isOptionalDatadogTraceStateField (key, injection) {
|
|
|
134
134
|
* @returns {DatadogSpanContext | undefined}
|
|
135
135
|
*/
|
|
136
136
|
function extractGenericContext (traceId, spanId, radix) {
|
|
137
|
-
if (!traceId || invalidSegment.test(traceId)) return
|
|
138
|
-
if (!spanId) return
|
|
137
|
+
if (!traceId || invalidSegment.test(traceId) || !spanId) return
|
|
139
138
|
|
|
140
139
|
return new DatadogSpanContext({
|
|
141
140
|
traceId: id(traceId, radix),
|
|
@@ -305,13 +304,25 @@ class TextMapPropagator {
|
|
|
305
304
|
/** @type {string[] | undefined} Source array that `#baggageTagKeysSet` was built from. */
|
|
306
305
|
#baggageTagKeysSetSource
|
|
307
306
|
|
|
308
|
-
/** @type {import('../../config')} */
|
|
307
|
+
/** @type {ReturnType<import('../../config')>} */
|
|
309
308
|
#config
|
|
310
309
|
|
|
311
310
|
/** @type {typeof extractB3SingleContext | typeof extractB3MultiContext} */
|
|
312
311
|
#extractB3Context
|
|
313
312
|
|
|
314
|
-
/**
|
|
313
|
+
/** Whether the `b3` injection style resolves to single-header propagation. */
|
|
314
|
+
#b3InjectIsSingleHeader
|
|
315
|
+
|
|
316
|
+
/** Cached origin used to resolve the v5 `b3` injection style. */
|
|
317
|
+
#b3InjectOrigin
|
|
318
|
+
|
|
319
|
+
/** Cached state of the v5 experimental B3 setting. */
|
|
320
|
+
#b3ExperimentalEnabled
|
|
321
|
+
|
|
322
|
+
/** Cached origin of the shared v5 propagation style. */
|
|
323
|
+
#b3SharedOrigin
|
|
324
|
+
|
|
325
|
+
/** @param {ReturnType<import('../../config')>} config */
|
|
315
326
|
constructor (config) {
|
|
316
327
|
this.#config = config
|
|
317
328
|
|
|
@@ -319,16 +330,44 @@ class TextMapPropagator {
|
|
|
319
330
|
// expect single, legacy `DD_TRACE_PROPAGATION_STYLE` callers expect multi.
|
|
320
331
|
/* istanbul ignore else: v5 fallback */
|
|
321
332
|
if (DD_MAJOR >= 6) {
|
|
333
|
+
this.#b3InjectIsSingleHeader = true
|
|
322
334
|
this.#extractB3Context = extractB3SingleContext
|
|
323
335
|
} else {
|
|
324
|
-
const envName =
|
|
336
|
+
const envName = getValidatedConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
|
|
325
337
|
// eslint-disable-next-line eslint-rules/eslint-env-aliases
|
|
326
338
|
this.#extractB3Context = envName === 'OTEL_PROPAGATORS'
|
|
327
339
|
? extractB3SingleContext
|
|
328
340
|
: extractB3MultiContext
|
|
341
|
+
|
|
342
|
+
this.#refreshB3InjectionStyle()
|
|
329
343
|
}
|
|
330
344
|
}
|
|
331
345
|
|
|
346
|
+
#refreshB3InjectionStyle () {
|
|
347
|
+
const origin = this.#config.getOrigin?.('tracePropagationStyle.inject') ?? 'default'
|
|
348
|
+
const experimentalEnabled = this.#config.experimental?.b3 === true
|
|
349
|
+
const sharedOrigin = origin === 'calculated'
|
|
350
|
+
? this.#config.getOrigin?.('DD_TRACE_PROPAGATION_STYLE') ?? 'default'
|
|
351
|
+
: undefined
|
|
352
|
+
if (origin === this.#b3InjectOrigin &&
|
|
353
|
+
experimentalEnabled === this.#b3ExperimentalEnabled &&
|
|
354
|
+
sharedOrigin === this.#b3SharedOrigin) return
|
|
355
|
+
|
|
356
|
+
let envName
|
|
357
|
+
if (origin === 'calculated' && !experimentalEnabled && sharedOrigin !== 'remote_config') {
|
|
358
|
+
envName = getValidatedConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
|
|
359
|
+
} else if (origin === 'env_var' ||
|
|
360
|
+
origin === 'local_stable_config' ||
|
|
361
|
+
origin === 'fleet_stable_config') {
|
|
362
|
+
envName = getValidatedConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE_INJECT')
|
|
363
|
+
}
|
|
364
|
+
// eslint-disable-next-line eslint-rules/eslint-env-aliases
|
|
365
|
+
this.#b3InjectIsSingleHeader = envName === 'OTEL_PROPAGATORS'
|
|
366
|
+
this.#b3InjectOrigin = origin
|
|
367
|
+
this.#b3ExperimentalEnabled = experimentalEnabled
|
|
368
|
+
this.#b3SharedOrigin = sharedOrigin
|
|
369
|
+
}
|
|
370
|
+
|
|
332
371
|
/**
|
|
333
372
|
* Returns a `Set` view of `#config.baggageTagKeys` that is rebuilt only
|
|
334
373
|
* when the source array reference changes. Avoids an `O(n)` `Set` alloc
|
|
@@ -574,9 +613,8 @@ class TextMapPropagator {
|
|
|
574
613
|
* @returns {Record<string, string> | undefined}
|
|
575
614
|
*/
|
|
576
615
|
#injectB3MultipleHeaders (spanContext, carrier) {
|
|
577
|
-
// v5 also accepts the legacy `'b3'` spelling for multi; v6+ routes `'b3'` to single-header.
|
|
578
616
|
const hasB3multi = this.#hasPropagationStyle('inject', 'b3multi') ||
|
|
579
|
-
(
|
|
617
|
+
(this.#hasPropagationStyle('inject', 'b3') && !this.#usesB3SingleHeaderInjection())
|
|
580
618
|
if (!hasB3multi) return
|
|
581
619
|
|
|
582
620
|
carrier ??= {}
|
|
@@ -603,7 +641,7 @@ class TextMapPropagator {
|
|
|
603
641
|
#injectB3SingleHeader (spanContext, carrier) {
|
|
604
642
|
// v6+ keeps `'b3 single header'` as a back-compat alias for callers that bypass parser normalisation.
|
|
605
643
|
const hasB3SingleHeader = this.#hasPropagationStyle('inject', 'b3 single header') ||
|
|
606
|
-
(
|
|
644
|
+
(this.#hasPropagationStyle('inject', 'b3') && this.#usesB3SingleHeaderInjection())
|
|
607
645
|
if (!hasB3SingleHeader) return
|
|
608
646
|
|
|
609
647
|
carrier ??= {}
|
|
@@ -620,6 +658,11 @@ class TextMapPropagator {
|
|
|
620
658
|
return carrier
|
|
621
659
|
}
|
|
622
660
|
|
|
661
|
+
#usesB3SingleHeaderInjection () {
|
|
662
|
+
if (DD_MAJOR < 6) this.#refreshB3InjectionStyle()
|
|
663
|
+
return this.#b3InjectIsSingleHeader
|
|
664
|
+
}
|
|
665
|
+
|
|
623
666
|
/**
|
|
624
667
|
* @param {DatadogSpanContext} spanContext
|
|
625
668
|
* @param {Record<string, string> | undefined} carrier
|
|
@@ -982,11 +1025,8 @@ class TextMapPropagator {
|
|
|
982
1025
|
const traceparent = { version }
|
|
983
1026
|
// W3C Trace Context §3.3.1.1: multiple tracestate fields MUST be combined per RFC 7230 §3.2.2.
|
|
984
1027
|
const tracestate = TraceState.fromString(readTracestate(carrier))
|
|
985
|
-
if (invalidSegment.test(traceId)) return
|
|
986
|
-
if (invalidSegment.test(spanId)) return
|
|
987
|
-
|
|
988
1028
|
// Version ff is considered invalid
|
|
989
|
-
if (version === 'ff') return
|
|
1029
|
+
if (invalidSegment.test(traceId) || invalidSegment.test(spanId) || version === 'ff') return
|
|
990
1030
|
|
|
991
1031
|
// Version 00 should have no tail, but future versions may
|
|
992
1032
|
if (tail && version === '00') return
|
|
@@ -11,11 +11,10 @@ const UINT64_MASK = 2n ** 64n - 1n
|
|
|
11
11
|
/**
|
|
12
12
|
* @param {unknown} value
|
|
13
13
|
* @param {number} minLength
|
|
14
|
-
* @param {number} maxLength
|
|
15
14
|
* @returns {value is string}
|
|
16
15
|
*/
|
|
17
|
-
function isLowerHex (value, minLength
|
|
18
|
-
if (typeof value !== 'string' || value.length < minLength || value.length >
|
|
16
|
+
function isLowerHex (value, minLength) {
|
|
17
|
+
if (typeof value !== 'string' || value.length < minLength || value.length > 14) return false
|
|
19
18
|
|
|
20
19
|
for (let index = 0; index < value.length; index++) {
|
|
21
20
|
const code = value.charCodeAt(index)
|
|
@@ -112,11 +111,11 @@ function sanitizeFields (state) {
|
|
|
112
111
|
let randomValue = state.get('rv')
|
|
113
112
|
let threshold = state.get('th')
|
|
114
113
|
|
|
115
|
-
if (!isLowerHex(randomValue, 14
|
|
114
|
+
if (!isLowerHex(randomValue, 14)) {
|
|
116
115
|
state.delete('rv')
|
|
117
116
|
randomValue = undefined
|
|
118
117
|
}
|
|
119
|
-
if (!isLowerHex(threshold, 1
|
|
118
|
+
if (!isLowerHex(threshold, 1)) {
|
|
120
119
|
state.delete('th')
|
|
121
120
|
threshold = undefined
|
|
122
121
|
}
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
// and the OTel ContextManager activates spans via
|
|
21
21
|
// storage('legacy').run({ span: ddSpan }, ...), so the active span
|
|
22
22
|
// our writer reads from legacy storage is the same DatadogSpan in
|
|
23
|
-
// both cases. The
|
|
24
|
-
// _ddContext.toTraceId/toSpanId
|
|
23
|
+
// both cases. The record's trace/span IDs are read from the same
|
|
24
|
+
// Identifier bytes (and _dd.p.tid) behind _ddContext.toTraceId/toSpanId,
|
|
25
|
+
// and the spec pins them byte-for-byte to those methods.
|
|
25
26
|
|
|
26
27
|
const { isMainThread, threadId } = require('worker_threads')
|
|
27
28
|
|
|
@@ -40,6 +41,10 @@ const {
|
|
|
40
41
|
} = require('./profiling/webspan-utils')
|
|
41
42
|
const webTagsCache = require('./web-tags-cache')
|
|
42
43
|
|
|
44
|
+
/** @typedef {import('./opentracing/span')} DatadogSpan */
|
|
45
|
+
/** @typedef {import('./opentracing/span_context')} DatadogSpanContext */
|
|
46
|
+
/** @typedef {InstanceType<typeof import('@datadog/pprof').otelThreadCtx.ThreadContext>} ThreadContext */
|
|
47
|
+
|
|
43
48
|
// OTEP-4947 duplicates are last-wins, so appending a later endpoint value would
|
|
44
49
|
// correctly overwrite an earlier one — but an out-of-process reader sampling the
|
|
45
50
|
// record mid-request would still see the interim value (e.g. a bare `GET`) as
|
|
@@ -124,6 +129,7 @@ function traceFlagsOf (spanContext) {
|
|
|
124
129
|
// waiting on; undefined while the span has no web-server ancestry.
|
|
125
130
|
// Distinguishes a stale announcement from a live one when a nearer
|
|
126
131
|
// web-server span takes over.
|
|
132
|
+
// awaitsOwnEndpoint: this is the request span, waiting on its own endpoint.
|
|
127
133
|
const CachedSym = Symbol('OtelThreadCtx.cached')
|
|
128
134
|
|
|
129
135
|
let started = false
|
|
@@ -131,12 +137,59 @@ let ThreadContext
|
|
|
131
137
|
let getContext
|
|
132
138
|
let clearContext
|
|
133
139
|
|
|
140
|
+
// Reusable: the ThreadContext constructor copies the ids into its own record.
|
|
141
|
+
const traceIdBytes = Buffer.alloc(16)
|
|
142
|
+
const spanIdBytes = new Uint8Array(8)
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Same bytes as decoding toTraceId(true); unexpected shapes fall back to that.
|
|
146
|
+
*
|
|
147
|
+
* @param {DatadogSpanContext} spanContext
|
|
148
|
+
* @returns {Uint8Array}
|
|
149
|
+
*/
|
|
150
|
+
function getTraceIdBytes (spanContext) {
|
|
151
|
+
const low = spanContext._traceId.toBuffer()
|
|
152
|
+
if (low.length === 16) {
|
|
153
|
+
for (let i = 0; i < 16; i++) traceIdBytes[i] = low[i]
|
|
154
|
+
return traceIdBytes
|
|
155
|
+
}
|
|
156
|
+
if (low.length !== 8) return Buffer.from(spanContext.toTraceId(true), 'hex')
|
|
157
|
+
const high = spanContext._trace.tags['_dd.p.tid']
|
|
158
|
+
if (high) {
|
|
159
|
+
// write() stops at the first non-hex character, so only a valid _dd.p.tid fills all 8 bytes.
|
|
160
|
+
if (typeof high !== 'string' || high.length !== 16 || traceIdBytes.write(high, 0, 8, 'hex') !== 8) {
|
|
161
|
+
return Buffer.from(spanContext.toTraceId(true), 'hex')
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
for (let i = 0; i < 8; i++) traceIdBytes[i] = 0
|
|
165
|
+
}
|
|
166
|
+
for (let i = 0; i < 8; i++) traceIdBytes[i + 8] = low[i]
|
|
167
|
+
return traceIdBytes
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {DatadogSpanContext} spanContext
|
|
172
|
+
* @returns {Uint8Array}
|
|
173
|
+
*/
|
|
174
|
+
function getSpanIdBytes (spanContext) {
|
|
175
|
+
const bytes = spanContext._spanId.toBuffer()
|
|
176
|
+
if (bytes.length !== 8) return Buffer.from(spanContext.toSpanId(true), 'hex')
|
|
177
|
+
for (let i = 0; i < 8; i++) spanIdBytes[i] = bytes[i]
|
|
178
|
+
return spanIdBytes
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {DatadogSpan} span
|
|
183
|
+
* @returns {ThreadContext | undefined} The span's record, or undefined when the span's trace or span id is malformed.
|
|
184
|
+
*/
|
|
134
185
|
function getOrBuildContext (span) {
|
|
135
186
|
let cached = span[CachedSym]
|
|
136
187
|
if (cached !== undefined && cached.context !== undefined) return cached.context
|
|
137
188
|
const spanContext = span.context()
|
|
138
|
-
const traceId =
|
|
139
|
-
const spanId =
|
|
189
|
+
const traceId = getTraceIdBytes(spanContext)
|
|
190
|
+
const spanId = getSpanIdBytes(spanContext)
|
|
191
|
+
// The native constructor throws on any other id length, and onEnter runs inside user code.
|
|
192
|
+
if (traceId.length !== 16 || spanId.length !== 8) return
|
|
140
193
|
// Local root span: the first entry in the trace's started-spans list, or
|
|
141
194
|
// this span itself when it IS the root. Encoded as 16-char lowercase hex
|
|
142
195
|
// per the libdatadog convention.
|
|
@@ -152,7 +205,7 @@ function getOrBuildContext (span) {
|
|
|
152
205
|
attrs[THREAD_NAME_IDX] = THREAD_NAME
|
|
153
206
|
attrs[THREAD_ID_IDX] = THREAD_ID
|
|
154
207
|
if (cached === undefined) {
|
|
155
|
-
cached = {}
|
|
208
|
+
cached = { context: undefined, webTags: undefined, awaitsOwnEndpoint: false }
|
|
156
209
|
span[CachedSym] = cached
|
|
157
210
|
}
|
|
158
211
|
// The flags are the one part of the record that can still change after
|
|
@@ -163,17 +216,22 @@ function getOrBuildContext (span) {
|
|
|
163
216
|
// built before the decision keeps reading as not sampled.
|
|
164
217
|
cached.context = new ThreadContext(traceId, spanId, traceFlagsOf(spanContext), attrs)
|
|
165
218
|
cached.webTags = webTags
|
|
166
|
-
if (endpoint === undefined) awaitEndpoint(webTags, cached)
|
|
219
|
+
if (endpoint === undefined) awaitEndpoint(span, webTags, cached)
|
|
167
220
|
return cached.context
|
|
168
221
|
}
|
|
169
222
|
|
|
170
223
|
// Enlist a record built without an endpoint, so the resolution announcement for
|
|
171
224
|
// its request fills it in.
|
|
172
|
-
function awaitEndpoint (webTags, cached) {
|
|
225
|
+
function awaitEndpoint (span, webTags, cached) {
|
|
173
226
|
// No web-server span in this span's ancestry, so no endpoint is coming. Should
|
|
174
227
|
// one appear later, webTagsCache announces that separately — see
|
|
175
228
|
// onWebTagsResolved.
|
|
176
229
|
if (webTags === undefined) return
|
|
230
|
+
// The request span waits on its own announcement; only descendants need the shared list.
|
|
231
|
+
if (webTags === span.context().getTags()) {
|
|
232
|
+
cached.awaitsOwnEndpoint = true
|
|
233
|
+
return
|
|
234
|
+
}
|
|
177
235
|
const waiting = pendingEndpoints.get(webTags)
|
|
178
236
|
if (waiting === undefined) {
|
|
179
237
|
pendingEndpoints.set(webTags, [cached])
|
|
@@ -193,12 +251,13 @@ function appendEndpoint (context, endpoint) {
|
|
|
193
251
|
|
|
194
252
|
function onEnter () {
|
|
195
253
|
if (!started) return
|
|
196
|
-
const span = getActiveSpan()
|
|
197
|
-
|
|
198
|
-
|
|
254
|
+
const span = /** @type {DatadogSpan | null | undefined} */ (getActiveSpan())
|
|
255
|
+
const context = span && getOrBuildContext(span)
|
|
256
|
+
if (!context) {
|
|
257
|
+
// Clearing an empty frame would still make pprof install a new AsyncContextFrame.
|
|
258
|
+
if (getContext() !== undefined) clearContext()
|
|
199
259
|
return
|
|
200
260
|
}
|
|
201
|
-
const context = getOrBuildContext(span)
|
|
202
261
|
// Skip if this CPED already holds the same context. Same allocation-churn
|
|
203
262
|
// fix as the wall profiler in dd-trace-js#8638.
|
|
204
263
|
if (getContext() === context) return
|
|
@@ -246,11 +305,18 @@ function onSpanFinished (span) {
|
|
|
246
305
|
function onEndpointResolved (span) {
|
|
247
306
|
if (!started) return
|
|
248
307
|
const webTags = span.context().getTags()
|
|
308
|
+
const own = span[CachedSym]
|
|
309
|
+
const ownWaits = own !== undefined && own.awaitsOwnEndpoint
|
|
249
310
|
const waiting = pendingEndpoints.get(webTags)
|
|
250
|
-
if (waiting === undefined) return
|
|
311
|
+
if (!ownWaits && waiting === undefined) return
|
|
251
312
|
const endpoint = finalEndpoint(webTags)
|
|
252
313
|
// webTagsCache only announces settled endpoints, so this is belt and braces.
|
|
253
314
|
if (endpoint === undefined) return
|
|
315
|
+
if (ownWaits) {
|
|
316
|
+
own.awaitsOwnEndpoint = false
|
|
317
|
+
if (own.context !== undefined && own.webTags === webTags) appendEndpoint(own.context, endpoint)
|
|
318
|
+
}
|
|
319
|
+
if (waiting === undefined) return
|
|
254
320
|
pendingEndpoints.delete(webTags)
|
|
255
321
|
for (const cached of waiting) {
|
|
256
322
|
// A record whose span has since been attributed to a nearer web-server span
|
|
@@ -281,7 +347,7 @@ function onWebTagsResolved (span) {
|
|
|
281
347
|
// ThreadContext would strand every async-context frame holding this one.
|
|
282
348
|
// The work is still nested in the outer request, which makes that the least
|
|
283
349
|
// wrong of the values available.
|
|
284
|
-
awaitEndpoint(webTags, cached)
|
|
350
|
+
awaitEndpoint(span, webTags, cached)
|
|
285
351
|
} else {
|
|
286
352
|
appendEndpoint(cached.context, endpoint)
|
|
287
353
|
}
|
|
@@ -25,7 +25,7 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
25
25
|
// trails ddh — so per-query work shrinks to encoding dddb / dddbs / ddh and concatenating.
|
|
26
26
|
#dbmEnvFragment
|
|
27
27
|
#dbmEndFragment
|
|
28
|
-
// Cache the rendered prefix per `${db.name}\0${out.host}\0${
|
|
28
|
+
// Cache the rendered prefix per `${db.name}\0${out.host}\0${serviceName}`. The triple is
|
|
29
29
|
// connection-stable for a real workload, so the steady state is one `LRUCache.get` plus the
|
|
30
30
|
// optional per-call `,ddprs='...'` suffix.
|
|
31
31
|
#dbmPrefixCache = new LRUCache({ max: DBM_PREFIX_CACHE_MAX })
|
|
@@ -61,23 +61,15 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
61
61
|
this.#dbmPrefixCache.set(cacheKey, prefix)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
// `dddbs` never follows the peer service: it is always the configured database service, and the
|
|
65
|
+
// peer service is reported through `ddprs` only, and only when it was set explicitly.
|
|
66
|
+
const peerService = peerData?.[PEER_SERVICE_KEY]
|
|
67
|
+
if (peerService && peerData[PEER_SERVICE_SOURCE_KEY] === PEER_SERVICE_KEY) {
|
|
68
|
+
return `${prefix},ddprs='${encode(peerService)}'`
|
|
66
69
|
}
|
|
67
70
|
return prefix
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
/**
|
|
71
|
-
* @param {string} tracerService
|
|
72
|
-
* @param {object} peerData
|
|
73
|
-
*/
|
|
74
|
-
#getDbmServiceName (tracerService, peerData) {
|
|
75
|
-
if (this._tracerConfig.spanComputePeerService) {
|
|
76
|
-
return this.getPeerServiceRemap(peerData)[PEER_SERVICE_KEY] || tracerService
|
|
77
|
-
}
|
|
78
|
-
return tracerService
|
|
79
|
-
}
|
|
80
|
-
|
|
81
73
|
/**
|
|
82
74
|
* @param {import('../../../..').Span} span
|
|
83
75
|
* @param {string} serviceName
|
|
@@ -91,8 +83,7 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
91
83
|
}
|
|
92
84
|
|
|
93
85
|
const peerData = this.getPeerService(span.context().getTags())
|
|
94
|
-
const
|
|
95
|
-
const servicePropagation = this.#createDBMPropagationCommentService(dbmService, span, peerData)
|
|
86
|
+
const servicePropagation = this.#createDBMPropagationCommentService(serviceName, span, peerData)
|
|
96
87
|
|
|
97
88
|
let dbmComment = servicePropagation
|
|
98
89
|
|
|
@@ -45,7 +45,6 @@ const legacyStorage = storage('legacy')
|
|
|
45
45
|
const GIT_REV_LIST_MAX_BUFFER = 12 * 1024 * 1024 // 12MB
|
|
46
46
|
|
|
47
47
|
function sanitizedExec (
|
|
48
|
-
cmd,
|
|
49
48
|
flags,
|
|
50
49
|
operationMetric,
|
|
51
50
|
durationMetric,
|
|
@@ -61,7 +60,7 @@ function sanitizedExec (
|
|
|
61
60
|
startTime = Date.now()
|
|
62
61
|
}
|
|
63
62
|
try {
|
|
64
|
-
let result = cachedExec(
|
|
63
|
+
let result = cachedExec('git', flags, { stdio: 'pipe' }).toString()
|
|
65
64
|
|
|
66
65
|
if (shouldTrim) {
|
|
67
66
|
result = result.replaceAll(/(\r\n|\n|\r)/g, '')
|
|
@@ -106,7 +105,6 @@ function isGitAvailable () {
|
|
|
106
105
|
|
|
107
106
|
function isShallowRepository () {
|
|
108
107
|
return sanitizedExec(
|
|
109
|
-
'git',
|
|
110
108
|
['rev-parse', '--is-shallow-repository'],
|
|
111
109
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'check_shallow' } },
|
|
112
110
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'check_shallow' } },
|
|
@@ -115,7 +113,7 @@ function isShallowRepository () {
|
|
|
115
113
|
}
|
|
116
114
|
|
|
117
115
|
function getGitVersion () {
|
|
118
|
-
const gitVersionString = sanitizedExec(
|
|
116
|
+
const gitVersionString = sanitizedExec(['version'])
|
|
119
117
|
const gitVersionMatches = /** @type {RegExpMatch} */ (gitVersionString.match(/git version (\d+)\.(\d+)\.(\d+)/))
|
|
120
118
|
try {
|
|
121
119
|
return {
|
|
@@ -138,8 +136,8 @@ function unshallowRepository (parentOnly = false) {
|
|
|
138
136
|
log.warn('Git version is <2.27, so git unshallow will not proceed')
|
|
139
137
|
return
|
|
140
138
|
}
|
|
141
|
-
const defaultRemoteName = sanitizedExec(
|
|
142
|
-
const revParseHead = sanitizedExec(
|
|
139
|
+
const defaultRemoteName = sanitizedExec(['config', '--default', 'origin', '--get', 'clone.defaultRemoteName'])
|
|
140
|
+
const revParseHead = sanitizedExec(['rev-parse', 'HEAD'])
|
|
143
141
|
|
|
144
142
|
const baseGitOptions = [
|
|
145
143
|
'fetch',
|
|
@@ -167,7 +165,6 @@ function unshallowRepository (parentOnly = false) {
|
|
|
167
165
|
{ command: 'unshallow', errorType: err.code, exitCode: err.status || err.errno }
|
|
168
166
|
)
|
|
169
167
|
const upstreamRemote = sanitizedExec(
|
|
170
|
-
'git',
|
|
171
168
|
['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}']
|
|
172
169
|
)
|
|
173
170
|
flags = [
|
|
@@ -186,7 +183,6 @@ function unshallowRepository (parentOnly = false) {
|
|
|
186
183
|
)
|
|
187
184
|
// We use sanitizedExec here because if this last option fails, we'll give up.
|
|
188
185
|
sanitizedExec(
|
|
189
|
-
'git',
|
|
190
186
|
baseGitOptions,
|
|
191
187
|
null,
|
|
192
188
|
null,
|
|
@@ -199,7 +195,6 @@ function unshallowRepository (parentOnly = false) {
|
|
|
199
195
|
|
|
200
196
|
function getRepositoryUrl () {
|
|
201
197
|
return sanitizedExec(
|
|
202
|
-
'git',
|
|
203
198
|
['config', '--get', 'remote.origin.url'],
|
|
204
199
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'get_repository' } },
|
|
205
200
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'get_repository' } },
|
|
@@ -233,7 +228,6 @@ function getGitDiff (baseCommit, targetCommit) {
|
|
|
233
228
|
flags.push(targetCommit)
|
|
234
229
|
}
|
|
235
230
|
return sanitizedExec(
|
|
236
|
-
'git',
|
|
237
231
|
flags,
|
|
238
232
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'diff' } },
|
|
239
233
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'diff' } },
|
|
@@ -244,7 +238,6 @@ function getGitDiff (baseCommit, targetCommit) {
|
|
|
244
238
|
|
|
245
239
|
function getGitRemoteName () {
|
|
246
240
|
const upstreamRemote = sanitizedExec(
|
|
247
|
-
'git',
|
|
248
241
|
['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'],
|
|
249
242
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'get_remote_name' } },
|
|
250
243
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'get_remote_name' } },
|
|
@@ -256,7 +249,6 @@ function getGitRemoteName () {
|
|
|
256
249
|
}
|
|
257
250
|
|
|
258
251
|
const remotes = sanitizedExec(
|
|
259
|
-
'git',
|
|
260
252
|
['remote'],
|
|
261
253
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'get_remote_name' } },
|
|
262
254
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'get_remote_name' } },
|
|
@@ -269,7 +261,6 @@ function getGitRemoteName () {
|
|
|
269
261
|
|
|
270
262
|
function getSourceBranch () {
|
|
271
263
|
return sanitizedExec(
|
|
272
|
-
'git',
|
|
273
264
|
['rev-parse', '--abbrev-ref', 'HEAD'],
|
|
274
265
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'get_source_branch' } },
|
|
275
266
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'get_source_branch' } },
|
|
@@ -315,7 +306,6 @@ function checkAndFetchBranch (branch, remoteName) {
|
|
|
315
306
|
|
|
316
307
|
function getLocalBranches (remoteName) {
|
|
317
308
|
const localBranches = sanitizedExec(
|
|
318
|
-
'git',
|
|
319
309
|
['for-each-ref', '--format=%(refname:short)', `refs/remotes/${remoteName}`],
|
|
320
310
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'get_local_branches' } },
|
|
321
311
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'get_local_branches' } },
|
|
@@ -331,7 +321,6 @@ function getLocalBranches (remoteName) {
|
|
|
331
321
|
|
|
332
322
|
function getMergeBase (baseBranch, sourceBranch) {
|
|
333
323
|
return sanitizedExec(
|
|
334
|
-
'git',
|
|
335
324
|
['merge-base', baseBranch, sourceBranch],
|
|
336
325
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'get_merge_base' } },
|
|
337
326
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'get_merge_base' } },
|
|
@@ -341,7 +330,6 @@ function getMergeBase (baseBranch, sourceBranch) {
|
|
|
341
330
|
|
|
342
331
|
function getCounts (sourceBranch, candidateBranch) {
|
|
343
332
|
const counts = sanitizedExec(
|
|
344
|
-
'git',
|
|
345
333
|
['rev-list', '--left-right', '--count', `${candidateBranch}...${sourceBranch}`],
|
|
346
334
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'get_counts' } },
|
|
347
335
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'get_counts' } },
|
|
@@ -460,7 +448,6 @@ function generatePackFilesForCommits (commitsToUpload) {
|
|
|
460
448
|
|
|
461
449
|
function getRepositoryRoot () {
|
|
462
450
|
return sanitizedExec(
|
|
463
|
-
'git',
|
|
464
451
|
['rev-parse', '--show-toplevel']
|
|
465
452
|
)
|
|
466
453
|
}
|
|
@@ -488,13 +475,13 @@ function getGitMetadata (ciMetadata) {
|
|
|
488
475
|
committerName,
|
|
489
476
|
committerEmail,
|
|
490
477
|
committerDate,
|
|
491
|
-
] = sanitizedExec(
|
|
478
|
+
] = sanitizedExec(['show', '-s', '--format=%an,%ae,%aI,%cn,%ce,%cI']).split(',')
|
|
492
479
|
|
|
493
480
|
const tags = {
|
|
494
481
|
[GIT_COMMIT_MESSAGE]:
|
|
495
|
-
commitMessage || sanitizedExec(
|
|
496
|
-
[GIT_BRANCH]: branch || sanitizedExec(
|
|
497
|
-
[GIT_COMMIT_SHA]: commitSHA || sanitizedExec(
|
|
482
|
+
commitMessage || sanitizedExec(['show', '-s', '--format=%B'], null, null, null, false),
|
|
483
|
+
[GIT_BRANCH]: branch || sanitizedExec(['rev-parse', '--abbrev-ref', 'HEAD']),
|
|
484
|
+
[GIT_COMMIT_SHA]: commitSHA || sanitizedExec(['rev-parse', 'HEAD']),
|
|
498
485
|
[CI_WORKSPACE_PATH]: ciWorkspacePath || getRepositoryRoot(),
|
|
499
486
|
}
|
|
500
487
|
|
|
@@ -513,7 +500,6 @@ function getGitMetadata (ciMetadata) {
|
|
|
513
500
|
headCommitterEmail,
|
|
514
501
|
headCommitMessage,
|
|
515
502
|
] = sanitizedExec(
|
|
516
|
-
'git',
|
|
517
503
|
[
|
|
518
504
|
'show',
|
|
519
505
|
'-s',
|
|
@@ -539,7 +525,7 @@ function getGitMetadata (ciMetadata) {
|
|
|
539
525
|
|
|
540
526
|
const entries = [
|
|
541
527
|
GIT_REPOSITORY_URL,
|
|
542
|
-
filterSensitiveInfoFromRepository(repositoryUrl || sanitizedExec(
|
|
528
|
+
filterSensitiveInfoFromRepository(repositoryUrl || sanitizedExec(['ls-remote', '--get-url'])),
|
|
543
529
|
GIT_COMMIT_AUTHOR_DATE, authorDate,
|
|
544
530
|
GIT_COMMIT_AUTHOR_NAME, ciAuthorName || authorName,
|
|
545
531
|
GIT_COMMIT_AUTHOR_EMAIL, ciAuthorEmail || authorEmail,
|
|
@@ -563,7 +549,6 @@ function getGitInformationDiscrepancy () {
|
|
|
563
549
|
const gitRepositoryUrl = getRepositoryUrl()
|
|
564
550
|
|
|
565
551
|
const gitCommitSHA = sanitizedExec(
|
|
566
|
-
'git',
|
|
567
552
|
['rev-parse', 'HEAD'],
|
|
568
553
|
{ name: TELEMETRY_GIT_COMMAND, tags: { command: 'get_commit_sha' } },
|
|
569
554
|
{ name: TELEMETRY_GIT_COMMAND_MS, tags: { command: 'get_commit_sha' } },
|
|
@@ -577,7 +562,6 @@ function fetchHeadCommitSha (headSha) {
|
|
|
577
562
|
const remoteName = getGitRemoteName()
|
|
578
563
|
|
|
579
564
|
sanitizedExec(
|
|
580
|
-
'git',
|
|
581
565
|
[
|
|
582
566
|
'fetch',
|
|
583
567
|
'--update-shallow',
|
|
@@ -51,11 +51,7 @@ const supportedProxies = {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
function createInferredProxySpan (headers, childOf, tracer, reqCtx, traceCtx, config, startSpanHelper) {
|
|
54
|
-
if (!headers) {
|
|
55
|
-
return null
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!tracer._config?.inferredProxyServicesEnabled) {
|
|
54
|
+
if (!headers || !tracer._config?.inferredProxyServicesEnabled) {
|
|
59
55
|
return null
|
|
60
56
|
}
|
|
61
57
|
|
|
@@ -5,6 +5,7 @@ const fs = require('fs')
|
|
|
5
5
|
const { URL } = require('url')
|
|
6
6
|
const { stripVTControlCharacters } = require('node:util')
|
|
7
7
|
const { getLageTestSessionName } = require('../../ci-visibility/lage')
|
|
8
|
+
const resolveRunfilePath = require('../../ci-visibility/resolve-runfile-path')
|
|
8
9
|
const log = require('../../log')
|
|
9
10
|
const { getEnvironmentVariable, getValueFromEnvSources } = require('../../config/helper')
|
|
10
11
|
const { getSegment } = require('../../util')
|
|
@@ -250,8 +251,11 @@ const TEST_LEVEL_EVENT_TYPES = [
|
|
|
250
251
|
'test_module_end',
|
|
251
252
|
'test_session_end',
|
|
252
253
|
]
|
|
254
|
+
const TEST_PARALLELIZATION_ENABLED = 'test.parallelization.enabled'
|
|
255
|
+
const TEST_PARALLELIZATION_PLAN_TAG_PREFIX = 'test.parallelization.plan.'
|
|
253
256
|
const TEST_LEVELS_METADATA = 'test_levels'
|
|
254
257
|
const TEST_LEVELS_METADATA_TAGS = [
|
|
258
|
+
TEST_PARALLELIZATION_ENABLED,
|
|
255
259
|
CI_JOB_ID,
|
|
256
260
|
CI_JOB_NAME,
|
|
257
261
|
CI_JOB_URL,
|
|
@@ -809,12 +813,49 @@ function getTestEnvironmentMetadata (testFramework, config, shouldSkipGitMetadat
|
|
|
809
813
|
...userProvidedGitMetadata,
|
|
810
814
|
...runtimeAndOSMetadata,
|
|
811
815
|
}
|
|
816
|
+
if (/^(true|1)$/i.test(getEnvironmentVariable('_DD_TEST_PARALLELIZATION_ENABLED'))) {
|
|
817
|
+
metadata[TEST_PARALLELIZATION_ENABLED] = 'true'
|
|
818
|
+
Object.assign(metadata, getTestParallelizationPlanMetadata())
|
|
819
|
+
}
|
|
812
820
|
if (config && config.service) {
|
|
813
821
|
metadata['service.name'] = config.service
|
|
814
822
|
}
|
|
815
823
|
return removeInvalidMetadata(metadata)
|
|
816
824
|
}
|
|
817
825
|
|
|
826
|
+
/** @returns {Record<string, string>} */
|
|
827
|
+
function getTestParallelizationPlanMetadata () {
|
|
828
|
+
try {
|
|
829
|
+
let metadataFile = getEnvironmentVariable('_DD_TEST_PARALLELIZATION_METADATA_FILE')
|
|
830
|
+
if (!metadataFile) {
|
|
831
|
+
// The manifest locator belongs to DDTest's artifact protocol, rather than tracer configuration.
|
|
832
|
+
// eslint-disable-next-line eslint-rules/eslint-process-env
|
|
833
|
+
const env = process.env
|
|
834
|
+
const manifestFile = env.TEST_OPTIMIZATION_MANIFEST_FILE || env.DD_TEST_OPTIMIZATION_MANIFEST_FILE
|
|
835
|
+
const planDirectory = manifestFile
|
|
836
|
+
? path.dirname(resolveRunfilePath(manifestFile, env))
|
|
837
|
+
: path.join(process.cwd(), '.testoptimization')
|
|
838
|
+
metadataFile = path.join(planDirectory, 'plan-metadata.json')
|
|
839
|
+
}
|
|
840
|
+
const { tags } = JSON.parse(fs.readFileSync(metadataFile, 'utf8'))
|
|
841
|
+
if (!tags || typeof tags !== 'object' || Array.isArray(tags) ||
|
|
842
|
+
Object.values(tags).some(value => typeof value !== 'string')) {
|
|
843
|
+
return {}
|
|
844
|
+
}
|
|
845
|
+
const metadata = {}
|
|
846
|
+
for (const [tag, value] of Object.entries(tags)) {
|
|
847
|
+
if (tag.startsWith(TEST_PARALLELIZATION_PLAN_TAG_PREFIX) &&
|
|
848
|
+
tag.length > TEST_PARALLELIZATION_PLAN_TAG_PREFIX.length && value) {
|
|
849
|
+
metadata[tag] = value
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return metadata
|
|
853
|
+
} catch (err) {
|
|
854
|
+
log.debug('Failed to read DDTest plan metadata: %s', err.message)
|
|
855
|
+
return {}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
818
859
|
function getTestParametersString (parametersByTestName, testName) {
|
|
819
860
|
if (!parametersByTestName[testName]) {
|
|
820
861
|
return ''
|
|
@@ -845,6 +886,11 @@ function getTestLevelsMetadataTags (testEnvironmentMetadata) {
|
|
|
845
886
|
testLevelsMetadataTags[key] = value
|
|
846
887
|
}
|
|
847
888
|
}
|
|
889
|
+
for (const [key, value] of Object.entries(testEnvironmentMetadata)) {
|
|
890
|
+
if (key.startsWith(TEST_PARALLELIZATION_PLAN_TAG_PREFIX) && value !== undefined) {
|
|
891
|
+
testLevelsMetadataTags[key] = value
|
|
892
|
+
}
|
|
893
|
+
}
|
|
848
894
|
return testLevelsMetadataTags
|
|
849
895
|
}
|
|
850
896
|
|
|
@@ -1556,8 +1602,7 @@ function applySkippedCoverageToFileCoverage (fileCoverage, skippedBitmap) {
|
|
|
1556
1602
|
let updated = false
|
|
1557
1603
|
for (const [statementId, statementLocation] of Object.entries(fileCoverage.data.statementMap)) {
|
|
1558
1604
|
const startLine = statementLocation?.start?.line
|
|
1559
|
-
if (!isLineCoveredByBitmap(skippedBitmap, startLine)) continue
|
|
1560
|
-
if (fileCoverage.data.s[statementId] > 0) continue
|
|
1605
|
+
if (!isLineCoveredByBitmap(skippedBitmap, startLine) || fileCoverage.data.s[statementId] > 0) continue
|
|
1561
1606
|
|
|
1562
1607
|
fileCoverage.data.s[statementId] = 1
|
|
1563
1608
|
updated = true
|
|
@@ -2256,8 +2301,7 @@ function recordTestManagementExecution (execution, executions = testManagementEx
|
|
|
2256
2301
|
* }} execution
|
|
2257
2302
|
*/
|
|
2258
2303
|
function recordAttemptToFixExecution (attemptToFixExecutions, execution) {
|
|
2259
|
-
if (!execution?.testName) return
|
|
2260
|
-
if (getValueFromEnvSources('DD_TEST_MANAGEMENT_REPORT_ENABLED') === false) return
|
|
2304
|
+
if (!execution?.testName || getValueFromEnvSources('DD_TEST_MANAGEMENT_REPORT_ENABLED') === false) return
|
|
2261
2305
|
|
|
2262
2306
|
const { testSuite, testName, status, isDisabled, isQuarantined } = execution
|
|
2263
2307
|
const identity = getTestOptimizationIdentity(testSuite, testName)
|