dd-trace 5.103.0 → 5.105.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 +90 -102
- package/index.d.ts +107 -6
- package/package.json +18 -17
- package/packages/datadog-core/src/storage.js +1 -1
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/ai.js +8 -7
- package/packages/datadog-instrumentations/src/aws-sdk.js +15 -2
- package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
- package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
- package/packages/datadog-instrumentations/src/cucumber.js +181 -35
- package/packages/datadog-instrumentations/src/dns.js +54 -18
- package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
- package/packages/datadog-instrumentations/src/fastify.js +142 -82
- package/packages/datadog-instrumentations/src/graphql.js +188 -67
- package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
- package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/kafka.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
- package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +31 -229
- package/packages/datadog-instrumentations/src/hono.js +54 -3
- package/packages/datadog-instrumentations/src/http/client.js +2 -2
- package/packages/datadog-instrumentations/src/http/server.js +9 -4
- package/packages/datadog-instrumentations/src/ioredis.js +3 -3
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
- package/packages/datadog-instrumentations/src/jest.js +390 -183
- package/packages/datadog-instrumentations/src/kafkajs.js +140 -17
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/memcached.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +399 -107
- package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
- package/packages/datadog-instrumentations/src/mongodb-core.js +1 -1
- package/packages/datadog-instrumentations/src/mongoose.js +10 -12
- package/packages/datadog-instrumentations/src/mysql.js +2 -2
- package/packages/datadog-instrumentations/src/mysql2.js +1 -1
- package/packages/datadog-instrumentations/src/nats.js +182 -0
- package/packages/datadog-instrumentations/src/nyc.js +38 -1
- package/packages/datadog-instrumentations/src/openai.js +33 -18
- package/packages/datadog-instrumentations/src/oracledb.js +6 -1
- package/packages/datadog-instrumentations/src/pg.js +1 -1
- package/packages/datadog-instrumentations/src/pino.js +17 -5
- package/packages/datadog-instrumentations/src/playwright.js +537 -297
- package/packages/datadog-instrumentations/src/router.js +80 -34
- package/packages/datadog-instrumentations/src/stripe.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +246 -149
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
- package/packages/datadog-plugin-bunyan/src/index.js +28 -0
- package/packages/datadog-plugin-cucumber/src/index.js +17 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +223 -45
- package/packages/datadog-plugin-cypress/src/support.js +69 -1
- package/packages/datadog-plugin-dns/src/lookup.js +8 -6
- package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +2 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
- package/packages/datadog-plugin-graphql/src/utils.js +4 -1
- package/packages/datadog-plugin-http/src/server.js +40 -15
- package/packages/datadog-plugin-jest/src/index.js +11 -3
- package/packages/datadog-plugin-jest/src/util.js +15 -8
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +35 -0
- package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +19 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +311 -35
- package/packages/datadog-plugin-nats/src/consumer.js +43 -0
- package/packages/datadog-plugin-nats/src/index.js +20 -0
- package/packages/datadog-plugin-nats/src/producer.js +62 -0
- package/packages/datadog-plugin-nats/src/util.js +33 -0
- package/packages/datadog-plugin-next/src/index.js +5 -3
- package/packages/datadog-plugin-openai/src/tracing.js +15 -2
- package/packages/datadog-plugin-oracledb/src/index.js +13 -2
- package/packages/datadog-plugin-pino/src/index.js +42 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -4
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-redis/src/index.js +37 -2
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +33 -44
- package/packages/datadog-plugin-selenium/src/index.js +1 -1
- package/packages/datadog-plugin-undici/src/index.js +19 -0
- package/packages/datadog-plugin-vitest/src/index.js +24 -20
- package/packages/datadog-plugin-winston/src/index.js +30 -0
- package/packages/datadog-shimmer/src/shimmer.js +49 -21
- package/packages/dd-trace/src/aiguard/index.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/blocking.js +2 -2
- package/packages/dd-trace/src/appsec/index.js +11 -4
- package/packages/dd-trace/src/appsec/reporter.js +24 -11
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
- package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
- package/packages/dd-trace/src/baggage.js +7 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
- package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -2
- package/packages/dd-trace/src/config/supported-configurations.json +36 -8
- package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
- package/packages/dd-trace/src/datastreams/context.js +4 -2
- package/packages/dd-trace/src/datastreams/writer.js +2 -4
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
- package/packages/dd-trace/src/encode/0.4.js +124 -108
- package/packages/dd-trace/src/encode/0.5.js +114 -26
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +57 -42
- package/packages/dd-trace/src/encode/agentless-json.js +4 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
- package/packages/dd-trace/src/encode/span-stats.js +16 -16
- package/packages/dd-trace/src/encode/tags-processors.js +16 -0
- package/packages/dd-trace/src/exporters/common/agents.js +3 -1
- package/packages/dd-trace/src/exporters/common/request.js +3 -1
- package/packages/dd-trace/src/id.js +17 -4
- package/packages/dd-trace/src/lambda/handler.js +2 -4
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +10 -16
- package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
- package/packages/dd-trace/src/llmobs/tagger.js +9 -1
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/util.js +66 -3
- package/packages/dd-trace/src/log/index.js +1 -1
- package/packages/dd-trace/src/log/writer.js +3 -1
- package/packages/dd-trace/src/msgpack/chunk.js +394 -10
- package/packages/dd-trace/src/msgpack/index.js +96 -2
- package/packages/dd-trace/src/noop/span.js +3 -1
- package/packages/dd-trace/src/openfeature/encoding.js +70 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
- package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
- package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
- package/packages/dd-trace/src/opentracing/span.js +59 -19
- package/packages/dd-trace/src/opentracing/span_context.js +49 -0
- package/packages/dd-trace/src/plugins/apollo.js +3 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +23 -33
- package/packages/dd-trace/src/plugins/database.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +4 -0
- package/packages/dd-trace/src/plugins/log_injection.js +56 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -46
- package/packages/dd-trace/src/plugins/outbound.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +15 -17
- package/packages/dd-trace/src/plugins/tracing.js +48 -8
- package/packages/dd-trace/src/plugins/util/git.js +3 -1
- package/packages/dd-trace/src/plugins/util/test.js +318 -13
- package/packages/dd-trace/src/plugins/util/web.js +89 -64
- package/packages/dd-trace/src/priority_sampler.js +2 -2
- package/packages/dd-trace/src/profiling/profiler.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
- package/packages/dd-trace/src/sampling_rule.js +7 -7
- package/packages/dd-trace/src/scope.js +7 -5
- package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
- package/packages/dd-trace/src/span_format.js +190 -58
- package/packages/dd-trace/src/spanleak.js +1 -1
- package/packages/dd-trace/src/standalone/index.js +3 -3
- package/packages/dd-trace/src/tagger.js +0 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
- package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
- 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/dd-trace/src/msgpack/encoder.js +0 -308
- package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
- package/vendor/dist/opentracing/LICENSE +0 -201
- package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
- package/vendor/dist/opentracing/constants.d.ts +0 -61
- package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
- package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
- package/vendor/dist/opentracing/functions.d.ts +0 -20
- package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
- package/vendor/dist/opentracing/index.d.ts +0 -12
- package/vendor/dist/opentracing/index.js +0 -1
- package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
- package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
- package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
- package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
- package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
- package/vendor/dist/opentracing/noop.d.ts +0 -8
- package/vendor/dist/opentracing/reference.d.ts +0 -33
- package/vendor/dist/opentracing/span.d.ts +0 -147
- package/vendor/dist/opentracing/span_context.d.ts +0 -26
- package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
- package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
- package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
- package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
- package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
- package/vendor/dist/opentracing/tracer.d.ts +0 -127
|
@@ -229,11 +229,11 @@ const BASE_LIKE_BRANCH_FILTER = /^(main|master|preprod|prod|dev|development|trun
|
|
|
229
229
|
|
|
230
230
|
/**
|
|
231
231
|
* Returns request error tags from a test session span for propagation to child events.
|
|
232
|
-
* @param {{ context: () => {
|
|
232
|
+
* @param {{ context: () => { getTag?: (key: string) => string } } | undefined} sessionSpan
|
|
233
233
|
* @returns {Record<string, string>}
|
|
234
234
|
*/
|
|
235
235
|
function getSessionRequestErrorTags (sessionSpan) {
|
|
236
|
-
const tags = sessionSpan?.context()
|
|
236
|
+
const tags = sessionSpan?.context()?.getTags?.()
|
|
237
237
|
const sessionRequestErrorTags = {}
|
|
238
238
|
if (!tags || typeof tags !== 'object') return {}
|
|
239
239
|
if (tags[DD_CI_LIBRARY_CONFIGURATION_ERROR_SETTINGS] === 'true') {
|
|
@@ -253,11 +253,11 @@ function getSessionRequestErrorTags (sessionSpan) {
|
|
|
253
253
|
|
|
254
254
|
/**
|
|
255
255
|
* Returns ITR skipping-enabled tags from a test session span for propagation to child events.
|
|
256
|
-
* @param {{ context: () => {
|
|
256
|
+
* @param {{ context: () => { getTags?: () => Record<string, string> } } | undefined} sessionSpan
|
|
257
257
|
* @returns {Record<string, string>}
|
|
258
258
|
*/
|
|
259
259
|
function getSessionItrSkippingEnabledTags (sessionSpan) {
|
|
260
|
-
const tags = sessionSpan?.context()
|
|
260
|
+
const tags = sessionSpan?.context()?.getTags?.()
|
|
261
261
|
if (!tags || typeof tags !== 'object') return {}
|
|
262
262
|
if (tags[TEST_ITR_SKIPPING_ENABLED] !== undefined) {
|
|
263
263
|
return {
|
|
@@ -267,6 +267,87 @@ function getSessionItrSkippingEnabledTags (sessionSpan) {
|
|
|
267
267
|
return {}
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Starts supported test optimization requests together when each feature is enabled.
|
|
272
|
+
*
|
|
273
|
+
* @param {{
|
|
274
|
+
* isKnownTestsEnabled: boolean,
|
|
275
|
+
* isTestManagementTestsEnabled: boolean,
|
|
276
|
+
* isSuitesSkippingEnabled?: boolean,
|
|
277
|
+
* getKnownTests: () => Promise<object>,
|
|
278
|
+
* getTestManagementTests: () => Promise<object>,
|
|
279
|
+
* getSkippableSuites?: () => Promise<object>
|
|
280
|
+
* }} options - Test optimization request factories.
|
|
281
|
+
* @returns {Promise<{
|
|
282
|
+
* knownTestsResponse?: object,
|
|
283
|
+
* testManagementTestsResponse?: object,
|
|
284
|
+
* skippableSuitesResponse?: object
|
|
285
|
+
* }>}
|
|
286
|
+
*/
|
|
287
|
+
function getTestOptimizationRequestResults ({
|
|
288
|
+
isKnownTestsEnabled,
|
|
289
|
+
isTestManagementTestsEnabled,
|
|
290
|
+
isSuitesSkippingEnabled,
|
|
291
|
+
getKnownTests,
|
|
292
|
+
getTestManagementTests,
|
|
293
|
+
getSkippableSuites,
|
|
294
|
+
}) {
|
|
295
|
+
const requestPromises = []
|
|
296
|
+
const responseNames = []
|
|
297
|
+
|
|
298
|
+
if (isKnownTestsEnabled) {
|
|
299
|
+
addTestOptimizationRequest(requestPromises, responseNames, 'knownTestsResponse', getKnownTests)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (isTestManagementTestsEnabled) {
|
|
303
|
+
addTestOptimizationRequest(
|
|
304
|
+
requestPromises,
|
|
305
|
+
responseNames,
|
|
306
|
+
'testManagementTestsResponse',
|
|
307
|
+
getTestManagementTests
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (isSuitesSkippingEnabled && getSkippableSuites) {
|
|
312
|
+
addTestOptimizationRequest(requestPromises, responseNames, 'skippableSuitesResponse', getSkippableSuites)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (!requestPromises.length) {
|
|
316
|
+
return Promise.resolve({})
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return Promise.allSettled(requestPromises).then(requestResults => {
|
|
320
|
+
const responses = {}
|
|
321
|
+
|
|
322
|
+
for (let index = 0; index < requestResults.length; index++) {
|
|
323
|
+
const requestResult = requestResults[index]
|
|
324
|
+
responses[responseNames[index]] = requestResult.status === 'fulfilled'
|
|
325
|
+
? requestResult.value
|
|
326
|
+
: { err: requestResult.reason }
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return responses
|
|
330
|
+
})
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Starts a test optimization request.
|
|
335
|
+
*
|
|
336
|
+
* @param {Promise<object>[]} requestPromises - Test optimization request promises.
|
|
337
|
+
* @param {string[]} responseNames - Response keys matching request promises.
|
|
338
|
+
* @param {string} responseName - Response key for this request.
|
|
339
|
+
* @param {() => Promise<object>} getRequest - Test optimization request factory.
|
|
340
|
+
*/
|
|
341
|
+
function addTestOptimizationRequest (requestPromises, responseNames, responseName, getRequest) {
|
|
342
|
+
responseNames.push(responseName)
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
requestPromises.push(Promise.resolve(getRequest()))
|
|
346
|
+
} catch (err) {
|
|
347
|
+
requestPromises.push(Promise.reject(err))
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
270
351
|
module.exports = {
|
|
271
352
|
TEST_CODE_OWNERS,
|
|
272
353
|
TEST_SESSION_NAME,
|
|
@@ -337,6 +418,12 @@ module.exports = {
|
|
|
337
418
|
ITR_CORRELATION_ID,
|
|
338
419
|
addIntelligentTestRunnerSpanTags,
|
|
339
420
|
getCoveredFilenamesFromCoverage,
|
|
421
|
+
getCoveredFilesFromCoverage,
|
|
422
|
+
getExecutableFilesFromCoverage,
|
|
423
|
+
getRelativeCoverageFiles,
|
|
424
|
+
getLineCoverageBitmap,
|
|
425
|
+
applySkippedCoverageToCoverage,
|
|
426
|
+
getTestCoverageLinesPercentage,
|
|
340
427
|
resetCoverage,
|
|
341
428
|
mergeCoverage,
|
|
342
429
|
fromCoverageMapToCoverage,
|
|
@@ -383,6 +470,7 @@ module.exports = {
|
|
|
383
470
|
DD_CI_LIBRARY_CONFIGURATION_ERROR_KNOWN_TESTS,
|
|
384
471
|
DD_CI_LIBRARY_CONFIGURATION_ERROR_TEST_MANAGEMENT_TESTS,
|
|
385
472
|
getSessionItrSkippingEnabledTags,
|
|
473
|
+
getTestOptimizationRequestResults,
|
|
386
474
|
checkShaDiscrepancies,
|
|
387
475
|
getPullRequestDiff,
|
|
388
476
|
getPullRequestBaseBranch,
|
|
@@ -870,7 +958,6 @@ function getTestLevelCommonTags (command, testFrameworkVersion, testFramework) {
|
|
|
870
958
|
return {
|
|
871
959
|
[TEST_FRAMEWORK_VERSION]: testFrameworkVersion,
|
|
872
960
|
[LIBRARY_VERSION]: ddTraceVersion,
|
|
873
|
-
[TEST_COMMAND]: command,
|
|
874
961
|
[TEST_TYPE]: getTestTypeFromFramework(testFramework),
|
|
875
962
|
}
|
|
876
963
|
}
|
|
@@ -948,15 +1035,233 @@ function addIntelligentTestRunnerSpanTags (
|
|
|
948
1035
|
}
|
|
949
1036
|
|
|
950
1037
|
function getCoveredFilenamesFromCoverage (coverage) {
|
|
951
|
-
|
|
1038
|
+
return getCoveredFilesFromCoverage(coverage).map(({ filename }) => filename)
|
|
1039
|
+
}
|
|
952
1040
|
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1041
|
+
function getCoverageMap (coverage) {
|
|
1042
|
+
if (coverage?.files && coverage?.fileCoverageFor) {
|
|
1043
|
+
return coverage
|
|
1044
|
+
}
|
|
1045
|
+
return istanbul.createCoverageMap(coverage)
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function getCoveredFilesFromCoverage (coverage) {
|
|
1049
|
+
const coverageMap = getCoverageMap(coverage)
|
|
1050
|
+
const coverageFiles = []
|
|
1051
|
+
|
|
1052
|
+
for (const filename of coverageMap.files()) {
|
|
1053
|
+
const fileCoverage = coverageMap.fileCoverageFor(filename)
|
|
1054
|
+
const bitmap = getLineCoverageBitmap(fileCoverage.getLineCoverage(), true)
|
|
1055
|
+
if (bitmap) {
|
|
1056
|
+
coverageFiles.push({ filename, bitmap })
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
return coverageFiles
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function getExecutableFilesFromCoverage (coverage) {
|
|
1064
|
+
const coverageMap = getCoverageMap(coverage)
|
|
1065
|
+
const coverageFiles = []
|
|
1066
|
+
|
|
1067
|
+
for (const filename of coverageMap.files()) {
|
|
1068
|
+
const fileCoverage = coverageMap.fileCoverageFor(filename)
|
|
1069
|
+
const bitmap = getLineCoverageBitmap(fileCoverage.getLineCoverage())
|
|
1070
|
+
if (bitmap) {
|
|
1071
|
+
coverageFiles.push({ filename, bitmap })
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
return coverageFiles
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
function getRelativeCoverageFiles (coverageFiles, rootDir) {
|
|
1079
|
+
return coverageFiles.map(({ filename, bitmap }) => ({
|
|
1080
|
+
filename: getTestSuitePath(filename, rootDir),
|
|
1081
|
+
bitmap,
|
|
1082
|
+
}))
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function getLineCoverageBitmap (lineCoverage, onlyCoveredLines = false) {
|
|
1086
|
+
let maxLine = 0
|
|
1087
|
+
const lines = []
|
|
1088
|
+
|
|
1089
|
+
for (const [line, hits] of Object.entries(lineCoverage)) {
|
|
1090
|
+
if (onlyCoveredLines && !hits) continue
|
|
1091
|
+
|
|
1092
|
+
const lineNumber = Number(line)
|
|
1093
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber <= 0) continue
|
|
1094
|
+
|
|
1095
|
+
lines.push(lineNumber)
|
|
1096
|
+
if (lineNumber > maxLine) {
|
|
1097
|
+
maxLine = lineNumber
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (maxLine === 0) return
|
|
1102
|
+
|
|
1103
|
+
const bitmap = Buffer.alloc(Math.ceil((maxLine + 1) / 8))
|
|
1104
|
+
for (const lineNumber of lines) {
|
|
1105
|
+
bitmap[lineNumber >> 3] |= 1 << (lineNumber % 8)
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
return bitmap
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function mergeCoverageBitmaps (targetBitmap, bitmap) {
|
|
1112
|
+
if (!targetBitmap) {
|
|
1113
|
+
return Buffer.from(bitmap)
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
if (targetBitmap.length < bitmap.length) {
|
|
1117
|
+
const biggerBitmap = Buffer.alloc(bitmap.length)
|
|
1118
|
+
targetBitmap.copy(biggerBitmap)
|
|
1119
|
+
targetBitmap = biggerBitmap
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
for (let i = 0; i < bitmap.length; i++) {
|
|
1123
|
+
targetBitmap[i] |= bitmap[i]
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
return targetBitmap
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function countBitmapBits (bitmap) {
|
|
1130
|
+
let count = 0
|
|
1131
|
+
|
|
1132
|
+
for (const byte of bitmap) {
|
|
1133
|
+
let value = byte
|
|
1134
|
+
while (value) {
|
|
1135
|
+
value &= value - 1
|
|
1136
|
+
count++
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
return count
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
function countCoveredExecutableBits (coveredBitmap, executableBitmap) {
|
|
1144
|
+
if (!coveredBitmap) return 0
|
|
1145
|
+
|
|
1146
|
+
let count = 0
|
|
1147
|
+
const length = Math.min(coveredBitmap.length, executableBitmap.length)
|
|
1148
|
+
|
|
1149
|
+
for (let i = 0; i < length; i++) {
|
|
1150
|
+
let value = coveredBitmap[i] & executableBitmap[i]
|
|
1151
|
+
while (value) {
|
|
1152
|
+
value &= value - 1
|
|
1153
|
+
count++
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
return count
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function getCoverageFileBitmap (bitmap) {
|
|
1161
|
+
if (!bitmap) return
|
|
1162
|
+
if (Buffer.isBuffer(bitmap)) return bitmap
|
|
1163
|
+
if (ArrayBuffer.isView(bitmap)) {
|
|
1164
|
+
return Buffer.from(bitmap.buffer, bitmap.byteOffset, bitmap.byteLength)
|
|
1165
|
+
}
|
|
1166
|
+
if (typeof bitmap === 'string') {
|
|
1167
|
+
return Buffer.from(bitmap, 'base64')
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function addCoverageFilesToMap (files, targetMap, rootDir) {
|
|
1172
|
+
for (const file of files) {
|
|
1173
|
+
const bitmap = getCoverageFileBitmap(file.bitmap)
|
|
1174
|
+
if (!bitmap) continue
|
|
1175
|
+
|
|
1176
|
+
const filename = rootDir ? getTestSuitePath(file.filename, rootDir) : file.filename
|
|
1177
|
+
targetMap.set(filename, mergeCoverageBitmaps(targetMap.get(filename), bitmap))
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
function addSkippedCoverageToMap (skippedCoverage, targetMap) {
|
|
1182
|
+
if (!skippedCoverage) return
|
|
1183
|
+
|
|
1184
|
+
for (const [filename, bitmap] of Object.entries(skippedCoverage)) {
|
|
1185
|
+
const coverageBitmap = getCoverageFileBitmap(bitmap)
|
|
1186
|
+
if (!coverageBitmap) continue
|
|
1187
|
+
targetMap.set(filename, mergeCoverageBitmaps(targetMap.get(filename), coverageBitmap))
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
function hasSkippedCoverage (skippedCoverage) {
|
|
1192
|
+
return skippedCoverage && typeof skippedCoverage === 'object' && Object.keys(skippedCoverage).length > 0
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
function getTestCoverageLinesPercentage (coverage, skippedCoverage, rootDir) {
|
|
1196
|
+
const executableLinesByFile = new Map()
|
|
1197
|
+
const coveredLinesByFile = new Map()
|
|
1198
|
+
|
|
1199
|
+
addCoverageFilesToMap(getExecutableFilesFromCoverage(coverage), executableLinesByFile, rootDir)
|
|
1200
|
+
addCoverageFilesToMap(getCoveredFilesFromCoverage(coverage), coveredLinesByFile, rootDir)
|
|
1201
|
+
addSkippedCoverageToMap(skippedCoverage, coveredLinesByFile)
|
|
1202
|
+
|
|
1203
|
+
let totalExecutableLines = 0
|
|
1204
|
+
let totalCoveredLines = 0
|
|
1205
|
+
|
|
1206
|
+
for (const [filename, executableLines] of executableLinesByFile) {
|
|
1207
|
+
totalExecutableLines += countBitmapBits(executableLines)
|
|
1208
|
+
totalCoveredLines += countCoveredExecutableBits(coveredLinesByFile.get(filename), executableLines)
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
return totalExecutableLines === 0 ? 0 : Math.floor((totalCoveredLines / totalExecutableLines) * 10_000) / 100
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function isLineCoveredByBitmap (bitmap, line) {
|
|
1215
|
+
if (!Number.isSafeInteger(line) || line <= 0) return false
|
|
1216
|
+
|
|
1217
|
+
const byteIndex = line >> 3
|
|
1218
|
+
return byteIndex < bitmap.length && !!(bitmap[byteIndex] & (1 << (line % 8)))
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function getSkippedCoverageByFilename (skippedCoverage) {
|
|
1222
|
+
const skippedCoverageByFilename = new Map()
|
|
1223
|
+
addSkippedCoverageToMap(skippedCoverage, skippedCoverageByFilename)
|
|
1224
|
+
return skippedCoverageByFilename
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
function applySkippedCoverageToFileCoverage (fileCoverage, skippedBitmap) {
|
|
1228
|
+
let updated = false
|
|
1229
|
+
for (const [statementId, statementLocation] of Object.entries(fileCoverage.data.statementMap)) {
|
|
1230
|
+
const startLine = statementLocation?.start?.line
|
|
1231
|
+
if (!isLineCoveredByBitmap(skippedBitmap, startLine)) continue
|
|
1232
|
+
if (fileCoverage.data.s[statementId] > 0) continue
|
|
1233
|
+
|
|
1234
|
+
fileCoverage.data.s[statementId] = 1
|
|
1235
|
+
updated = true
|
|
1236
|
+
}
|
|
1237
|
+
return updated
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Applies backend skipped-suite coverage to an Istanbul coverage map.
|
|
1242
|
+
* @param {object} coverage
|
|
1243
|
+
* @param {object} skippedCoverage
|
|
1244
|
+
* @param {string} [rootDir]
|
|
1245
|
+
* @returns {boolean}
|
|
1246
|
+
*/
|
|
1247
|
+
function applySkippedCoverageToCoverage (coverage, skippedCoverage, rootDir) {
|
|
1248
|
+
if (!hasSkippedCoverage(skippedCoverage)) return false
|
|
1249
|
+
|
|
1250
|
+
const coverageMap = getCoverageMap(coverage)
|
|
1251
|
+
const skippedCoverageByFilename = getSkippedCoverageByFilename(skippedCoverage)
|
|
1252
|
+
let matched = false
|
|
1253
|
+
|
|
1254
|
+
for (const filename of coverageMap.files()) {
|
|
1255
|
+
const relativeFilename = rootDir ? getTestSuitePath(filename, rootDir) : filename
|
|
1256
|
+
const skippedBitmap = skippedCoverageByFilename.get(relativeFilename)
|
|
1257
|
+
if (!skippedBitmap) continue
|
|
1258
|
+
|
|
1259
|
+
const fileCoverage = coverageMap.fileCoverageFor(filename)
|
|
1260
|
+
applySkippedCoverageToFileCoverage(fileCoverage, skippedBitmap)
|
|
1261
|
+
matched = true
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
return matched
|
|
960
1265
|
}
|
|
961
1266
|
|
|
962
1267
|
function resetCoverage (coverage) {
|
|
@@ -10,16 +10,14 @@ const kinds = require('../../../../../ext/kinds')
|
|
|
10
10
|
const { ERROR_MESSAGE } = require('../../constants')
|
|
11
11
|
const TracingPlugin = require('../tracing')
|
|
12
12
|
const { storage } = require('../../../../datadog-core')
|
|
13
|
+
const legacyStorage = storage('legacy')
|
|
13
14
|
const urlFilter = require('./urlfilter')
|
|
14
15
|
const { createInferredProxySpan, finishInferredProxySpan } = require('./inferred_proxy')
|
|
15
16
|
const { extractURL, obfuscateQs, calculateHttpEndpoint } = require('./url')
|
|
16
17
|
|
|
17
|
-
let extractIp
|
|
18
|
-
|
|
19
18
|
const WEB = types.WEB
|
|
20
19
|
const SERVER = kinds.SERVER
|
|
21
20
|
const RESOURCE_NAME = tags.RESOURCE_NAME
|
|
22
|
-
const SERVICE_NAME = tags.SERVICE_NAME
|
|
23
21
|
const SPAN_TYPE = tags.SPAN_TYPE
|
|
24
22
|
const SPAN_KIND = tags.SPAN_KIND
|
|
25
23
|
const ERROR = tags.ERROR
|
|
@@ -35,7 +33,6 @@ const HTTP_CLIENT_IP = tags.HTTP_CLIENT_IP
|
|
|
35
33
|
const MANUAL_DROP = tags.MANUAL_DROP
|
|
36
34
|
|
|
37
35
|
const contexts = new WeakMap()
|
|
38
|
-
const ends = new WeakMap()
|
|
39
36
|
|
|
40
37
|
// TODO: change this to no longer rely on creating a dummy plugin to be able to access startSpan
|
|
41
38
|
function createWebPlugin (tracer, config = {}) {
|
|
@@ -67,7 +64,9 @@ const web = {
|
|
|
67
64
|
const middleware = getMiddlewareSetting(config)
|
|
68
65
|
const queryStringObfuscation = getQsObfuscator(config)
|
|
69
66
|
|
|
70
|
-
extractIp = config.clientIpEnabled
|
|
67
|
+
const extractIp = config.clientIpEnabled
|
|
68
|
+
? require('./ip_extractor').extractIp
|
|
69
|
+
: undefined
|
|
71
70
|
|
|
72
71
|
return {
|
|
73
72
|
...config,
|
|
@@ -77,6 +76,7 @@ const web = {
|
|
|
77
76
|
filter,
|
|
78
77
|
middleware,
|
|
79
78
|
queryStringObfuscation,
|
|
79
|
+
extractIp,
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
|
|
@@ -87,7 +87,7 @@ const web = {
|
|
|
87
87
|
if (!span) return
|
|
88
88
|
|
|
89
89
|
span.context()._name = `${name}.request`
|
|
90
|
-
span.context().
|
|
90
|
+
span.context().setTag('component', name)
|
|
91
91
|
span._integrationName = name
|
|
92
92
|
|
|
93
93
|
web.setConfig(req, config)
|
|
@@ -105,7 +105,7 @@ const web = {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
if (config.service) {
|
|
108
|
-
|
|
108
|
+
web.plugin.setServiceName(span, config.service)
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
analyticsSampler.sample(span, config.measured, true)
|
|
@@ -126,7 +126,6 @@ const web = {
|
|
|
126
126
|
context.tracer = tracer
|
|
127
127
|
context.span = span
|
|
128
128
|
context.res = res
|
|
129
|
-
context.store = storage('legacy').getStore()
|
|
130
129
|
|
|
131
130
|
this.setConfig(req, config)
|
|
132
131
|
addRequestTags(context, this.TYPE)
|
|
@@ -204,7 +203,7 @@ const web = {
|
|
|
204
203
|
startServerlessSpanWithInferredProxy (tracer, config, name, req, traceCtx) {
|
|
205
204
|
const headers = req.headers
|
|
206
205
|
const reqCtx = contexts.get(req)
|
|
207
|
-
const store =
|
|
206
|
+
const store = legacyStorage.getStore()
|
|
208
207
|
const pubsubSpan = store?.span?._name === 'pubsub.push.receive' ? store.span : null
|
|
209
208
|
|
|
210
209
|
let childOf = pubsubSpan || tracer.extract(FORMAT_HTTP_HEADERS, headers)
|
|
@@ -225,9 +224,11 @@ const web = {
|
|
|
225
224
|
const context = contexts.get(req)
|
|
226
225
|
const { span, inferredProxySpan, error } = context
|
|
227
226
|
|
|
228
|
-
const
|
|
227
|
+
const spanContext = span.context()
|
|
228
|
+
const spanHasExistingError = spanContext.getTag('error') || spanContext.getTag(ERROR_MESSAGE)
|
|
229
229
|
const inferredSpanContext = inferredProxySpan?.context()
|
|
230
|
-
const inferredSpanHasExistingError = inferredSpanContext?.
|
|
230
|
+
const inferredSpanHasExistingError = inferredSpanContext?.getTag('error') ||
|
|
231
|
+
inferredSpanContext?.getTag(ERROR_MESSAGE)
|
|
231
232
|
|
|
232
233
|
const isValidStatusCode = context.config.validateStatus(statusCode)
|
|
233
234
|
|
|
@@ -266,7 +267,16 @@ const web = {
|
|
|
266
267
|
|
|
267
268
|
if (context.finished && !req.stream) return
|
|
268
269
|
|
|
270
|
+
// `addRequestTags` is idempotent: in the normal HTTP path it ran during
|
|
271
|
+
// `web.startSpan`. Serverless callers (e.g. Azure Functions) skip
|
|
272
|
+
// `web.startSpan` and rely on this call to do the request-side work.
|
|
269
273
|
addRequestTags(context, spanType)
|
|
274
|
+
// Configured-header tagging runs at finish time. Framework plugins
|
|
275
|
+
// (connect, express, ...) install their own config via `setFramework`
|
|
276
|
+
// after `web.startSpan` has already locked the http-plugin config in;
|
|
277
|
+
// tagging earlier would use the http-plugin's `headers` list and drop
|
|
278
|
+
// the framework's.
|
|
279
|
+
addRequestHeaders(context)
|
|
270
280
|
addResponseTags(context)
|
|
271
281
|
|
|
272
282
|
context.config.hooks.request(context.span, req, res)
|
|
@@ -293,11 +303,18 @@ const web = {
|
|
|
293
303
|
const writeHead = res.writeHead
|
|
294
304
|
|
|
295
305
|
return function (statusCode, statusMessage, headers) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
306
|
+
// CORS preflight tagging only matters for OPTIONS requests. Skip the
|
|
307
|
+
// getHeaders() spread + isOriginAllowed work entirely for the common
|
|
308
|
+
// GET / POST / etc. case. Node's http module passes `req.method`
|
|
309
|
+
// through unchanged, so all standard methods are uppercase; the
|
|
310
|
+
// `toLowerCase` fallback covers any non-standard caller.
|
|
311
|
+
if (req.method === 'OPTIONS' || req.method.toLowerCase() === 'options') {
|
|
312
|
+
headers = typeof statusMessage === 'string' ? headers : statusMessage
|
|
313
|
+
headers = { ...res.getHeaders(), ...headers }
|
|
314
|
+
|
|
315
|
+
if (isOriginAllowed(req, headers)) {
|
|
316
|
+
addAllowHeaders(req, res, headers)
|
|
317
|
+
}
|
|
301
318
|
}
|
|
302
319
|
|
|
303
320
|
return writeHead.apply(this, arguments)
|
|
@@ -306,34 +323,6 @@ const web = {
|
|
|
306
323
|
getContext (req) {
|
|
307
324
|
return contexts.get(req)
|
|
308
325
|
},
|
|
309
|
-
wrapRes (context, req, res, end) {
|
|
310
|
-
return function (...args) {
|
|
311
|
-
web.finishAll(context)
|
|
312
|
-
|
|
313
|
-
return end.apply(res, args)
|
|
314
|
-
}
|
|
315
|
-
},
|
|
316
|
-
wrapEnd (context) {
|
|
317
|
-
const req = context.req
|
|
318
|
-
const res = context.res
|
|
319
|
-
const end = res.end
|
|
320
|
-
|
|
321
|
-
res.writeHead = web.wrapWriteHead(context)
|
|
322
|
-
|
|
323
|
-
ends.set(res, this.wrapRes(context, req, res, end))
|
|
324
|
-
|
|
325
|
-
Object.defineProperty(res, 'end', {
|
|
326
|
-
configurable: true,
|
|
327
|
-
get () {
|
|
328
|
-
return ends.get(this)
|
|
329
|
-
},
|
|
330
|
-
set (value) {
|
|
331
|
-
ends.set(this, function (...args) {
|
|
332
|
-
return storage('legacy').run(context.store, value, ...args)
|
|
333
|
-
})
|
|
334
|
-
},
|
|
335
|
-
})
|
|
336
|
-
},
|
|
337
326
|
setRouteOrEndpointTag (req) {
|
|
338
327
|
const context = contexts.get(req)
|
|
339
328
|
|
|
@@ -379,6 +368,16 @@ function splitHeader (str) {
|
|
|
379
368
|
|
|
380
369
|
function addRequestTags (context, spanType) {
|
|
381
370
|
const { req, span, inferredProxySpan, config } = context
|
|
371
|
+
const spanContext = span.context()
|
|
372
|
+
|
|
373
|
+
// Idempotency guard. `addRequestTags` runs in `web.startSpan` for the
|
|
374
|
+
// normal HTTP path and again in `web.finishSpan`; without this guard the
|
|
375
|
+
// second call would re-extract the URL, re-obfuscate the query string,
|
|
376
|
+
// and re-publish five `tagsUpdateCh` events with the same values. The
|
|
377
|
+
// serverless path skips `startSpan` and lands here first, in which case
|
|
378
|
+
// HTTP_URL is unset and the work runs normally.
|
|
379
|
+
if (spanContext.hasTag(HTTP_URL)) return
|
|
380
|
+
|
|
382
381
|
const url = extractURL(req)
|
|
383
382
|
const type = spanType ?? WEB
|
|
384
383
|
|
|
@@ -391,8 +390,8 @@ function addRequestTags (context, spanType) {
|
|
|
391
390
|
})
|
|
392
391
|
|
|
393
392
|
// if client ip has already been set by appsec, no need to run it again
|
|
394
|
-
if (extractIp && !
|
|
395
|
-
const clientIp = extractIp(config, req)
|
|
393
|
+
if (config.extractIp && !spanContext.hasTag(HTTP_CLIENT_IP)) {
|
|
394
|
+
const clientIp = config.extractIp(config, req)
|
|
396
395
|
|
|
397
396
|
if (clientIp) {
|
|
398
397
|
span.setTag(HTTP_CLIENT_IP, clientIp)
|
|
@@ -400,7 +399,16 @@ function addRequestTags (context, spanType) {
|
|
|
400
399
|
}
|
|
401
400
|
}
|
|
402
401
|
|
|
403
|
-
|
|
402
|
+
// Datadog scan/test markers, tagged unconditionally so the API endpoint
|
|
403
|
+
// reducer can keep scan/test traffic out of the API inventory.
|
|
404
|
+
const endpointScan = req.headers['x-datadog-endpoint-scan']
|
|
405
|
+
if (endpointScan !== undefined) {
|
|
406
|
+
span.setTag(`${HTTP_REQUEST_HEADERS}.x-datadog-endpoint-scan`, endpointScan)
|
|
407
|
+
}
|
|
408
|
+
const securityTest = req.headers['x-datadog-security-test']
|
|
409
|
+
if (securityTest !== undefined) {
|
|
410
|
+
span.setTag(`${HTTP_REQUEST_HEADERS}.x-datadog-security-test`, securityTest)
|
|
411
|
+
}
|
|
404
412
|
}
|
|
405
413
|
|
|
406
414
|
function addResponseTags (context) {
|
|
@@ -415,14 +423,26 @@ function addResponseTags (context) {
|
|
|
415
423
|
[HTTP_STATUS_CODE]: res.statusCode,
|
|
416
424
|
})
|
|
417
425
|
|
|
426
|
+
addResponseHeaders(context)
|
|
427
|
+
|
|
418
428
|
web.addStatusError(req, res.statusCode)
|
|
419
429
|
}
|
|
420
430
|
|
|
421
431
|
function applyRouteOrEndpointTag (context) {
|
|
422
432
|
const { paths, span, config } = context
|
|
423
433
|
if (!span) return
|
|
424
|
-
const
|
|
425
|
-
|
|
434
|
+
const spanContext = span.context()
|
|
435
|
+
|
|
436
|
+
// AppSec calls `web.setRouteOrEndpointTag` from a pre-finish hook so the
|
|
437
|
+
// route/endpoint tags are available for API Security sampling, and the
|
|
438
|
+
// normal finish-time path runs this again. Either tag being present
|
|
439
|
+
// means the work has already been done; paths are stable between the
|
|
440
|
+
// two calls, so the second pass has nothing to add.
|
|
441
|
+
if (spanContext.hasTag(HTTP_ROUTE) || spanContext.hasTag(HTTP_ENDPOINT)) return
|
|
442
|
+
|
|
443
|
+
// Skip the `Array.prototype.join` builtin in the empty / single-segment
|
|
444
|
+
// cases; `paths[0]` covers both (`undefined` is falsy for the empty case).
|
|
445
|
+
const route = paths.length > 1 ? paths.join('') : paths[0]
|
|
426
446
|
|
|
427
447
|
if (route) {
|
|
428
448
|
// Use http.route from trusted framework instrumentation.
|
|
@@ -430,44 +450,49 @@ function applyRouteOrEndpointTag (context) {
|
|
|
430
450
|
return
|
|
431
451
|
}
|
|
432
452
|
|
|
433
|
-
if (!config.resourceRenamingEnabled
|
|
434
|
-
return
|
|
435
|
-
}
|
|
453
|
+
if (!config.resourceRenamingEnabled) return
|
|
436
454
|
|
|
437
455
|
// Route is unavailable, compute http.endpoint once.
|
|
438
|
-
const url =
|
|
456
|
+
const url = spanContext.getTag(HTTP_URL)
|
|
439
457
|
const endpoint = url ? calculateHttpEndpoint(url) : '/'
|
|
440
458
|
span.setTag(HTTP_ENDPOINT, endpoint)
|
|
441
459
|
}
|
|
442
460
|
|
|
443
461
|
function addResourceTag (context) {
|
|
444
462
|
const { req, span } = context
|
|
445
|
-
const
|
|
463
|
+
const spanContext = span.context()
|
|
446
464
|
|
|
447
|
-
if (
|
|
465
|
+
if (spanContext.getTag(RESOURCE_NAME)) return
|
|
448
466
|
|
|
449
|
-
const resource = [req.method,
|
|
467
|
+
const resource = [req.method, spanContext.getTag(HTTP_ROUTE)]
|
|
450
468
|
.filter(Boolean)
|
|
451
469
|
.join(' ')
|
|
452
470
|
|
|
453
471
|
span.setTag(RESOURCE_NAME, resource)
|
|
454
472
|
}
|
|
455
473
|
|
|
456
|
-
function
|
|
457
|
-
const { req,
|
|
474
|
+
function addRequestHeaders (context) {
|
|
475
|
+
const { req, config, span, inferredProxySpan } = context
|
|
458
476
|
|
|
459
477
|
for (const [key, tag] of config.headers) {
|
|
460
478
|
const reqHeader = req.headers[key]
|
|
461
|
-
const resHeader = res.getHeader(key)
|
|
462
|
-
|
|
463
479
|
if (reqHeader) {
|
|
464
|
-
|
|
465
|
-
|
|
480
|
+
const tagName = tag || `${HTTP_REQUEST_HEADERS}.${key}`
|
|
481
|
+
span.setTag(tagName, reqHeader)
|
|
482
|
+
inferredProxySpan?.setTag(tagName, reqHeader)
|
|
466
483
|
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function addResponseHeaders (context) {
|
|
488
|
+
const { res, config, span, inferredProxySpan } = context
|
|
467
489
|
|
|
490
|
+
for (const [key, tag] of config.headers) {
|
|
491
|
+
const resHeader = res.getHeader(key)
|
|
468
492
|
if (resHeader) {
|
|
469
|
-
|
|
470
|
-
|
|
493
|
+
const tagName = tag || `${HTTP_RESPONSE_HEADERS}.${key}`
|
|
494
|
+
span.setTag(tagName, resHeader)
|
|
495
|
+
inferredProxySpan?.setTag(tagName, resHeader)
|
|
471
496
|
}
|
|
472
497
|
}
|
|
473
498
|
}
|