dd-trace 5.89.0 → 5.91.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 +0 -3
- package/index.d.ts +38 -0
- package/package.json +12 -11
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +75 -0
- package/packages/datadog-instrumentations/src/cucumber.js +58 -4
- package/packages/datadog-instrumentations/src/elasticsearch.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +30 -0
- package/packages/datadog-instrumentations/src/jest.js +31 -2
- package/packages/datadog-instrumentations/src/langgraph.js +7 -0
- package/packages/datadog-instrumentations/src/mocha/main.js +41 -12
- package/packages/datadog-instrumentations/src/mocha/utils.js +6 -1
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -4
- package/packages/datadog-instrumentations/src/playwright.js +20 -2
- package/packages/datadog-instrumentations/src/prisma.js +4 -2
- package/packages/datadog-instrumentations/src/vitest.js +69 -24
- package/packages/datadog-plugin-apollo/src/gateway/execute.js +8 -0
- package/packages/datadog-plugin-apollo/src/gateway/fetch.js +5 -0
- package/packages/datadog-plugin-apollo/src/gateway/plan.js +8 -0
- package/packages/datadog-plugin-apollo/src/gateway/postprocessing.js +5 -0
- package/packages/datadog-plugin-apollo/src/gateway/request.js +4 -3
- package/packages/datadog-plugin-apollo/src/gateway/validate.js +4 -3
- package/packages/datadog-plugin-apollo/src/index.js +28 -0
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +49 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +12 -2
- package/packages/datadog-plugin-cypress/src/support.js +5 -7
- package/packages/datadog-plugin-jest/src/index.js +6 -0
- package/packages/datadog-plugin-langgraph/src/index.js +24 -0
- package/packages/datadog-plugin-langgraph/src/stream.js +41 -0
- package/packages/datadog-plugin-playwright/src/index.js +35 -8
- package/packages/dd-trace/src/aiguard/noop.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +14 -5
- package/packages/dd-trace/src/appsec/api_security_sampler.js +22 -1
- package/packages/dd-trace/src/appsec/index.js +11 -1
- package/packages/dd-trace/src/appsec/reporter.js +28 -11
- package/packages/dd-trace/src/appsec/waf/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/config/defaults.js +1 -0
- package/packages/dd-trace/src/config/index.js +9 -1
- package/packages/dd-trace/src/config/supported-configurations.json +14 -0
- package/packages/dd-trace/src/constants.js +2 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -0
- package/packages/dd-trace/src/dogstatsd.js +1 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +1 -8
- package/packages/dd-trace/src/encode/agentless-json.js +67 -22
- package/packages/dd-trace/src/exporters/agentless/index.js +58 -15
- package/packages/dd-trace/src/exporters/agentless/writer.js +35 -18
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/anthropic.js +9 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +11 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +114 -0
- package/packages/dd-trace/src/llmobs/tagger.js +8 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -0
- package/packages/dd-trace/src/plugins/apollo.js +7 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +2 -2
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/ci.js +95 -3
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +36 -2
- package/packages/dd-trace/src/plugins/util/test.js +7 -10
- package/packages/dd-trace/src/plugins/util/web.js +31 -11
- package/packages/dd-trace/src/priority_sampler.js +20 -2
- package/packages/dd-trace/src/process-tags/index.js +41 -34
- package/packages/dd-trace/src/profiling/profilers/wall.js +9 -1
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +4 -0
- package/packages/dd-trace/src/standalone/product.js +2 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { readFileSync } = require('fs')
|
|
3
|
+
const { readFileSync, readdirSync, existsSync } = require('fs')
|
|
4
|
+
const path = require('path')
|
|
4
5
|
const { getEnvironmentVariable, getEnvironmentVariables, getValueFromEnvSources } = require('../../config/helper')
|
|
5
6
|
const {
|
|
6
7
|
GIT_BRANCH,
|
|
@@ -102,8 +103,92 @@ function getGitHubEventPayload () {
|
|
|
102
103
|
return JSON.parse(readFileSync(path, 'utf8'))
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
function getJobIDFromDiagFile (runnerTemp) {
|
|
107
|
+
if (!runnerTemp || !existsSync(runnerTemp)) { return null }
|
|
108
|
+
|
|
109
|
+
// RUNNER_TEMP usually looks like:
|
|
110
|
+
// Linux/mac hosted: /home/runner/work/_temp
|
|
111
|
+
// Windows hosted: C:\actions-runner\_work\_temp
|
|
112
|
+
// Self-hosted (unix): /opt/actions-runner/_work/_temp
|
|
113
|
+
|
|
114
|
+
const workDir = path.dirname(runnerTemp) // .../work or .../_work
|
|
115
|
+
const runnerRoot = path.dirname(workDir) // /home/runner/ (runner root)
|
|
116
|
+
|
|
117
|
+
const dirs = [
|
|
118
|
+
path.join(runnerRoot, 'cached', '_diag'),
|
|
119
|
+
path.join(runnerRoot, '_diag'),
|
|
120
|
+
path.join(runnerRoot, 'actions-runner', 'cached', '_diag'),
|
|
121
|
+
path.join(runnerRoot, 'actions-runner', '_diag'),
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
const isWin = process.platform === 'win32'
|
|
125
|
+
|
|
126
|
+
// Hardcoded fallbacks
|
|
127
|
+
if (isWin) {
|
|
128
|
+
dirs.push(
|
|
129
|
+
'C:/actions-runner/cached/_diag',
|
|
130
|
+
'C:/actions-runner/_diag',
|
|
131
|
+
)
|
|
132
|
+
} else {
|
|
133
|
+
dirs.push(
|
|
134
|
+
'/home/runner/actions-runner/cached/_diag',
|
|
135
|
+
'/home/runner/actions-runner/_diag',
|
|
136
|
+
'/opt/actions-runner/_diag',
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Remove duplicates
|
|
141
|
+
const possibleDiagsPaths = [...new Set(dirs)]
|
|
142
|
+
|
|
143
|
+
// This will hold the names of the worker log files that (potentially) contain the Job ID
|
|
144
|
+
let workerLogFiles = []
|
|
145
|
+
|
|
146
|
+
// This will hold the chosen diagnostics path (between the ones that are contemplated in possibleDiagsPath)
|
|
147
|
+
let chosenDiagPath = ''
|
|
148
|
+
|
|
149
|
+
for (const diagPath of possibleDiagsPaths) {
|
|
150
|
+
try {
|
|
151
|
+
// Obtain a list of fs.Dirent objects of the files in diagPath
|
|
152
|
+
const files = readdirSync(diagPath, { withFileTypes: true })
|
|
153
|
+
|
|
154
|
+
// Check if there are valid potential log files
|
|
155
|
+
const potentialLogs = files
|
|
156
|
+
.filter((file) => file.isFile() && file.name.startsWith('Worker_'))
|
|
157
|
+
.map((file) => file.name)
|
|
158
|
+
|
|
159
|
+
if (potentialLogs.length > 0) {
|
|
160
|
+
chosenDiagPath = diagPath
|
|
161
|
+
workerLogFiles = potentialLogs
|
|
162
|
+
break // No need to keep looking for more log files
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
// If the directory was not found, just look in the next one
|
|
166
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Any other kind of error must force a return
|
|
171
|
+
return null
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Get the job ID via regex
|
|
176
|
+
for (const logFile of workerLogFiles) {
|
|
177
|
+
const filePath = path.posix.join(chosenDiagPath, logFile)
|
|
178
|
+
const content = readFileSync(filePath, 'utf8')
|
|
179
|
+
|
|
180
|
+
const match = content.match(/"job":\s*{[\s\S]*?"v"\s*:\s*(\d+)(?:\.0)?/)
|
|
181
|
+
|
|
182
|
+
// match[1] is the captured group with the display name
|
|
183
|
+
if (match && match[1]) { return match[1] }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return null
|
|
187
|
+
}
|
|
188
|
+
|
|
105
189
|
module.exports = {
|
|
106
190
|
normalizeRef,
|
|
191
|
+
getJobIDFromDiagFile,
|
|
107
192
|
getCIMetadata () {
|
|
108
193
|
const env = getEnvironmentVariables()
|
|
109
194
|
|
|
@@ -281,6 +366,8 @@ module.exports = {
|
|
|
281
366
|
GITHUB_RUN_ATTEMPT,
|
|
282
367
|
GITHUB_JOB,
|
|
283
368
|
GITHUB_BASE_REF,
|
|
369
|
+
RUNNER_TEMP,
|
|
370
|
+
JOB_CHECK_RUN_ID,
|
|
284
371
|
} = env
|
|
285
372
|
|
|
286
373
|
const repositoryURL = `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git`
|
|
@@ -290,7 +377,12 @@ module.exports = {
|
|
|
290
377
|
pipelineURL = `${pipelineURL}/attempts/${GITHUB_RUN_ATTEMPT}`
|
|
291
378
|
}
|
|
292
379
|
|
|
293
|
-
|
|
380
|
+
// Build the job url extracting the job ID. If extraction fails, job url is constructed as a generalized url
|
|
381
|
+
const GITHUB_JOB_ID = JOB_CHECK_RUN_ID ?? getJobIDFromDiagFile(RUNNER_TEMP)
|
|
382
|
+
const jobUrl =
|
|
383
|
+
GITHUB_JOB_ID === null
|
|
384
|
+
? `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}/checks`
|
|
385
|
+
: `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/job/${GITHUB_JOB_ID}`
|
|
294
386
|
|
|
295
387
|
const ref = GITHUB_HEAD_REF || GITHUB_REF || ''
|
|
296
388
|
const refKey = ref.includes('tags/') ? GIT_TAG : GIT_BRANCH
|
|
@@ -315,7 +407,7 @@ module.exports = {
|
|
|
315
407
|
GITHUB_RUN_ID,
|
|
316
408
|
GITHUB_RUN_ATTEMPT,
|
|
317
409
|
}),
|
|
318
|
-
[CI_JOB_ID]: GITHUB_JOB,
|
|
410
|
+
[CI_JOB_ID]: GITHUB_JOB_ID ?? GITHUB_JOB,
|
|
319
411
|
}
|
|
320
412
|
if (GITHUB_BASE_REF) { // `pull_request` or `pull_request_target` event
|
|
321
413
|
tags[GIT_PULL_REQUEST_BASE_BRANCH] = GITHUB_BASE_REF
|
|
@@ -5,8 +5,10 @@ const tags = require('../../../../../ext/tags')
|
|
|
5
5
|
|
|
6
6
|
const RESOURCE_NAME = tags.RESOURCE_NAME
|
|
7
7
|
const SPAN_TYPE = tags.SPAN_TYPE
|
|
8
|
+
const SPAN_KIND = tags.SPAN_KIND
|
|
8
9
|
const HTTP_URL = tags.HTTP_URL
|
|
9
10
|
const HTTP_METHOD = tags.HTTP_METHOD
|
|
11
|
+
const HTTP_ROUTE = tags.HTTP_ROUTE
|
|
10
12
|
|
|
11
13
|
const PROXY_HEADER_SYSTEM = 'x-dd-proxy'
|
|
12
14
|
const PROXY_HEADER_START_TIME_MS = 'x-dd-proxy-request-time-ms'
|
|
@@ -15,12 +17,20 @@ const PROXY_HEADER_HTTPMETHOD = 'x-dd-proxy-httpmethod'
|
|
|
15
17
|
const PROXY_HEADER_DOMAIN = 'x-dd-proxy-domain-name'
|
|
16
18
|
const PROXY_HEADER_STAGE = 'x-dd-proxy-stage'
|
|
17
19
|
const PROXY_HEADER_REGION = 'x-dd-proxy-region'
|
|
20
|
+
const PROXY_HEADER_RESOURCE_PATH = 'x-dd-proxy-resource-path'
|
|
21
|
+
const PROXY_HEADER_ACCOUNT_ID = 'x-dd-proxy-account-id'
|
|
22
|
+
const PROXY_HEADER_API_ID = 'x-dd-proxy-api-id'
|
|
23
|
+
const PROXY_HEADER_AWS_USER = 'x-dd-proxy-user'
|
|
18
24
|
|
|
19
25
|
const supportedProxies = {
|
|
20
26
|
'aws-apigateway': {
|
|
21
27
|
spanName: 'aws.apigateway',
|
|
22
28
|
component: 'aws-apigateway',
|
|
23
29
|
},
|
|
30
|
+
'aws-httpapi': {
|
|
31
|
+
spanName: 'aws.httpapi',
|
|
32
|
+
component: 'aws-httpapi',
|
|
33
|
+
},
|
|
24
34
|
'azure-apim': {
|
|
25
35
|
spanName: 'azure.apim',
|
|
26
36
|
component: 'azure-apim',
|
|
@@ -55,10 +65,16 @@ function createInferredProxySpan (headers, childOf, tracer, reqCtx, traceCtx, co
|
|
|
55
65
|
service: proxyContext.domainName || tracer._config.service,
|
|
56
66
|
component: proxySpanInfo.component,
|
|
57
67
|
[SPAN_TYPE]: 'web',
|
|
68
|
+
[SPAN_KIND]: 'server',
|
|
58
69
|
[HTTP_METHOD]: proxyContext.method,
|
|
59
|
-
[HTTP_URL]: proxyContext.domainName + proxyContext.path,
|
|
70
|
+
[HTTP_URL]: 'https://' + proxyContext.domainName + proxyContext.path,
|
|
60
71
|
stage: proxyContext.stage,
|
|
61
72
|
region: proxyContext.region,
|
|
73
|
+
...(proxyContext.resourcePath && { [HTTP_ROUTE]: proxyContext.resourcePath }),
|
|
74
|
+
...(proxyContext.accountId && { account_id: proxyContext.accountId }),
|
|
75
|
+
...(proxyContext.apiId && { apiid: proxyContext.apiId }),
|
|
76
|
+
...(proxyContext.region && { region: proxyContext.region }),
|
|
77
|
+
...(proxyContext.awsUser && { aws_user: proxyContext.awsUser }),
|
|
62
78
|
},
|
|
63
79
|
}, traceCtx, config)
|
|
64
80
|
|
|
@@ -73,8 +89,22 @@ function createInferredProxySpan (headers, childOf, tracer, reqCtx, traceCtx, co
|
|
|
73
89
|
}
|
|
74
90
|
|
|
75
91
|
function setInferredProxySpanTags (span, proxyContext) {
|
|
76
|
-
|
|
92
|
+
const resourcePath = proxyContext.resourcePath || proxyContext.path
|
|
93
|
+
span.setTag(RESOURCE_NAME, `${proxyContext.method} ${resourcePath}`)
|
|
77
94
|
span.setTag('_dd.inferred_span', 1)
|
|
95
|
+
|
|
96
|
+
// Set dd_resource_key as API Gateway ARN if we have the required components
|
|
97
|
+
if (proxyContext.apiId && proxyContext.region) {
|
|
98
|
+
const partition = 'aws'
|
|
99
|
+
// API Gateway v1 (REST): arn:{partition}:apigateway:{region}::/restapis/{api-id}
|
|
100
|
+
// API Gateway v2 (HTTP): arn:{partition}:apigateway:{region}::/apis/{api-id}
|
|
101
|
+
const apiType = proxyContext.proxySystemName === 'aws-httpapi' ? 'apis' : 'restapis'
|
|
102
|
+
span.setTag(
|
|
103
|
+
'dd_resource_key',
|
|
104
|
+
`arn:${partition}:apigateway:${proxyContext.region}::/${apiType}/${proxyContext.apiId}`
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
78
108
|
return span
|
|
79
109
|
}
|
|
80
110
|
|
|
@@ -98,6 +128,10 @@ function extractInferredProxyContext (headers) {
|
|
|
98
128
|
domainName: headers[PROXY_HEADER_DOMAIN],
|
|
99
129
|
proxySystemName: headers[PROXY_HEADER_SYSTEM],
|
|
100
130
|
region: headers[PROXY_HEADER_REGION],
|
|
131
|
+
resourcePath: headers[PROXY_HEADER_RESOURCE_PATH],
|
|
132
|
+
accountId: headers[PROXY_HEADER_ACCOUNT_ID],
|
|
133
|
+
apiId: headers[PROXY_HEADER_API_ID],
|
|
134
|
+
awsUser: headers[PROXY_HEADER_AWS_USER],
|
|
101
135
|
}
|
|
102
136
|
}
|
|
103
137
|
|
|
@@ -150,7 +150,6 @@ const DD_CAPABILITIES_FAILED_TEST_REPLAY = '_dd.library_capabilities.failed_test
|
|
|
150
150
|
const DD_CI_LIBRARY_CONFIGURATION_ERROR = '_dd.ci.library_configuration_error'
|
|
151
151
|
|
|
152
152
|
const UNSUPPORTED_TIA_FRAMEWORKS = new Set(['playwright', 'vitest'])
|
|
153
|
-
const UNSUPPORTED_TIA_FRAMEWORKS_PARALLEL_MODE = new Set(['cucumber', 'mocha'])
|
|
154
153
|
const MINIMUM_FRAMEWORK_VERSION_FOR_EFD = {
|
|
155
154
|
playwright: '>=1.38.0',
|
|
156
155
|
}
|
|
@@ -170,7 +169,6 @@ const MINIMUM_FRAMEWORK_VERSION_FOR_FAILED_TEST_REPLAY = {
|
|
|
170
169
|
playwright: '>=1.38.0',
|
|
171
170
|
}
|
|
172
171
|
|
|
173
|
-
const UNSUPPORTED_ATTEMPT_TO_FIX_FRAMEWORKS_PARALLEL_MODE = new Set(['mocha'])
|
|
174
172
|
const NOT_SUPPORTED_GRANULARITY_IMPACTED_TESTS_FRAMEWORKS = new Set(['mocha', 'playwright', 'vitest'])
|
|
175
173
|
|
|
176
174
|
const TEST_LEVEL_EVENT_TYPES = [
|
|
@@ -987,9 +985,8 @@ function getFormattedError (error, repositoryRoot) {
|
|
|
987
985
|
return newError
|
|
988
986
|
}
|
|
989
987
|
|
|
990
|
-
function isTiaSupported (testFramework
|
|
991
|
-
return !
|
|
992
|
-
(isParallel && UNSUPPORTED_TIA_FRAMEWORKS_PARALLEL_MODE.has(testFramework)))
|
|
988
|
+
function isTiaSupported (testFramework) {
|
|
989
|
+
return !UNSUPPORTED_TIA_FRAMEWORKS.has(testFramework)
|
|
993
990
|
}
|
|
994
991
|
|
|
995
992
|
function isEarlyFlakeDetectionSupported (testFramework, frameworkVersion) {
|
|
@@ -1016,12 +1013,12 @@ function isDisableSupported (testFramework, frameworkVersion) {
|
|
|
1016
1013
|
: true
|
|
1017
1014
|
}
|
|
1018
1015
|
|
|
1019
|
-
function isAttemptToFixSupported (testFramework,
|
|
1016
|
+
function isAttemptToFixSupported (testFramework, frameworkVersion) {
|
|
1020
1017
|
if (testFramework === 'playwright') {
|
|
1021
1018
|
return satisfies(frameworkVersion, MINIMUM_FRAMEWORK_VERSION_FOR_ATTEMPT_TO_FIX[testFramework])
|
|
1022
1019
|
}
|
|
1023
1020
|
|
|
1024
|
-
return
|
|
1021
|
+
return true
|
|
1025
1022
|
}
|
|
1026
1023
|
|
|
1027
1024
|
function isFailedTestReplaySupported (testFramework, frameworkVersion) {
|
|
@@ -1030,9 +1027,9 @@ function isFailedTestReplaySupported (testFramework, frameworkVersion) {
|
|
|
1030
1027
|
: true
|
|
1031
1028
|
}
|
|
1032
1029
|
|
|
1033
|
-
function getLibraryCapabilitiesTags (testFramework,
|
|
1030
|
+
function getLibraryCapabilitiesTags (testFramework, frameworkVersion) {
|
|
1034
1031
|
return {
|
|
1035
|
-
[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS]: isTiaSupported(testFramework
|
|
1032
|
+
[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS]: isTiaSupported(testFramework)
|
|
1036
1033
|
? '1'
|
|
1037
1034
|
: undefined,
|
|
1038
1035
|
[DD_CAPABILITIES_EARLY_FLAKE_DETECTION]: isEarlyFlakeDetectionSupported(testFramework, frameworkVersion)
|
|
@@ -1049,7 +1046,7 @@ function getLibraryCapabilitiesTags (testFramework, isParallel, frameworkVersion
|
|
|
1049
1046
|
? '1'
|
|
1050
1047
|
: undefined,
|
|
1051
1048
|
[DD_CAPABILITIES_TEST_MANAGEMENT_ATTEMPT_TO_FIX]:
|
|
1052
|
-
isAttemptToFixSupported(testFramework,
|
|
1049
|
+
isAttemptToFixSupported(testFramework, frameworkVersion)
|
|
1053
1050
|
? '5'
|
|
1054
1051
|
: undefined,
|
|
1055
1052
|
[DD_CAPABILITIES_FAILED_TEST_REPLAY]: isFailedTestReplaySupported(testFramework, frameworkVersion)
|
|
@@ -410,6 +410,13 @@ const web = {
|
|
|
410
410
|
},
|
|
411
411
|
})
|
|
412
412
|
},
|
|
413
|
+
setRouteOrEndpointTag (req) {
|
|
414
|
+
const context = contexts.get(req)
|
|
415
|
+
|
|
416
|
+
if (!context) return
|
|
417
|
+
|
|
418
|
+
applyRouteOrEndpointTag(context)
|
|
419
|
+
},
|
|
413
420
|
}
|
|
414
421
|
|
|
415
422
|
function addAllowHeaders (req, res, headers) {
|
|
@@ -481,18 +488,9 @@ function addRequestTags (context, spanType) {
|
|
|
481
488
|
}
|
|
482
489
|
|
|
483
490
|
function addResponseTags (context) {
|
|
484
|
-
const { req, res,
|
|
491
|
+
const { req, res, inferredProxySpan, span } = context
|
|
485
492
|
|
|
486
|
-
|
|
487
|
-
if (route) {
|
|
488
|
-
// Use http.route from trusted framework instrumentation
|
|
489
|
-
span.setTag(HTTP_ROUTE, route)
|
|
490
|
-
} else if (config.resourceRenamingEnabled) {
|
|
491
|
-
// Route is unavailable, compute http.endpoint instead
|
|
492
|
-
const url = span.context()._tags[HTTP_URL]
|
|
493
|
-
const endpoint = url ? calculateHttpEndpoint(url) : '/'
|
|
494
|
-
span.setTag(HTTP_ENDPOINT, endpoint)
|
|
495
|
-
}
|
|
493
|
+
applyRouteOrEndpointTag(context)
|
|
496
494
|
|
|
497
495
|
span.addTags({
|
|
498
496
|
[HTTP_STATUS_CODE]: res.statusCode,
|
|
@@ -504,6 +502,28 @@ function addResponseTags (context) {
|
|
|
504
502
|
web.addStatusError(req, res.statusCode)
|
|
505
503
|
}
|
|
506
504
|
|
|
505
|
+
function applyRouteOrEndpointTag (context) {
|
|
506
|
+
const { paths, span, config } = context
|
|
507
|
+
if (!span) return
|
|
508
|
+
const tags = span.context()._tags
|
|
509
|
+
const route = paths.join('')
|
|
510
|
+
|
|
511
|
+
if (route) {
|
|
512
|
+
// Use http.route from trusted framework instrumentation.
|
|
513
|
+
span.setTag(HTTP_ROUTE, route)
|
|
514
|
+
return
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (!config.resourceRenamingEnabled || tags[HTTP_ENDPOINT]) {
|
|
518
|
+
return
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Route is unavailable, compute http.endpoint once.
|
|
522
|
+
const url = tags[HTTP_URL]
|
|
523
|
+
const endpoint = url ? calculateHttpEndpoint(url) : '/'
|
|
524
|
+
span.setTag(HTTP_ENDPOINT, endpoint)
|
|
525
|
+
}
|
|
526
|
+
|
|
507
527
|
function addResourceTag (context) {
|
|
508
528
|
const { req, span } = context
|
|
509
529
|
const tags = span.context()._tags
|
|
@@ -31,10 +31,21 @@ const {
|
|
|
31
31
|
SAMPLING_LIMIT_DECISION,
|
|
32
32
|
SAMPLING_AGENT_DECISION,
|
|
33
33
|
DECISION_MAKER_KEY,
|
|
34
|
+
SAMPLING_KNUTH_RATE,
|
|
34
35
|
} = require('./constants')
|
|
35
36
|
|
|
36
37
|
const DEFAULT_KEY = 'service:,env:'
|
|
37
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Formats a sampling rate as a string with up to 6 significant digits and no trailing zeros.
|
|
41
|
+
*
|
|
42
|
+
* @param {number} rate
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
function formatKnuthRate (rate) {
|
|
46
|
+
return Number(rate.toPrecision(6)).toString()
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
const defaultSampler = new Sampler(AUTO_KEEP)
|
|
39
50
|
|
|
40
51
|
/**
|
|
@@ -254,6 +265,7 @@ class PrioritySampler {
|
|
|
254
265
|
*/
|
|
255
266
|
#getPriorityByRule (context, rule) {
|
|
256
267
|
context._trace[SAMPLING_RULE_DECISION] = rule.sampleRate
|
|
268
|
+
context._trace.tags[SAMPLING_KNUTH_RATE] = formatKnuthRate(rule.sampleRate)
|
|
257
269
|
context._sampling.mechanism = SAMPLING_MECHANISM_RULE
|
|
258
270
|
if (rule.provenance === 'customer') context._sampling.mechanism = SAMPLING_MECHANISM_REMOTE_USER
|
|
259
271
|
if (rule.provenance === 'dynamic') context._sampling.mechanism = SAMPLING_MECHANISM_REMOTE_DYNAMIC
|
|
@@ -290,9 +302,15 @@ class PrioritySampler {
|
|
|
290
302
|
// TODO: Change underscored properties to private ones.
|
|
291
303
|
const sampler = this._samplers[key] || this._samplers[DEFAULT_KEY]
|
|
292
304
|
|
|
293
|
-
|
|
305
|
+
const rate = sampler.rate()
|
|
306
|
+
context._trace[SAMPLING_AGENT_DECISION] = rate
|
|
294
307
|
|
|
295
|
-
|
|
308
|
+
if (sampler === defaultSampler) {
|
|
309
|
+
context._sampling.mechanism = SAMPLING_MECHANISM_DEFAULT
|
|
310
|
+
} else {
|
|
311
|
+
context._trace.tags[SAMPLING_KNUTH_RATE] = formatKnuthRate(rate)
|
|
312
|
+
context._sampling.mechanism = SAMPLING_MECHANISM_AGENT
|
|
313
|
+
}
|
|
296
314
|
|
|
297
315
|
return sampler.isSampled(context) ? AUTO_KEEP : AUTO_REJECT
|
|
298
316
|
}
|
|
@@ -12,8 +12,19 @@ const ENTRYPOINT_PATH = require.main?.filename || ''
|
|
|
12
12
|
// entrypoint.basedir = baz
|
|
13
13
|
// package.json.name = <from package.json>
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Sanitize a process tag value
|
|
17
|
+
*
|
|
18
|
+
* @param {string} value
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
function sanitize (value) {
|
|
22
|
+
return String(value)
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
.replaceAll(/[^a-zA-Z0-9/_.-]+/g, '_')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildProcessTags (config) {
|
|
17
28
|
// Lazy load pkg to avoid issues with require.main during test initialization
|
|
18
29
|
const pkg = require('../pkg')
|
|
19
30
|
|
|
@@ -35,6 +46,13 @@ function getProcessTags () {
|
|
|
35
46
|
['package.json.name', pkg.name || undefined],
|
|
36
47
|
]
|
|
37
48
|
|
|
49
|
+
// If config dependent tags keep growing, we should consider moving this into a function
|
|
50
|
+
if (config?.isServiceNameInferred) {
|
|
51
|
+
tags.push(['svc.auto', config.service])
|
|
52
|
+
} else if (config) {
|
|
53
|
+
tags.push(['svc.user', true])
|
|
54
|
+
}
|
|
55
|
+
|
|
38
56
|
const tagsArray = []
|
|
39
57
|
const tagsObject = {}
|
|
40
58
|
|
|
@@ -46,38 +64,27 @@ function getProcessTags () {
|
|
|
46
64
|
}
|
|
47
65
|
}
|
|
48
66
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
serialized,
|
|
54
|
-
tagsObject,
|
|
55
|
-
tagsArray,
|
|
56
|
-
}
|
|
67
|
+
processTags.tags = tags
|
|
68
|
+
processTags.serialized = tagsArray.join(',')
|
|
69
|
+
processTags.tagsObject = tagsObject
|
|
70
|
+
processTags.tagsArray = tagsArray
|
|
57
71
|
}
|
|
58
72
|
|
|
59
|
-
//
|
|
60
|
-
module.exports =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
*/
|
|
77
|
-
function sanitize (value) {
|
|
78
|
-
return String(value)
|
|
79
|
-
.toLowerCase()
|
|
80
|
-
.replaceAll(/[^a-zA-Z0-9/_.-]+/g, '_')
|
|
73
|
+
// Singleton with constant defaults so pre-init reads don't blow up
|
|
74
|
+
const processTags = module.exports = {
|
|
75
|
+
initialize (config) {
|
|
76
|
+
// check if one of the properties added during build exist and if so return
|
|
77
|
+
if (processTags.tags) return
|
|
78
|
+
buildProcessTags(config)
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
TRACING_FIELD_NAME: '_dd.tags.process',
|
|
82
|
+
DSM_FIELD_NAME: 'ProcessTags',
|
|
83
|
+
PROFILING_FIELD_NAME: 'process_tags',
|
|
84
|
+
DYNAMIC_INSTRUMENTATION_FIELD_NAME: 'process_tags',
|
|
85
|
+
TELEMETRY_FIELD_NAME: 'process_tags',
|
|
86
|
+
REMOTE_CONFIG_FIELD_NAME: 'process_tags',
|
|
87
|
+
CRASH_TRACKING_FIELD_NAME: 'process_tags',
|
|
88
|
+
CLIENT_TRACE_STATISTICS_FIELD_NAME: 'ProcessTags',
|
|
89
|
+
sanitize,
|
|
81
90
|
}
|
|
82
|
-
|
|
83
|
-
module.exports.sanitize = sanitize
|
|
@@ -290,7 +290,15 @@ class NativeWallProfiler {
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
profilingContext = { spanId, rootSpanId, webTags }
|
|
293
|
-
|
|
293
|
+
// Don't cache if endpoint collection is enabled and webTags is undefined but
|
|
294
|
+
// the span's type hasn't been set yet. TracingPlugin.startSpan() calls
|
|
295
|
+
// enterWith() before the plugin sets span.type='web' via addRequestTags(),
|
|
296
|
+
// so the first enterCh event fires before the type is known. Without this
|
|
297
|
+
// guard we'd cache webTags=undefined and then serve that stale value on the
|
|
298
|
+
// subsequent activation (when span.type='web' is already set).
|
|
299
|
+
if (!this.#endpointCollectionEnabled || webTags !== undefined || context._tags['span.type']) {
|
|
300
|
+
span[ProfilingContext] = profilingContext
|
|
301
|
+
}
|
|
294
302
|
}
|
|
295
303
|
return profilingContext
|
|
296
304
|
}
|
|
@@ -13,6 +13,7 @@ const nomenclature = require('./service-naming')
|
|
|
13
13
|
const PluginManager = require('./plugin_manager')
|
|
14
14
|
const NoopDogStatsDClient = require('./noop/dogstatsd')
|
|
15
15
|
const { IS_SERVERLESS } = require('./serverless')
|
|
16
|
+
const processTags = require('./process-tags')
|
|
16
17
|
const {
|
|
17
18
|
setBaggageItem,
|
|
18
19
|
getBaggageItem,
|
|
@@ -102,6 +103,9 @@ class Tracer extends NoopProxy {
|
|
|
102
103
|
try {
|
|
103
104
|
const config = getConfig(options) // TODO: support dynamic code config
|
|
104
105
|
|
|
106
|
+
// Add config dependent process tags
|
|
107
|
+
processTags.initialize(config)
|
|
108
|
+
|
|
105
109
|
// Configure propagation hash manager for process tags + container tags
|
|
106
110
|
const propagationHash = require('./propagation-hash')
|
|
107
111
|
propagationHash.configure(config)
|
|
@@ -11,6 +11,7 @@ const log = require('../log')
|
|
|
11
11
|
const { getValueFromEnvSources } = require('../config/helper')
|
|
12
12
|
|
|
13
13
|
const { NODE_MAJOR } = require('../../../../version')
|
|
14
|
+
const processTags = require('../process-tags')
|
|
14
15
|
// TODO: This environment variable may not be changed, since the agent expects a flush every ten seconds.
|
|
15
16
|
// It is only a variable for testing. Think about alternatives.
|
|
16
17
|
const DD_RUNTIME_METRICS_FLUSH_INTERVAL = getValueFromEnvSources('DD_RUNTIME_METRICS_FLUSH_INTERVAL') ?? '10000'
|
|
@@ -38,6 +39,12 @@ module.exports = {
|
|
|
38
39
|
this.stop()
|
|
39
40
|
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
40
41
|
|
|
42
|
+
if (config.propagateProcessTags?.enabled) {
|
|
43
|
+
for (const tag of processTags.tagsArray) {
|
|
44
|
+
clientConfig.tags.push(tag)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
const trackEventLoop = config.runtimeMetrics.eventLoop !== false
|
|
42
49
|
const trackGc = config.runtimeMetrics.gc !== false
|
|
43
50
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { SAMPLING_MECHANISM_APPSEC } = require('../constants')
|
|
3
|
+
const { SAMPLING_MECHANISM_APPSEC, SAMPLING_MECHANISM_AI_GUARD } = require('../constants')
|
|
4
4
|
const RateLimiter = require('../rate_limiter')
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -26,6 +26,7 @@ const PRODUCTS = {
|
|
|
26
26
|
DSM: { id: 1 << 2 },
|
|
27
27
|
DJM: { id: 1 << 3 },
|
|
28
28
|
DBM: { id: 1 << 4 },
|
|
29
|
+
AI_GUARD: { id: 1 << 5, mechanism: SAMPLING_MECHANISM_AI_GUARD },
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
module.exports = {
|