Comparing version 5.20.0 to 5.21.0
{ | ||
"name": "dd-trace", | ||
"version": "5.20.0", | ||
"version": "5.21.0", | ||
"description": "Datadog APM tracing client for JavaScript", | ||
@@ -76,4 +76,4 @@ "main": "index.js", | ||
"@datadog/native-appsec": "8.0.1", | ||
"@datadog/native-iast-rewriter": "2.4.0", | ||
"@datadog/native-iast-taint-tracking": "3.0.0", | ||
"@datadog/native-iast-rewriter": "2.4.1", | ||
"@datadog/native-iast-taint-tracking": "3.1.0", | ||
"@datadog/native-metrics": "^2.0.0", | ||
@@ -107,3 +107,3 @@ "@datadog/pprof": "5.3.0", | ||
"devDependencies": { | ||
"@types/node": ">=18", | ||
"@types/node": "^16.18.103", | ||
"autocannon": "^4.5.2", | ||
@@ -110,0 +110,0 @@ "aws-sdk": "^2.1446.0", |
'use strict' | ||
const shimmer = require('../../datadog-shimmer') | ||
const { channel, addHook } = require('./helpers/instrument') | ||
const { channel, addHook, AsyncResource } = require('./helpers/instrument') | ||
@@ -26,5 +26,17 @@ const bodyParserReadCh = channel('datadog:body-parser:read:finish') | ||
file: 'lib/read.js', | ||
versions: ['>=1.4.0'] | ||
versions: ['>=1.4.0 <1.20.0'] | ||
}, read => { | ||
return shimmer.wrap(read, function (req, res, next) { | ||
const nextResource = new AsyncResource('bound-anonymous-fn') | ||
arguments[2] = nextResource.bind(publishRequestBodyAndNext(req, res, next)) | ||
return read.apply(this, arguments) | ||
}) | ||
}) | ||
addHook({ | ||
name: 'body-parser', | ||
file: 'lib/read.js', | ||
versions: ['>=1.20.0'] | ||
}, read => { | ||
return shimmer.wrap(read, function (req, res, next) { | ||
arguments[2] = publishRequestBodyAndNext(req, res, next) | ||
@@ -31,0 +43,0 @@ return read.apply(this, arguments) |
@@ -31,2 +31,4 @@ 'use strict' | ||
const getCodeCoverageCh = channel('ci:nyc:get-coverage') | ||
const { | ||
@@ -360,2 +362,7 @@ getCoveredFilenamesFromCoverage, | ||
let untestedCoverage | ||
if (getCodeCoverageCh.hasSubscribers) { | ||
untestedCoverage = await getChannelPromise(getCodeCoverageCh) | ||
} | ||
let testCodeCoverageLinesTotal | ||
@@ -365,2 +372,5 @@ | ||
try { | ||
if (untestedCoverage) { | ||
originalCoverageMap.merge(fromCoverageMapToCoverage(untestedCoverage)) | ||
} | ||
testCodeCoverageLinesTotal = originalCoverageMap.getCoverageSummary().lines.pct | ||
@@ -367,0 +377,0 @@ } catch (e) { |
@@ -75,3 +75,2 @@ 'use strict' | ||
'mocha-each': () => require('../mocha'), | ||
workerpool: () => require('../mocha'), | ||
moleculer: () => require('../moleculer'), | ||
@@ -93,2 +92,3 @@ mongodb: () => require('../mongodb'), | ||
'node:net': () => require('../net'), | ||
nyc: () => require('../nyc'), | ||
oracledb: () => require('../oracledb'), | ||
@@ -118,3 +118,4 @@ openai: () => require('../openai'), | ||
when: () => require('../when'), | ||
winston: () => require('../winston') | ||
winston: () => require('../winston'), | ||
workerpool: () => require('../mocha') | ||
} |
@@ -93,3 +93,10 @@ 'use strict' | ||
if (matchesFile) { | ||
const version = moduleVersion || getVersion(moduleBaseDir) | ||
let version = moduleVersion | ||
try { | ||
version = version || getVersion(moduleBaseDir) | ||
} catch (e) { | ||
log.error(`Error getting version for "${name}": ${e.message}`) | ||
log.error(e) | ||
continue | ||
} | ||
if (!Object.hasOwnProperty(namesAndSuccesses, name)) { | ||
@@ -96,0 +103,0 @@ namesAndSuccesses[`${name}@${version}`] = false |
@@ -50,2 +50,3 @@ 'use strict' | ||
const originalCoverageMap = createCoverageMap() | ||
let untestedCoverage | ||
@@ -70,2 +71,4 @@ // test channels | ||
const getCodeCoverageCh = channel('ci:nyc:get-coverage') | ||
function getFilteredSuites (originalSuites) { | ||
@@ -136,2 +139,5 @@ return originalSuites.reduce((acc, suite) => { | ||
try { | ||
if (untestedCoverage) { | ||
originalCoverageMap.merge(fromCoverageMapToCoverage(untestedCoverage)) | ||
} | ||
testCodeCoverageLinesTotal = originalCoverageMap.getCoverageSummary().lines.pct | ||
@@ -159,2 +165,79 @@ } catch (e) { | ||
function getExecutionConfiguration (runner, onFinishRequest) { | ||
const mochaRunAsyncResource = new AsyncResource('bound-anonymous-fn') | ||
const onReceivedSkippableSuites = ({ err, skippableSuites, itrCorrelationId: responseItrCorrelationId }) => { | ||
if (err) { | ||
suitesToSkip = [] | ||
} else { | ||
suitesToSkip = skippableSuites | ||
itrCorrelationId = responseItrCorrelationId | ||
} | ||
// We remove the suites that we skip through ITR | ||
const filteredSuites = getFilteredSuites(runner.suite.suites) | ||
const { suitesToRun } = filteredSuites | ||
isSuitesSkipped = suitesToRun.length !== runner.suite.suites.length | ||
log.debug( | ||
() => `${suitesToRun.length} out of ${runner.suite.suites.length} suites are going to run.` | ||
) | ||
runner.suite.suites = suitesToRun | ||
skippedSuites = Array.from(filteredSuites.skippedSuites) | ||
onFinishRequest() | ||
} | ||
const onReceivedKnownTests = ({ err, knownTests: receivedKnownTests }) => { | ||
if (err) { | ||
knownTests = [] | ||
isEarlyFlakeDetectionEnabled = false | ||
} else { | ||
knownTests = receivedKnownTests | ||
} | ||
if (isSuitesSkippingEnabled) { | ||
skippableSuitesCh.publish({ | ||
onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites) | ||
}) | ||
} else { | ||
onFinishRequest() | ||
} | ||
} | ||
const onReceivedConfiguration = ({ err, libraryConfig }) => { | ||
if (err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) { | ||
return onFinishRequest() | ||
} | ||
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled | ||
isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled | ||
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries | ||
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled | ||
config.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled | ||
config.isSuitesSkippingEnabled = isSuitesSkippingEnabled | ||
config.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries | ||
config.isFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled | ||
if (isEarlyFlakeDetectionEnabled) { | ||
knownTestsCh.publish({ | ||
onDone: mochaRunAsyncResource.bind(onReceivedKnownTests) | ||
}) | ||
} else if (isSuitesSkippingEnabled) { | ||
skippableSuitesCh.publish({ | ||
onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites) | ||
}) | ||
} else { | ||
onFinishRequest() | ||
} | ||
} | ||
libraryConfigurationCh.publish({ | ||
onDone: mochaRunAsyncResource.bind(onReceivedConfiguration) | ||
}) | ||
} | ||
// In this hook we delay the execution with options.delay to grab library configuration, | ||
@@ -168,3 +251,2 @@ // skippable and known tests. | ||
}, (Mocha) => { | ||
const mochaRunAsyncResource = new AsyncResource('bound-anonymous-fn') | ||
shimmer.wrap(Mocha.prototype, 'run', run => function () { | ||
@@ -189,37 +271,9 @@ // Workers do not need to request any data, just run the tests | ||
const onReceivedSkippableSuites = ({ err, skippableSuites, itrCorrelationId: responseItrCorrelationId }) => { | ||
if (err) { | ||
suitesToSkip = [] | ||
} else { | ||
suitesToSkip = skippableSuites | ||
itrCorrelationId = responseItrCorrelationId | ||
} | ||
// We remove the suites that we skip through ITR | ||
const filteredSuites = getFilteredSuites(runner.suite.suites) | ||
const { suitesToRun } = filteredSuites | ||
isSuitesSkipped = suitesToRun.length !== runner.suite.suites.length | ||
log.debug( | ||
() => `${suitesToRun.length} out of ${runner.suite.suites.length} suites are going to run.` | ||
) | ||
runner.suite.suites = suitesToRun | ||
skippedSuites = Array.from(filteredSuites.skippedSuites) | ||
global.run() | ||
} | ||
const onReceivedKnownTests = ({ err, knownTests: receivedKnownTests }) => { | ||
if (err) { | ||
knownTests = [] | ||
isEarlyFlakeDetectionEnabled = false | ||
} else { | ||
knownTests = receivedKnownTests | ||
} | ||
if (isSuitesSkippingEnabled) { | ||
skippableSuitesCh.publish({ | ||
onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites) | ||
getExecutionConfiguration(runner, () => { | ||
if (getCodeCoverageCh.hasSubscribers) { | ||
getCodeCoverageCh.publish({ | ||
onDone: (receivedCodeCoverage) => { | ||
untestedCoverage = receivedCodeCoverage | ||
global.run() | ||
} | ||
}) | ||
@@ -229,36 +283,2 @@ } else { | ||
} | ||
} | ||
const onReceivedConfiguration = ({ err, libraryConfig }) => { | ||
if (err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) { | ||
return global.run() | ||
} | ||
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled | ||
isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled | ||
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries | ||
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled | ||
config.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled | ||
config.isSuitesSkippingEnabled = isSuitesSkippingEnabled | ||
config.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries | ||
config.isFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled | ||
if (isEarlyFlakeDetectionEnabled) { | ||
knownTestsCh.publish({ | ||
onDone: mochaRunAsyncResource.bind(onReceivedKnownTests) | ||
}) | ||
} else if (isSuitesSkippingEnabled) { | ||
skippableSuitesCh.publish({ | ||
onDone: mochaRunAsyncResource.bind(onReceivedSkippableSuites) | ||
}) | ||
} else { | ||
global.run() | ||
} | ||
} | ||
mochaRunAsyncResource.runInAsyncScope(() => { | ||
libraryConfigurationCh.publish({ | ||
onDone: mochaRunAsyncResource.bind(onReceivedConfiguration) | ||
}) | ||
}) | ||
@@ -265,0 +285,0 @@ |
@@ -124,2 +124,17 @@ const { addHook, channel, AsyncResource } = require('./helpers/instrument') | ||
let testCodeCoverageLinesTotal | ||
if (this.ctx.coverageProvider?.generateCoverage) { | ||
shimmer.wrap(this.ctx.coverageProvider, 'generateCoverage', generateCoverage => async function () { | ||
const totalCodeCoverage = await generateCoverage.apply(this, arguments) | ||
try { | ||
testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct | ||
} catch (e) { | ||
// ignore errors | ||
} | ||
return totalCodeCoverage | ||
}) | ||
} | ||
shimmer.wrap(this.ctx, 'exit', exit => async function () { | ||
@@ -140,4 +155,5 @@ let onFinish | ||
status: getSessionStatus(this.state), | ||
onFinish, | ||
error | ||
testCodeCoverageLinesTotal, | ||
error, | ||
onFinish | ||
}) | ||
@@ -144,0 +160,0 @@ }) |
@@ -40,3 +40,6 @@ 'use strict' | ||
TELEMETRY_ITR_UNSKIPPABLE, | ||
TELEMETRY_CODE_COVERAGE_NUM_FILES | ||
TELEMETRY_CODE_COVERAGE_NUM_FILES, | ||
TEST_IS_RUM_ACTIVE, | ||
TEST_BROWSER_DRIVER, | ||
TELEMETRY_TEST_SESSION | ||
} = require('../../dd-trace/src/ci-visibility/telemetry') | ||
@@ -111,2 +114,3 @@ const id = require('../../dd-trace/src/id') | ||
finishAllTraceSpans(this.testSessionSpan) | ||
this.telemetry.count(TELEMETRY_TEST_SESSION, { provider: this.ciProviderName }) | ||
@@ -290,6 +294,12 @@ this.libraryConfig = null | ||
if (!isStep) { | ||
const spanTags = span.context()._tags | ||
this.telemetry.ciVisEvent( | ||
TELEMETRY_EVENT_FINISHED, | ||
'test', | ||
{ hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] } | ||
{ | ||
hasCodeOwners: !!spanTags[TEST_CODE_OWNERS], | ||
isNew, | ||
isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true', | ||
browserDriver: spanTags[TEST_BROWSER_DRIVER] | ||
} | ||
) | ||
@@ -296,0 +306,0 @@ finishAllTraceSpans(span) |
@@ -47,3 +47,5 @@ const { | ||
incrementCountMetric, | ||
distributionMetric | ||
distributionMetric, | ||
TELEMETRY_ITR_SKIPPED, | ||
TELEMETRY_TEST_SESSION | ||
} = require('../../dd-trace/src/ci-visibility/telemetry') | ||
@@ -183,3 +185,3 @@ | ||
this.repositoryRoot = repositoryRoot | ||
this.isUnsupportedCIProvider = !ciProviderName | ||
this.ciProviderName = ciProviderName | ||
this.codeOwnersEntries = getCodeOwnersFileEntries(repositoryRoot) | ||
@@ -326,3 +328,3 @@ | ||
testFramework: 'cypress', | ||
isUnsupportedCIProvider: this.isUnsupportedCIProvider, | ||
isUnsupportedCIProvider: !this.ciProviderName, | ||
...tags | ||
@@ -369,2 +371,3 @@ }) | ||
this.itrCorrelationId = correlationId | ||
incrementCountMetric(TELEMETRY_ITR_SKIPPED, { testLevel: 'test' }, this.testsToSkip.length) | ||
} | ||
@@ -443,2 +446,5 @@ } | ||
this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session') | ||
incrementCountMetric(TELEMETRY_TEST_SESSION, { | ||
provider: this.ciProviderName | ||
}) | ||
@@ -676,4 +682,10 @@ finishAllTraceSpans(this.testSessionSpan) | ||
} | ||
this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', { | ||
hasCodeOwners: !!this.activeTestSpan.context()._tags[TEST_CODE_OWNERS], | ||
isNew, | ||
isRum: isRUMActive, | ||
browserDriver: 'cypress' | ||
}) | ||
this.activeTestSpan = null | ||
this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test') | ||
return null | ||
@@ -680,0 +692,0 @@ }, |
@@ -23,3 +23,5 @@ const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin') | ||
TEST_EARLY_FLAKE_ABORT_REASON, | ||
JEST_DISPLAY_NAME | ||
JEST_DISPLAY_NAME, | ||
TEST_IS_RUM_ACTIVE, | ||
TEST_BROWSER_DRIVER | ||
} = require('../../dd-trace/src/plugins/util/test') | ||
@@ -36,3 +38,4 @@ const { COMPONENT } = require('../../dd-trace/src/constants') | ||
TELEMETRY_ITR_UNSKIPPABLE, | ||
TELEMETRY_CODE_COVERAGE_NUM_FILES | ||
TELEMETRY_CODE_COVERAGE_NUM_FILES, | ||
TELEMETRY_TEST_SESSION | ||
} = require('../../dd-trace/src/ci-visibility/telemetry') | ||
@@ -134,2 +137,4 @@ | ||
this.telemetry.count(TELEMETRY_TEST_SESSION, { provider: this.ciProviderName }) | ||
this.tracer._exporter.flush(() => { | ||
@@ -293,8 +298,16 @@ if (onDone) { | ||
} | ||
span.finish() | ||
const spanTags = span.context()._tags | ||
this.telemetry.ciVisEvent( | ||
TELEMETRY_EVENT_FINISHED, | ||
'test', | ||
{ hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] } | ||
{ | ||
hasCodeOwners: !!spanTags[TEST_CODE_OWNERS], | ||
isNew: spanTags[TEST_IS_NEW] === 'true', | ||
isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true', | ||
browserDriver: spanTags[TEST_BROWSER_DRIVER] | ||
} | ||
) | ||
span.finish() | ||
finishAllTraceSpans(span) | ||
@@ -301,0 +314,0 @@ }) |
@@ -30,3 +30,5 @@ 'use strict' | ||
TEST_SUITE, | ||
MOCHA_IS_PARALLEL | ||
MOCHA_IS_PARALLEL, | ||
TEST_IS_RUM_ACTIVE, | ||
TEST_BROWSER_DRIVER | ||
} = require('../../dd-trace/src/plugins/util/test') | ||
@@ -42,3 +44,4 @@ const { COMPONENT } = require('../../dd-trace/src/constants') | ||
TELEMETRY_ITR_UNSKIPPABLE, | ||
TELEMETRY_CODE_COVERAGE_NUM_FILES | ||
TELEMETRY_CODE_COVERAGE_NUM_FILES, | ||
TELEMETRY_TEST_SESSION | ||
} = require('../../dd-trace/src/ci-visibility/telemetry') | ||
@@ -189,8 +192,16 @@ const id = require('../../dd-trace/src/id') | ||
} | ||
span.finish() | ||
const spanTags = span.context()._tags | ||
this.telemetry.ciVisEvent( | ||
TELEMETRY_EVENT_FINISHED, | ||
'test', | ||
{ hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] } | ||
{ | ||
hasCodeOwners: !!spanTags[TEST_CODE_OWNERS], | ||
isNew: spanTags[TEST_IS_NEW] === 'true', | ||
isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true', | ||
browserDriver: spanTags[TEST_BROWSER_DRIVER] | ||
} | ||
) | ||
span.finish() | ||
finishAllTraceSpans(span) | ||
@@ -232,8 +243,15 @@ } | ||
span.finish() | ||
const spanTags = span.context()._tags | ||
this.telemetry.ciVisEvent( | ||
TELEMETRY_EVENT_FINISHED, | ||
'test', | ||
{ hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] } | ||
{ | ||
hasCodeOwners: !!spanTags[TEST_CODE_OWNERS], | ||
isNew: spanTags[TEST_IS_NEW] === 'true', | ||
isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true', | ||
browserDriver: spanTags[TEST_BROWSER_DRIVER] | ||
} | ||
) | ||
span.finish() | ||
finishAllTraceSpans(span) | ||
@@ -296,2 +314,3 @@ } | ||
finishAllTraceSpans(this.testSessionSpan) | ||
this.telemetry.count(TELEMETRY_TEST_SESSION, { provider: this.ciProviderName }) | ||
} | ||
@@ -298,0 +317,0 @@ this.libraryConfig = null |
@@ -17,3 +17,4 @@ 'use strict' | ||
TEST_IS_RETRY, | ||
TEST_EARLY_FLAKE_ENABLED | ||
TEST_EARLY_FLAKE_ENABLED, | ||
TELEMETRY_TEST_SESSION | ||
} = require('../../dd-trace/src/plugins/util/test') | ||
@@ -63,2 +64,3 @@ const { RESOURCE_NAME } = require('../../../ext/tags') | ||
finishAllTraceSpans(this.testSessionSpan) | ||
this.telemetry.count(TELEMETRY_TEST_SESSION, { provider: this.ciProviderName }) | ||
appClosingTelemetry() | ||
@@ -165,4 +167,2 @@ this.tracer._exporter.flush(onDone) | ||
span.finish() | ||
if (testStatus === 'fail') { | ||
@@ -175,4 +175,9 @@ this.numFailedTests++ | ||
'test', | ||
{ hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] } | ||
{ | ||
hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS], | ||
isNew, | ||
browserDriver: 'playwright' | ||
} | ||
) | ||
span.finish() | ||
@@ -179,0 +184,0 @@ finishAllTraceSpans(span) |
@@ -10,5 +10,12 @@ const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin') | ||
TEST_SOURCE_FILE, | ||
TEST_IS_RETRY | ||
TEST_IS_RETRY, | ||
TEST_CODE_COVERAGE_LINES_PCT, | ||
TEST_CODE_OWNERS | ||
} = require('../../dd-trace/src/plugins/util/test') | ||
const { COMPONENT } = require('../../dd-trace/src/constants') | ||
const { | ||
TELEMETRY_EVENT_CREATED, | ||
TELEMETRY_EVENT_FINISHED, | ||
TELEMETRY_TEST_SESSION | ||
} = require('../../dd-trace/src/ci-visibility/telemetry') | ||
@@ -68,2 +75,5 @@ // Milliseconds that we subtract from the error test duration | ||
if (span) { | ||
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', { | ||
hasCodeowners: !!span.context()._tags[TEST_CODE_OWNERS] | ||
}) | ||
span.setTag(TEST_STATUS, 'pass') | ||
@@ -80,2 +90,5 @@ span.finish(this.taskToFinishTime.get(task)) | ||
if (span) { | ||
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', { | ||
hasCodeowners: !!span.context()._tags[TEST_CODE_OWNERS] | ||
}) | ||
span.setTag(TEST_STATUS, 'fail') | ||
@@ -97,3 +110,3 @@ | ||
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot) | ||
this.startTestSpan( | ||
const testSpan = this.startTestSpan( | ||
testName, | ||
@@ -106,3 +119,7 @@ testSuite, | ||
} | ||
).finish() | ||
) | ||
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', { | ||
hasCodeowners: !!testSpan.context()._tags[TEST_CODE_OWNERS] | ||
}) | ||
testSpan.finish() | ||
}) | ||
@@ -132,2 +149,3 @@ | ||
}) | ||
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite') | ||
const store = storage.getStore() | ||
@@ -146,2 +164,3 @@ this.enter(testSuiteSpan, store) | ||
} | ||
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite') | ||
// TODO: too frequent flush - find for method in worker to decrease frequency | ||
@@ -160,3 +179,3 @@ this.tracer._exporter.flush(onFinish) | ||
this.addSub('ci:vitest:session:finish', ({ status, onFinish, error }) => { | ||
this.addSub('ci:vitest:session:finish', ({ status, onFinish, error, testCodeCoverageLinesTotal }) => { | ||
this.testSessionSpan.setTag(TEST_STATUS, status) | ||
@@ -168,5 +187,12 @@ this.testModuleSpan.setTag(TEST_STATUS, status) | ||
} | ||
if (testCodeCoverageLinesTotal) { | ||
this.testModuleSpan.setTag(TEST_CODE_COVERAGE_LINES_PCT, testCodeCoverageLinesTotal) | ||
this.testSessionSpan.setTag(TEST_CODE_COVERAGE_LINES_PCT, testCodeCoverageLinesTotal) | ||
} | ||
this.testModuleSpan.finish() | ||
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module') | ||
this.testSessionSpan.finish() | ||
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session') | ||
finishAllTraceSpans(this.testSessionSpan) | ||
this.telemetry.count(TELEMETRY_TEST_SESSION, { provider: this.ciProviderName }) | ||
this.tracer._exporter.flush(onFinish) | ||
@@ -173,0 +199,0 @@ }) |
const request = require('../../exporters/common/request') | ||
const id = require('../../id') | ||
const log = require('../../log') | ||
const { | ||
incrementCountMetric, | ||
distributionMetric, | ||
TELEMETRY_KNOWN_TESTS, | ||
TELEMETRY_KNOWN_TESTS_MS, | ||
TELEMETRY_KNOWN_TESTS_ERRORS, | ||
TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS, | ||
TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES | ||
} = require('../../ci-visibility/telemetry') | ||
function getNumTests (knownTests) { | ||
let totalNumTests = 0 | ||
for (const testModule of Object.values(knownTests)) { | ||
for (const testSuite of Object.values(testModule)) { | ||
for (const testList of Object.values(testSuite)) { | ||
totalNumTests += testList.length | ||
} | ||
} | ||
} | ||
return totalNumTests | ||
} | ||
function getKnownTests ({ | ||
@@ -67,4 +92,10 @@ url, | ||
request(data, options, (err, res) => { | ||
incrementCountMetric(TELEMETRY_KNOWN_TESTS) | ||
const startTime = Date.now() | ||
request(data, options, (err, res, statusCode) => { | ||
distributionMetric(TELEMETRY_KNOWN_TESTS_MS, {}, Date.now() - startTime) | ||
if (err) { | ||
incrementCountMetric(TELEMETRY_KNOWN_TESTS_ERRORS, { statusCode }) | ||
done(err) | ||
@@ -74,2 +105,10 @@ } else { | ||
const { data: { attributes: { tests: knownTests } } } = JSON.parse(res) | ||
const numTests = getNumTests(knownTests) | ||
incrementCountMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS, {}, numTests) | ||
distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES, {}, res.length) | ||
log.debug(() => `Number of received known tests: ${numTests}`) | ||
done(null, knownTests) | ||
@@ -76,0 +115,0 @@ } catch (err) { |
@@ -15,4 +15,3 @@ 'use strict' | ||
TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS, | ||
TELEMETRY_ENDPOINT_PAYLOAD_DROPPED, | ||
getErrorTypeFromStatusCode | ||
TELEMETRY_ENDPOINT_PAYLOAD_DROPPED | ||
} = require('../../../ci-visibility/telemetry') | ||
@@ -60,6 +59,5 @@ | ||
if (err) { | ||
const errorType = getErrorTypeFromStatusCode(statusCode) | ||
incrementCountMetric( | ||
TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS, | ||
{ endpoint: 'code_coverage', errorType } | ||
{ endpoint: 'code_coverage', statusCode } | ||
) | ||
@@ -66,0 +64,0 @@ incrementCountMetric( |
@@ -15,4 +15,3 @@ 'use strict' | ||
TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS, | ||
TELEMETRY_ENDPOINT_PAYLOAD_DROPPED, | ||
getErrorTypeFromStatusCode | ||
TELEMETRY_ENDPOINT_PAYLOAD_DROPPED | ||
} = require('../../../ci-visibility/telemetry') | ||
@@ -61,6 +60,5 @@ | ||
if (err) { | ||
const errorType = getErrorTypeFromStatusCode(statusCode) | ||
incrementCountMetric( | ||
TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS, | ||
{ endpoint: 'test_cycle', errorType } | ||
{ endpoint: 'test_cycle', statusCode } | ||
) | ||
@@ -67,0 +65,0 @@ incrementCountMetric( |
@@ -14,3 +14,4 @@ const fs = require('fs') | ||
isShallowRepository, | ||
unshallowRepository | ||
unshallowRepository, | ||
isGitAvailable | ||
} = require('../../../plugins/util/git') | ||
@@ -28,4 +29,3 @@ | ||
TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_ERRORS, | ||
TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_BYTES, | ||
getErrorTypeFromStatusCode | ||
TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_BYTES | ||
} = require('../../../ci-visibility/telemetry') | ||
@@ -97,4 +97,3 @@ | ||
if (err) { | ||
const errorType = getErrorTypeFromStatusCode(statusCode) | ||
incrementCountMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS, { errorType }) | ||
incrementCountMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS, { statusCode }) | ||
const error = new Error(`Error fetching commits to exclude: ${err.message}`) | ||
@@ -184,4 +183,3 @@ return callback(error) | ||
if (err) { | ||
const errorType = getErrorTypeFromStatusCode(statusCode) | ||
incrementCountMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_ERRORS, { errorType }) | ||
incrementCountMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_ERRORS, { statusCode }) | ||
const error = new Error(`Could not upload packfiles: status code ${statusCode}: ${err.message}`) | ||
@@ -252,2 +250,5 @@ return callback(error, uploadSize) | ||
function sendGitMetadata (url, { isEvpProxy, evpProxyPrefix }, configRepositoryUrl, callback) { | ||
if (!isGitAvailable()) { | ||
return callback(new Error('Git is not available')) | ||
} | ||
let repositoryUrl = configRepositoryUrl | ||
@@ -254,0 +255,0 @@ if (!repositoryUrl) { |
@@ -11,4 +11,3 @@ const request = require('../../exporters/common/request') | ||
TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS, | ||
TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES, | ||
getErrorTypeFromStatusCode | ||
TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES | ||
} = require('../../ci-visibility/telemetry') | ||
@@ -87,4 +86,3 @@ | ||
if (err) { | ||
const errorType = getErrorTypeFromStatusCode(statusCode) | ||
incrementCountMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_ERRORS, { errorType }) | ||
incrementCountMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_ERRORS, { statusCode }) | ||
done(err) | ||
@@ -91,0 +89,0 @@ } else { |
@@ -10,4 +10,3 @@ const request = require('../../exporters/common/request') | ||
TELEMETRY_GIT_REQUESTS_SETTINGS_ERRORS, | ||
TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE, | ||
getErrorTypeFromStatusCode | ||
TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE | ||
} = require('../telemetry') | ||
@@ -85,4 +84,3 @@ | ||
if (err) { | ||
const errorType = getErrorTypeFromStatusCode(statusCode) | ||
incrementCountMetric(TELEMETRY_GIT_REQUESTS_SETTINGS_ERRORS, { errorType }) | ||
incrementCountMetric(TELEMETRY_GIT_REQUESTS_SETTINGS_ERRORS, { statusCode }) | ||
done(err) | ||
@@ -89,0 +87,0 @@ } else { |
@@ -13,3 +13,6 @@ const telemetryMetrics = require('../telemetry/metrics') | ||
hasCodeOwners: 'has_code_owners', | ||
isUnsupportedCIProvider: 'is_unsupported_ci' | ||
isUnsupportedCIProvider: 'is_unsupported_ci', | ||
isNew: 'is_new', | ||
isRum: 'is_rum', | ||
browserDriver: 'browser_driver' | ||
} | ||
@@ -21,2 +24,10 @@ | ||
return Object.keys(tagsDictionary).reduce((acc, tagKey) => { | ||
if (tagKey === 'statusCode') { | ||
const statusCode = tagsDictionary[tagKey] | ||
if (isStatusCode400(statusCode)) { | ||
acc.push(`status_code:${statusCode}`) | ||
} | ||
acc.push(`error_type:${getErrorTypeFromStatusCode(statusCode)}`) | ||
return acc | ||
} | ||
const formattedTagKey = formattedTags[tagKey] || tagKey | ||
@@ -41,2 +52,3 @@ if (tagsDictionary[tagKey] === true) { | ||
// CI Visibility telemetry events | ||
const TELEMETRY_TEST_SESSION = 'test_session' | ||
const TELEMETRY_EVENT_CREATED = 'event_created' | ||
@@ -80,3 +92,13 @@ const TELEMETRY_EVENT_FINISHED = 'event_finished' | ||
const TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES = 'itr_skippable_tests.response_bytes' | ||
// early flake detection | ||
const TELEMETRY_KNOWN_TESTS = 'early_flake_detection.request' | ||
const TELEMETRY_KNOWN_TESTS_MS = 'early_flake_detection.request_ms' | ||
const TELEMETRY_KNOWN_TESTS_ERRORS = 'early_flake_detection.request_errors' | ||
const TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS = 'early_flake_detection.response_tests' | ||
const TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES = 'early_flake_detection.response_bytes' | ||
function isStatusCode400 (statusCode) { | ||
return statusCode >= 400 && statusCode < 500 | ||
} | ||
function getErrorTypeFromStatusCode (statusCode) { | ||
@@ -95,2 +117,3 @@ if (statusCode >= 400 && statusCode < 500) { | ||
distributionMetric, | ||
TELEMETRY_TEST_SESSION, | ||
TELEMETRY_EVENT_CREATED, | ||
@@ -134,3 +157,7 @@ TELEMETRY_EVENT_FINISHED, | ||
TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES, | ||
getErrorTypeFromStatusCode | ||
TELEMETRY_KNOWN_TESTS, | ||
TELEMETRY_KNOWN_TESTS_MS, | ||
TELEMETRY_KNOWN_TESTS_ERRORS, | ||
TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS, | ||
TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES | ||
} |
@@ -186,3 +186,3 @@ 'use strict' | ||
function propagationStyle (key, option, defaultValue) { | ||
function propagationStyle (key, option) { | ||
// Extract by key if in object-form value | ||
@@ -210,4 +210,12 @@ if (option !== null && typeof option === 'object' && !Array.isArray(option)) { | ||
} | ||
} | ||
return defaultValue | ||
function reformatSpanSamplingRules (rules) { | ||
if (!rules) return rules | ||
return rules.map(rule => { | ||
return remapify(rule, { | ||
sample_rate: 'sampleRate', | ||
max_per_second: 'maxPerSecond' | ||
}) | ||
}) | ||
} | ||
@@ -234,16 +242,2 @@ | ||
const DD_TRACE_MEMCACHED_COMMAND_ENABLED = coalesce( | ||
process.env.DD_TRACE_MEMCACHED_COMMAND_ENABLED, | ||
false | ||
) | ||
const DD_SERVICE_MAPPING = coalesce( | ||
options.serviceMapping, | ||
process.env.DD_SERVICE_MAPPING | ||
? fromEntries( | ||
process.env.DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':')) | ||
) | ||
: {} | ||
) | ||
const DD_API_KEY = coalesce( | ||
@@ -254,13 +248,2 @@ process.env.DATADOG_API_KEY, | ||
// TODO: Remove the experimental env vars as a major? | ||
const DD_TRACE_B3_ENABLED = coalesce( | ||
options.experimental && options.experimental.b3, | ||
process.env.DD_TRACE_EXPERIMENTAL_B3_ENABLED, | ||
false | ||
) | ||
const defaultPropagationStyle = ['datadog', 'tracecontext'] | ||
if (isTrue(DD_TRACE_B3_ENABLED)) { | ||
defaultPropagationStyle.push('b3') | ||
defaultPropagationStyle.push('b3 single header') | ||
} | ||
if (process.env.DD_TRACE_PROPAGATION_STYLE && ( | ||
@@ -279,17 +262,7 @@ process.env.DD_TRACE_PROPAGATION_STYLE_INJECT || | ||
options.tracePropagationStyle, | ||
defaultPropagationStyle | ||
this._getDefaultPropagationStyle(options) | ||
) | ||
const PROPAGATION_STYLE_EXTRACT = propagationStyle( | ||
'extract', | ||
options.tracePropagationStyle, | ||
defaultPropagationStyle | ||
) | ||
validateOtelPropagators(PROPAGATION_STYLE_INJECT) | ||
const DD_TRACE_PROPAGATION_EXTRACT_FIRST = coalesce( | ||
process.env.DD_TRACE_PROPAGATION_EXTRACT_FIRST, | ||
false | ||
) | ||
if (typeof options.appsec === 'boolean') { | ||
@@ -303,29 +276,2 @@ options.appsec = { | ||
const DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON = coalesce( | ||
maybeFile(options.appsec.blockedTemplateGraphql), | ||
maybeFile(process.env.DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON) | ||
) | ||
const DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING = coalesce( | ||
options.appsec.eventTracking && options.appsec.eventTracking.mode, | ||
process.env.DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING, | ||
'safe' | ||
).toLowerCase() | ||
const DD_API_SECURITY_ENABLED = coalesce( | ||
options.appsec?.apiSecurity?.enabled, | ||
process.env.DD_API_SECURITY_ENABLED && isTrue(process.env.DD_API_SECURITY_ENABLED), | ||
process.env.DD_EXPERIMENTAL_API_SECURITY_ENABLED && isTrue(process.env.DD_EXPERIMENTAL_API_SECURITY_ENABLED), | ||
true | ||
) | ||
const DD_API_SECURITY_REQUEST_SAMPLE_RATE = coalesce( | ||
options.appsec?.apiSecurity?.requestSampling, | ||
parseFloat(process.env.DD_API_SECURITY_REQUEST_SAMPLE_RATE), | ||
0.1 | ||
) | ||
// 0: disabled, 1: logging, 2: garbage collection + logging | ||
const DD_TRACE_SPAN_LEAK_DEBUG = coalesce( | ||
process.env.DD_TRACE_SPAN_LEAK_DEBUG, | ||
0 | ||
) | ||
const DD_INSTRUMENTATION_INSTALL_ID = coalesce( | ||
@@ -344,47 +290,6 @@ process.env.DD_INSTRUMENTATION_INSTALL_ID, | ||
const sampler = { | ||
spanSamplingRules: coalesce( | ||
options.spanSamplingRules, | ||
safeJsonParse(maybeFile(process.env.DD_SPAN_SAMPLING_RULES_FILE)), | ||
safeJsonParse(process.env.DD_SPAN_SAMPLING_RULES), | ||
[] | ||
).map(rule => { | ||
return remapify(rule, { | ||
sample_rate: 'sampleRate', | ||
max_per_second: 'maxPerSecond' | ||
}) | ||
}) | ||
} | ||
// TODO: refactor | ||
this.apiKey = DD_API_KEY | ||
this.serviceMapping = DD_SERVICE_MAPPING | ||
this.tracePropagationStyle = { | ||
inject: PROPAGATION_STYLE_INJECT, | ||
extract: PROPAGATION_STYLE_EXTRACT, | ||
otelPropagators: process.env.DD_TRACE_PROPAGATION_STYLE || | ||
process.env.DD_TRACE_PROPAGATION_STYLE_INJECT || | ||
process.env.DD_TRACE_PROPAGATION_STYLE_EXTRACT | ||
? false | ||
: !!process.env.OTEL_PROPAGATORS | ||
} | ||
this.tracePropagationExtractFirst = isTrue(DD_TRACE_PROPAGATION_EXTRACT_FIRST) | ||
this.sampler = sampler | ||
this.appsec = { | ||
blockedTemplateGraphql: DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON, | ||
eventTracking: { | ||
enabled: ['extended', 'safe'].includes(DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING), | ||
mode: DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING | ||
}, | ||
apiSecurity: { | ||
enabled: DD_API_SECURITY_ENABLED, | ||
// Coerce value between 0 and 1 | ||
requestSampling: Math.min(1, Math.max(0, DD_API_SECURITY_REQUEST_SAMPLE_RATE)) | ||
} | ||
} | ||
// Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent | ||
this.memcachedCommandEnabled = isTrue(DD_TRACE_MEMCACHED_COMMAND_ENABLED) | ||
this.isAzureFunction = getIsAzureFunction() | ||
this.spanLeakDebug = Number(DD_TRACE_SPAN_LEAK_DEBUG) | ||
// sent in telemetry event app-started | ||
this.installSignature = { | ||
@@ -463,2 +368,17 @@ id: DD_INSTRUMENTATION_INSTALL_ID, | ||
_getDefaultPropagationStyle (options) { | ||
// TODO: Remove the experimental env vars as a major? | ||
const DD_TRACE_B3_ENABLED = coalesce( | ||
options.experimental && options.experimental.b3, | ||
process.env.DD_TRACE_EXPERIMENTAL_B3_ENABLED, | ||
false | ||
) | ||
const defaultPropagationStyle = ['datadog', 'tracecontext'] | ||
if (isTrue(DD_TRACE_B3_ENABLED)) { | ||
defaultPropagationStyle.push('b3') | ||
defaultPropagationStyle.push('b3 single header') | ||
} | ||
return defaultPropagationStyle | ||
} | ||
_isInServerlessEnvironment () { | ||
@@ -489,5 +409,10 @@ const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined | ||
this._setValue(defaults, 'appsec.apiSecurity.enabled', true) | ||
this._setValue(defaults, 'appsec.apiSecurity.requestSampling', 0.1) | ||
this._setValue(defaults, 'appsec.blockedTemplateGraphql', undefined) | ||
this._setValue(defaults, 'appsec.blockedTemplateHtml', undefined) | ||
this._setValue(defaults, 'appsec.blockedTemplateJson', undefined) | ||
this._setValue(defaults, 'appsec.enabled', undefined) | ||
this._setValue(defaults, 'appsec.eventTracking.enabled', true) | ||
this._setValue(defaults, 'appsec.eventTracking.mode', 'safe') | ||
this._setValue(defaults, 'appsec.obfuscatorKeyRegex', defaultWafObfuscatorKeyRegex) | ||
@@ -528,2 +453,3 @@ this._setValue(defaults, 'appsec.obfuscatorValueRegex', defaultWafObfuscatorValueRegex) | ||
this._setValue(defaults, 'iast.telemetryVerbosity', 'INFORMATION') | ||
this._setValue(defaults, 'isAzureFunction', false) | ||
this._setValue(defaults, 'isCiVisibility', false) | ||
@@ -537,2 +463,3 @@ this._setValue(defaults, 'isEarlyFlakeDetectionEnabled', false) | ||
this._setValue(defaults, 'lookup', undefined) | ||
this._setValue(defaults, 'memcachedCommandEnabled', false) | ||
this._setValue(defaults, 'openAiLogsEnabled', false) | ||
@@ -558,7 +485,10 @@ this._setValue(defaults, 'openaiSpanCharLimit', 128) | ||
this._setValue(defaults, 'sampler.rules', []) | ||
this._setValue(defaults, 'sampler.spanSamplingRules', []) | ||
this._setValue(defaults, 'scope', undefined) | ||
this._setValue(defaults, 'service', service) | ||
this._setValue(defaults, 'serviceMapping', {}) | ||
this._setValue(defaults, 'site', 'datadoghq.com') | ||
this._setValue(defaults, 'spanAttributeSchema', 'v0') | ||
this._setValue(defaults, 'spanComputePeerService', false) | ||
this._setValue(defaults, 'spanLeakDebug', 0) | ||
this._setValue(defaults, 'spanRemoveIntegrationFromService', false) | ||
@@ -577,2 +507,6 @@ this._setValue(defaults, 'startupLogs', false) | ||
this._setValue(defaults, 'traceId128BitLoggingEnabled', false) | ||
this._setValue(defaults, 'tracePropagationExtractFirst', false) | ||
this._setValue(defaults, 'tracePropagationStyle.inject', ['datadog', 'tracecontext']) | ||
this._setValue(defaults, 'tracePropagationStyle.extract', ['datadog', 'tracecontext']) | ||
this._setValue(defaults, 'tracePropagationStyle.otelPropagators', false) | ||
this._setValue(defaults, 'tracing', true) | ||
@@ -588,3 +522,7 @@ this._setValue(defaults, 'url', undefined) | ||
DD_AGENT_HOST, | ||
DD_API_SECURITY_ENABLED, | ||
DD_API_SECURITY_REQUEST_SAMPLE_RATE, | ||
DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING, | ||
DD_APPSEC_ENABLED, | ||
DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON, | ||
DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML, | ||
@@ -607,2 +545,3 @@ DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON, | ||
DD_ENV, | ||
DD_EXPERIMENTAL_API_SECURITY_ENABLED, | ||
DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED, | ||
@@ -634,4 +573,7 @@ DD_EXPERIMENTAL_PROFILING_ENABLED, | ||
DD_SERVICE, | ||
DD_SERVICE_MAPPING, | ||
DD_SERVICE_NAME, | ||
DD_SITE, | ||
DD_SPAN_SAMPLING_RULES, | ||
DD_SPAN_SAMPLING_RULES_FILE, | ||
DD_TAGS, | ||
@@ -656,5 +598,10 @@ DD_TELEMETRY_DEBUG, | ||
DD_TRACE_HEADER_TAGS, | ||
DD_TRACE_MEMCACHED_COMMAND_ENABLED, | ||
DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP, | ||
DD_TRACE_PARTIAL_FLUSH_MIN_SPANS, | ||
DD_TRACE_PEER_SERVICE_MAPPING, | ||
DD_TRACE_PROPAGATION_EXTRACT_FIRST, | ||
DD_TRACE_PROPAGATION_STYLE, | ||
DD_TRACE_PROPAGATION_STYLE_INJECT, | ||
DD_TRACE_PROPAGATION_STYLE_EXTRACT, | ||
DD_TRACE_RATE_LIMIT, | ||
@@ -667,2 +614,3 @@ DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED, | ||
DD_TRACE_SPAN_ATTRIBUTE_SCHEMA, | ||
DD_TRACE_SPAN_LEAK_DEBUG, | ||
DD_TRACE_STARTUP_LOGS, | ||
@@ -674,7 +622,8 @@ DD_TRACE_TAGS, | ||
DD_VERSION, | ||
OTEL_METRICS_EXPORTER, | ||
OTEL_PROPAGATORS, | ||
OTEL_RESOURCE_ATTRIBUTES, | ||
OTEL_SERVICE_NAME, | ||
OTEL_RESOURCE_ATTRIBUTES, | ||
OTEL_TRACES_SAMPLER, | ||
OTEL_TRACES_SAMPLER_ARG, | ||
OTEL_METRICS_EXPORTER | ||
OTEL_TRACES_SAMPLER_ARG | ||
} = process.env | ||
@@ -691,2 +640,8 @@ | ||
this._setBoolean(env, 'appsec.apiSecurity.enabled', coalesce( | ||
DD_API_SECURITY_ENABLED && isTrue(DD_API_SECURITY_ENABLED), | ||
DD_EXPERIMENTAL_API_SECURITY_ENABLED && isTrue(DD_EXPERIMENTAL_API_SECURITY_ENABLED) | ||
)) | ||
this._setUnit(env, 'appsec.apiSecurity.requestSampling', DD_API_SECURITY_REQUEST_SAMPLE_RATE) | ||
this._setValue(env, 'appsec.blockedTemplateGraphql', maybeFile(DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON)) | ||
this._setValue(env, 'appsec.blockedTemplateHtml', maybeFile(DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML)) | ||
@@ -697,2 +652,7 @@ this._envUnprocessed['appsec.blockedTemplateHtml'] = DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML | ||
this._setBoolean(env, 'appsec.enabled', DD_APPSEC_ENABLED) | ||
if (DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING) { | ||
this._setValue(env, 'appsec.eventTracking.enabled', | ||
['extended', 'safe'].includes(DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING.toLowerCase())) | ||
this._setValue(env, 'appsec.eventTracking.mode', DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING.toLowerCase()) | ||
} | ||
this._setString(env, 'appsec.obfuscatorKeyRegex', DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP) | ||
@@ -745,4 +705,7 @@ this._setString(env, 'appsec.obfuscatorValueRegex', DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP) | ||
this._setString(env, 'iast.telemetryVerbosity', DD_IAST_TELEMETRY_VERBOSITY) | ||
this._setBoolean(env, 'isAzureFunction', getIsAzureFunction()) | ||
this._setBoolean(env, 'isGCPFunction', getIsGCPFunction()) | ||
this._setBoolean(env, 'logInjection', DD_LOGS_INJECTION) | ||
// Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent | ||
this._setBoolean(env, 'memcachedCommandEnabled', DD_TRACE_MEMCACHED_COMMAND_ENABLED) | ||
this._setBoolean(env, 'openAiLogsEnabled', DD_OPENAI_LOGS_ENABLED) | ||
@@ -787,2 +750,6 @@ this._setValue(env, 'openaiSpanCharLimit', maybeInt(DD_OPENAI_SPAN_CHAR_LIMIT)) | ||
otelSetRuntimeMetrics) | ||
this._setArray(env, 'sampler.spanSamplingRules', reformatSpanSamplingRules(coalesce( | ||
safeJsonParse(maybeFile(DD_SPAN_SAMPLING_RULES_FILE)), | ||
safeJsonParse(DD_SPAN_SAMPLING_RULES) | ||
))) | ||
this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE || | ||
@@ -795,2 +762,7 @@ getFromOtelSamplerMap(OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG)) | ||
this._setString(env, 'service', DD_SERVICE || DD_SERVICE_NAME || tags.service || OTEL_SERVICE_NAME) | ||
if (DD_SERVICE_MAPPING) { | ||
this._setValue(env, 'serviceMapping', fromEntries( | ||
process.env.DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':')) | ||
)) | ||
} | ||
this._setString(env, 'site', DD_SITE) | ||
@@ -801,2 +773,4 @@ if (DD_TRACE_SPAN_ATTRIBUTE_SCHEMA) { | ||
} | ||
// 0: disabled, 1: logging, 2: garbage collection + logging | ||
this._setValue(env, 'spanLeakDebug', maybeInt(DD_TRACE_SPAN_LEAK_DEBUG)) | ||
this._setBoolean(env, 'spanRemoveIntegrationFromService', DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED) | ||
@@ -825,2 +799,9 @@ this._setBoolean(env, 'startupLogs', DD_TRACE_STARTUP_LOGS) | ||
this._setBoolean(env, 'traceId128BitLoggingEnabled', DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED) | ||
this._setBoolean(env, 'tracePropagationExtractFirst', DD_TRACE_PROPAGATION_EXTRACT_FIRST) | ||
this._setBoolean(env, 'tracePropagationStyle.otelPropagators', | ||
DD_TRACE_PROPAGATION_STYLE || | ||
DD_TRACE_PROPAGATION_STYLE_INJECT || | ||
DD_TRACE_PROPAGATION_STYLE_EXTRACT | ||
? false | ||
: !!OTEL_PROPAGATORS) | ||
this._setBoolean(env, 'tracing', DD_TRACING_ENABLED) | ||
@@ -839,2 +820,5 @@ this._setString(env, 'version', DD_VERSION || tags.version) | ||
this._setBoolean(opts, 'appsec.apiSecurity.enabled', options.appsec.apiSecurity?.enabled) | ||
this._setUnit(opts, 'appsec.apiSecurity.requestSampling', options.appsec.apiSecurity?.requestSampling) | ||
this._setValue(opts, 'appsec.blockedTemplateGraphql', maybeFile(options.appsec.blockedTemplateGraphql)) | ||
this._setValue(opts, 'appsec.blockedTemplateHtml', maybeFile(options.appsec.blockedTemplateHtml)) | ||
@@ -845,2 +829,8 @@ this._optsUnprocessed['appsec.blockedTemplateHtml'] = options.appsec.blockedTemplateHtml | ||
this._setBoolean(opts, 'appsec.enabled', options.appsec.enabled) | ||
let eventTracking = options.appsec.eventTracking?.mode | ||
if (eventTracking) { | ||
eventTracking = eventTracking.toLowerCase() | ||
this._setValue(opts, 'appsec.eventTracking.enabled', ['extended', 'safe'].includes(eventTracking)) | ||
this._setValue(opts, 'appsec.eventTracking.mode', eventTracking) | ||
} | ||
this._setString(opts, 'appsec.obfuscatorKeyRegex', options.appsec.obfuscatorKeyRegex) | ||
@@ -911,2 +901,3 @@ this._setString(opts, 'appsec.obfuscatorValueRegex', options.appsec.obfuscatorValueRegex) | ||
this._setBoolean(opts, 'runtimeMetrics', options.runtimeMetrics) | ||
this._setArray(opts, 'sampler.spanSamplingRules', reformatSpanSamplingRules(options.spanSamplingRules)) | ||
this._setUnit(opts, 'sampleRate', coalesce(options.sampleRate, options.ingestion.sampleRate)) | ||
@@ -917,2 +908,3 @@ const ingestion = options.ingestion || {} | ||
this._setString(opts, 'service', options.service || tags.service) | ||
this._setValue(opts, 'serviceMapping', options.serviceMapping) | ||
this._setString(opts, 'site', options.site) | ||
@@ -1027,3 +1019,4 @@ if (options.spanAttributeSchema) { | ||
const { | ||
DD_CIVISIBILITY_AGENTLESS_URL | ||
DD_CIVISIBILITY_AGENTLESS_URL, | ||
DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED | ||
} = process.env | ||
@@ -1038,3 +1031,3 @@ | ||
this._setBoolean(calc, 'isEarlyFlakeDetectionEnabled', | ||
coalesce(process.env.DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED, true)) | ||
coalesce(DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED, true)) | ||
this._setBoolean(calc, 'isIntelligentTestRunnerEnabled', isTrue(this._isCiVisibilityItrEnabled())) | ||
@@ -1048,2 +1041,15 @@ this._setBoolean(calc, 'isManualApiEnabled', this._isCiVisibilityManualApiEnabled()) | ||
this._setBoolean(calc, 'stats.enabled', this._isTraceStatsComputationEnabled()) | ||
const defaultPropagationStyle = this._getDefaultPropagationStyle(this._optionsArg) | ||
this._setValue(calc, 'tracePropagationStyle.inject', propagationStyle( | ||
'inject', | ||
this._optionsArg.tracePropagationStyle | ||
)) | ||
this._setValue(calc, 'tracePropagationStyle.extract', propagationStyle( | ||
'extract', | ||
this._optionsArg.tracePropagationStyle | ||
)) | ||
if (defaultPropagationStyle.length > 2) { | ||
calc['tracePropagationStyle.inject'] = calc['tracePropagationStyle.inject'] || defaultPropagationStyle | ||
calc['tracePropagationStyle.extract'] = calc['tracePropagationStyle.extract'] || defaultPropagationStyle | ||
} | ||
} | ||
@@ -1050,0 +1056,0 @@ |
@@ -5,2 +5,3 @@ 'use strict' | ||
const { trace, ROOT_CONTEXT } = require('@opentelemetry/api') | ||
const DataDogSpanContext = require('../opentracing/span_context') | ||
@@ -10,31 +11,2 @@ const SpanContext = require('./span_context') | ||
// Horrible hack to acquire the otherwise inaccessible SPAN_KEY so we can redirect it... | ||
// This is used for getting the current span context in OpenTelemetry, but the SPAN_KEY value is | ||
// not exposed as it's meant to be read-only from outside the module. We want to hijack this logic | ||
// so we can instead get the span context from the datadog context manager instead. | ||
let SPAN_KEY | ||
trace.getSpan({ | ||
getValue (key) { | ||
SPAN_KEY = key | ||
} | ||
}) | ||
// Whenever a value is acquired from the context map we should mostly delegate to the real getter, | ||
// but when accessing the current span we should hijack that access to instead provide a fake span | ||
// which we can use to get an OTel span context wrapping the datadog active scope span context. | ||
function wrappedGetValue (target) { | ||
return (key) => { | ||
if (key === SPAN_KEY) { | ||
return { | ||
spanContext () { | ||
const activeSpan = tracer.scope().active() | ||
const context = activeSpan && activeSpan.context() | ||
return new SpanContext(context) | ||
} | ||
} | ||
} | ||
return target.getValue(key) | ||
} | ||
} | ||
class ContextManager { | ||
@@ -46,9 +18,18 @@ constructor () { | ||
active () { | ||
const active = this._store.getStore() || ROOT_CONTEXT | ||
const activeSpan = tracer.scope().active() | ||
const store = this._store.getStore() | ||
const context = (activeSpan && activeSpan.context()) || store || ROOT_CONTEXT | ||
return new Proxy(active, { | ||
get (target, key) { | ||
return key === 'getValue' ? wrappedGetValue(target) : target[key] | ||
} | ||
}) | ||
if (!(context instanceof DataDogSpanContext)) { | ||
return context | ||
} | ||
if (!context._otelSpanContext) { | ||
const newSpanContext = new SpanContext(context) | ||
context._otelSpanContext = newSpanContext | ||
} | ||
if (store && trace.getSpanContext(store) === context._otelSpanContext) { | ||
return store | ||
} | ||
return trace.setSpanContext(store || ROOT_CONTEXT, context._otelSpanContext) | ||
} | ||
@@ -59,6 +40,10 @@ | ||
const ddScope = tracer.scope() | ||
return ddScope.activate(span._ddSpan, () => { | ||
const run = () => { | ||
const cb = thisArg == null ? fn : fn.bind(thisArg) | ||
return this._store.run(context, cb, ...args) | ||
}) | ||
} | ||
if (span && span._ddSpan) { | ||
return ddScope.activate(span._ddSpan, run) | ||
} | ||
return run() | ||
} | ||
@@ -73,7 +58,5 @@ | ||
// Not part of the spec but the Node.js API expects these | ||
enable () {} | ||
disable () {} | ||
} | ||
module.exports = ContextManager |
@@ -27,7 +27,7 @@ 'use strict' | ||
get traceId () { | ||
return this._ddContext._traceId.toString(16) | ||
return this._ddContext.toTraceId(true) | ||
} | ||
get spanId () { | ||
return this._ddContext._spanId.toString(16) | ||
return this._ddContext.toSpanId(true) | ||
} | ||
@@ -34,0 +34,0 @@ |
'use strict' | ||
const { trace, context } = require('@opentelemetry/api') | ||
const { trace, context, propagation } = require('@opentelemetry/api') | ||
const { W3CTraceContextPropagator } = require('@opentelemetry/core') | ||
@@ -55,2 +56,9 @@ const tracer = require('../../') | ||
} | ||
// The default propagator used is the W3C Trace Context propagator, users should be able to pass in others | ||
// as needed | ||
if (config.propagator) { | ||
propagation.setGlobalPropagator(config.propagator) | ||
} else { | ||
propagation.setGlobalPropagator(new W3CTraceContextPropagator()) | ||
} | ||
} | ||
@@ -57,0 +65,0 @@ |
@@ -10,2 +10,3 @@ 'use strict' | ||
const SpanContext = require('./span_context') | ||
const TextMapPropagator = require('../opentracing/propagation/text_map') | ||
@@ -26,2 +27,20 @@ class Tracer { | ||
_createSpanContextFromParent (parentSpanContext) { | ||
return new SpanContext({ | ||
traceId: parentSpanContext._traceId, | ||
spanId: id(), | ||
parentId: parentSpanContext._spanId, | ||
sampling: parentSpanContext._sampling, | ||
baggageItems: Object.assign({}, parentSpanContext._baggageItems), | ||
trace: parentSpanContext._trace, | ||
tracestate: parentSpanContext._tracestate | ||
}) | ||
} | ||
// Extracted method to create span context for a new span | ||
_createSpanContextForNewSpan (context) { | ||
const { traceId, spanId, traceFlags, traceState } = context | ||
return TextMapPropagator._convertOtelContextToDatadog(traceId, spanId, traceFlags, traceState) | ||
} | ||
startSpan (name, options = {}, context = api.context.active()) { | ||
@@ -34,17 +53,7 @@ // remove span from context in case a root span is requested via options | ||
const parentSpanContext = parentSpan && parentSpan.spanContext() | ||
let spanContext | ||
// TODO: Need a way to get 128-bit trace IDs for the validity check API to work... | ||
// if (parent && api.trace.isSpanContextValid(parent)) { | ||
if (parentSpanContext && parentSpanContext.traceId) { | ||
const parent = parentSpanContext._ddContext | ||
spanContext = new SpanContext({ | ||
traceId: parent._traceId, | ||
spanId: id(), | ||
parentId: parent._spanId, | ||
sampling: parent._sampling, | ||
baggageItems: Object.assign({}, parent._baggageItems), | ||
trace: parent._trace, | ||
tracestate: parent._tracestate | ||
}) | ||
if (parentSpanContext && api.trace.isSpanContextValid(parentSpanContext)) { | ||
spanContext = parentSpanContext._ddContext | ||
? this._createSpanContextFromParent(parentSpanContext._ddContext) | ||
: this._createSpanContextForNewSpan(parentSpanContext) | ||
} else { | ||
@@ -51,0 +60,0 @@ spanContext = new SpanContext() |
@@ -18,3 +18,3 @@ 'use strict' | ||
if (this._config.traceId128BitLoggingEnabled && spanContext._trace.tags['_dd.p.tid']) { | ||
carrier.dd.trace_id = spanContext._trace.tags['_dd.p.tid'] + spanContext._traceId.toString(16) | ||
carrier.dd.trace_id = spanContext.toTraceId(true) | ||
} else { | ||
@@ -21,0 +21,0 @@ carrier.dd.trace_id = spanContext.toTraceId() |
@@ -6,2 +6,3 @@ 'use strict' | ||
const DatadogSpanContext = require('../span_context') | ||
const OtelSpanContext = require('../../opentelemetry/span_context') | ||
const log = require('../../log') | ||
@@ -622,4 +623,63 @@ const TraceState = require('./tracestate') | ||
} | ||
static _convertOtelContextToDatadog (traceId, spanId, traceFlag, ts, meta = {}) { | ||
const origin = null | ||
let samplingPriority = traceFlag | ||
ts = ts?.traceparent || null | ||
if (ts) { | ||
// Use TraceState.fromString to parse the tracestate header | ||
const traceState = TraceState.fromString(ts) | ||
let ddTraceStateData = null | ||
// Extract Datadog specific trace state data | ||
traceState.forVendor('dd', (state) => { | ||
ddTraceStateData = state | ||
return state // You might need to adjust this part based on actual logic needed | ||
}) | ||
if (ddTraceStateData) { | ||
// Assuming ddTraceStateData is now a Map or similar structure containing Datadog trace state data | ||
// Extract values as needed, similar to the original logic | ||
const samplingPriorityTs = ddTraceStateData.get('s') | ||
const origin = ddTraceStateData.get('o') | ||
// Convert Map to object for meta | ||
const otherPropagatedTags = Object.fromEntries(ddTraceStateData.entries()) | ||
// Update meta and samplingPriority based on extracted values | ||
Object.assign(meta, otherPropagatedTags) | ||
samplingPriority = TextMapPropagator._getSamplingPriority(traceFlag, parseInt(samplingPriorityTs, 10), origin) | ||
} else { | ||
log.debug(`no dd list member in tracestate from incoming request: ${ts}`) | ||
} | ||
} | ||
const spanContext = new OtelSpanContext({ | ||
traceId: id(traceId, 16), spanId: id(), tags: meta, parentId: id(spanId, 16) | ||
}) | ||
spanContext._sampling = { priority: samplingPriority } | ||
spanContext._trace = { origin } | ||
return spanContext | ||
} | ||
static _getSamplingPriority (traceparentSampled, tracestateSamplingPriority, origin = null) { | ||
const fromRumWithoutPriority = !tracestateSamplingPriority && origin === 'rum' | ||
let samplingPriority | ||
if (!fromRumWithoutPriority && traceparentSampled === 0 && | ||
(!tracestateSamplingPriority || tracestateSamplingPriority >= 0)) { | ||
samplingPriority = 0 | ||
} else if (!fromRumWithoutPriority && traceparentSampled === 1 && | ||
(!tracestateSamplingPriority || tracestateSamplingPriority < 0)) { | ||
samplingPriority = 1 | ||
} else { | ||
samplingPriority = tracestateSamplingPriority | ||
} | ||
return samplingPriority | ||
} | ||
} | ||
module.exports = TextMapPropagator |
@@ -30,2 +30,3 @@ 'use strict' | ||
} | ||
this._otelSpanContext = undefined | ||
} | ||
@@ -32,0 +33,0 @@ |
@@ -148,3 +148,3 @@ const { | ||
testFramework, | ||
isUnsupportedCIProvider: this.isUnsupportedCIProvider, | ||
isUnsupportedCIProvider: !this.ciProviderName, | ||
...tags | ||
@@ -183,3 +183,3 @@ }) | ||
this.isUnsupportedCIProvider = !ciProviderName | ||
this.ciProviderName = ciProviderName | ||
@@ -186,0 +186,0 @@ this.testConfiguration = { |
@@ -72,2 +72,3 @@ 'use strict' | ||
get 'node:net' () { return require('../../../datadog-plugin-net/src') }, | ||
get nyc () { return require('../../../datadog-plugin-nyc/src') }, | ||
get oracledb () { return require('../../../datadog-plugin-oracledb/src') }, | ||
@@ -74,0 +75,0 @@ get openai () { return require('../../../datadog-plugin-openai/src') }, |
@@ -80,2 +80,14 @@ const cp = require('child_process') | ||
function isGitAvailable () { | ||
const isWindows = os.platform() === 'win32' | ||
const command = isWindows ? 'where' : 'which' | ||
try { | ||
cp.execFileSync(command, ['git'], { stdio: 'pipe' }) | ||
return true | ||
} catch (e) { | ||
incrementCountMetric(TELEMETRY_GIT_COMMAND_ERRORS, { command: 'check_git', exitCode: 'missing' }) | ||
return false | ||
} | ||
} | ||
function isShallowRepository () { | ||
@@ -346,3 +358,4 @@ return sanitizedExec( | ||
isShallowRepository, | ||
unshallowRepository | ||
unshallowRepository, | ||
isGitAvailable | ||
} |
@@ -320,3 +320,3 @@ 'use strict' | ||
const namesNeedFormatting = new Set(['DD_TAGS', 'peerServiceMapping']) | ||
const namesNeedFormatting = new Set(['DD_TAGS', 'peerServiceMapping', 'serviceMapping']) | ||
@@ -323,0 +323,0 @@ const configuration = [] |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 11 instances in 1 package
1911126
558
55131
175
+ Added@datadog/native-iast-rewriter@2.4.1(transitive)
+ Added@datadog/native-iast-taint-tracking@3.1.0(transitive)
+ Added@types/node@22.5.4(transitive)
+ Addedpath-to-regexp@0.1.10(transitive)
- Removed@datadog/native-iast-rewriter@2.4.0(transitive)
- Removed@datadog/native-iast-taint-tracking@3.0.0(transitive)
- Removed@types/node@22.5.5(transitive)
- Removedpath-to-regexp@0.1.11(transitive)