dd-trace 3.27.0 → 3.29.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/README.md +2 -2
- package/package.json +4 -4
- package/packages/datadog-core/src/storage/async_resource.js +4 -0
- package/packages/datadog-instrumentations/src/couchbase.js +4 -4
- package/packages/datadog-instrumentations/src/cucumber.js +5 -2
- package/packages/datadog-instrumentations/src/grpc/client.js +44 -42
- package/packages/datadog-instrumentations/src/grpc/server.js +69 -60
- package/packages/datadog-instrumentations/src/http2/client.js +25 -26
- package/packages/datadog-instrumentations/src/jest.js +9 -5
- package/packages/datadog-instrumentations/src/mocha.js +5 -3
- package/packages/datadog-plugin-cypress/src/plugin.js +4 -2
- package/packages/datadog-plugin-graphql/src/execute.js +6 -4
- package/packages/datadog-plugin-grpc/src/client.js +29 -11
- package/packages/datadog-plugin-grpc/src/server.js +22 -6
- package/packages/datadog-plugin-http2/src/client.js +46 -29
- package/packages/datadog-plugin-jest/src/index.js +8 -3
- package/packages/datadog-plugin-openai/src/index.js +39 -16
- package/packages/datadog-plugin-openai/src/services.js +13 -9
- package/packages/datadog-plugin-router/src/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +7 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +45 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/index.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +66 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +19 -15
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +5 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +27 -8
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +18 -19
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +19 -0
- package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +205 -0
- package/packages/dd-trace/src/appsec/iast/index.js +11 -7
- package/packages/dd-trace/src/appsec/iast/tags.js +2 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +6 -6
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +7 -5
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +23 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +49 -17
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +33 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +23 -16
- package/packages/dd-trace/src/appsec/iast/taint-tracking/{origin-types.js → source-types.js} +1 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +76 -37
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +101 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +45 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/{logs.js → log/index.js} +5 -5
- package/packages/dd-trace/src/appsec/iast/telemetry/{log_collector.js → log/log-collector.js} +1 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +76 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +53 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/verbosity.js +42 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +5 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- package/packages/dd-trace/src/config.js +47 -12
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/external-logger/src/index.js +9 -1
- package/packages/dd-trace/src/external-logger/test/index.spec.js +1 -1
- package/packages/dd-trace/src/format.js +1 -1
- package/packages/dd-trace/src/lambda/handler.js +8 -1
- package/packages/dd-trace/src/opentelemetry/span.js +3 -1
- package/packages/dd-trace/src/opentracing/span_context.js +2 -1
- package/packages/dd-trace/src/opentracing/tracer.js +1 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/outbound.js +29 -12
- package/packages/dd-trace/src/plugins/plugin.js +28 -0
- package/packages/dd-trace/src/plugins/tracing.js +33 -16
- package/packages/dd-trace/src/plugins/util/ci.js +3 -2
- package/packages/dd-trace/src/plugins/util/test.js +55 -11
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -22
- package/packages/dd-trace/src/plugins/util/web.js +1 -0
- package/packages/dd-trace/src/profiling/config.js +8 -8
- package/packages/dd-trace/src/profiling/exporters/agent.js +4 -1
- package/packages/dd-trace/src/profiling/index.js +0 -2
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +162 -10
- package/packages/dd-trace/src/service-naming/index.js +2 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/graphql.js +12 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/graphql.js +12 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
- package/packages/dd-trace/src/span_processor.js +0 -4
- package/packages/dd-trace/src/span_sampler.js +1 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +24 -12
- package/packages/dd-trace/src/telemetry/metrics.js +11 -1
- package/packages/diagnostics_channel/src/index.js +64 -0
- package/scripts/install_plugin_modules.js +1 -0
- package/packages/dd-trace/src/profiling/profilers/cpu.js +0 -126
- package/scripts/version.js +0 -66
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const fs = require('fs')
|
|
3
|
+
const { URL } = require('url')
|
|
4
|
+
const log = require('../../log')
|
|
3
5
|
|
|
4
6
|
const istanbul = require('istanbul-lib-coverage')
|
|
5
7
|
const ignore = require('ignore')
|
|
6
8
|
|
|
7
9
|
const { getGitMetadata } = require('./git')
|
|
8
|
-
const { getUserProviderGitMetadata } = require('./user-provided-git')
|
|
10
|
+
const { getUserProviderGitMetadata, validateGitRepositoryUrl, validateGitCommitSha } = require('./user-provided-git')
|
|
9
11
|
const { getCIMetadata } = require('./ci')
|
|
10
12
|
const { getRuntimeAndOSMetadata } = require('./env')
|
|
11
13
|
const {
|
|
@@ -16,7 +18,8 @@ const {
|
|
|
16
18
|
GIT_COMMIT_AUTHOR_EMAIL,
|
|
17
19
|
GIT_COMMIT_AUTHOR_NAME,
|
|
18
20
|
GIT_COMMIT_MESSAGE,
|
|
19
|
-
CI_WORKSPACE_PATH
|
|
21
|
+
CI_WORKSPACE_PATH,
|
|
22
|
+
CI_PIPELINE_URL
|
|
20
23
|
} = require('./tags')
|
|
21
24
|
const id = require('../../id')
|
|
22
25
|
|
|
@@ -104,7 +107,8 @@ module.exports = {
|
|
|
104
107
|
mergeCoverage,
|
|
105
108
|
fromCoverageMapToCoverage,
|
|
106
109
|
getTestLineStart,
|
|
107
|
-
getCallSites
|
|
110
|
+
getCallSites,
|
|
111
|
+
removeInvalidMetadata
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
// Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
|
|
@@ -116,6 +120,39 @@ function getPkgManager () {
|
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
|
|
123
|
+
function validateUrl (url) {
|
|
124
|
+
try {
|
|
125
|
+
const urlObject = new URL(url)
|
|
126
|
+
return (urlObject.protocol === 'https:' || urlObject.protocol === 'http:')
|
|
127
|
+
} catch (e) {
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function removeInvalidMetadata (metadata) {
|
|
133
|
+
return Object.keys(metadata).reduce((filteredTags, tag) => {
|
|
134
|
+
if (tag === GIT_REPOSITORY_URL) {
|
|
135
|
+
if (!validateGitRepositoryUrl(metadata[GIT_REPOSITORY_URL])) {
|
|
136
|
+
log.error('DD_GIT_REPOSITORY_URL must be a valid URL')
|
|
137
|
+
return filteredTags
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (tag === GIT_COMMIT_SHA) {
|
|
141
|
+
if (!validateGitCommitSha(metadata[GIT_COMMIT_SHA])) {
|
|
142
|
+
log.error('DD_GIT_COMMIT_SHA must be a full-length git SHA')
|
|
143
|
+
return filteredTags
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (tag === CI_PIPELINE_URL) {
|
|
147
|
+
if (!validateUrl(metadata[CI_PIPELINE_URL])) {
|
|
148
|
+
return filteredTags
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
filteredTags[tag] = metadata[tag]
|
|
152
|
+
return filteredTags
|
|
153
|
+
}, {})
|
|
154
|
+
}
|
|
155
|
+
|
|
119
156
|
function getTestEnvironmentMetadata (testFramework, config) {
|
|
120
157
|
// TODO: eventually these will come from the tracer (generally available)
|
|
121
158
|
const ciMetadata = getCIMetadata()
|
|
@@ -155,7 +192,7 @@ function getTestEnvironmentMetadata (testFramework, config) {
|
|
|
155
192
|
if (config && config.service) {
|
|
156
193
|
metadata['service.name'] = config.service
|
|
157
194
|
}
|
|
158
|
-
return metadata
|
|
195
|
+
return removeInvalidMetadata(metadata)
|
|
159
196
|
}
|
|
160
197
|
|
|
161
198
|
function getTestParametersString (parametersByTestName, testName) {
|
|
@@ -173,6 +210,13 @@ function getTestParametersString (parametersByTestName, testName) {
|
|
|
173
210
|
}
|
|
174
211
|
}
|
|
175
212
|
|
|
213
|
+
function getTestTypeFromFramework (testFramework) {
|
|
214
|
+
if (testFramework === 'playwright' || testFramework === 'cypress') {
|
|
215
|
+
return 'browser'
|
|
216
|
+
}
|
|
217
|
+
return 'test'
|
|
218
|
+
}
|
|
219
|
+
|
|
176
220
|
function finishAllTraceSpans (span) {
|
|
177
221
|
span.context()._trace.started.forEach(traceSpan => {
|
|
178
222
|
if (traceSpan !== span) {
|
|
@@ -188,10 +232,10 @@ function getTestParentSpan (tracer) {
|
|
|
188
232
|
})
|
|
189
233
|
}
|
|
190
234
|
|
|
191
|
-
function getTestCommonTags (name, suite, version) {
|
|
235
|
+
function getTestCommonTags (name, suite, version, testFramework) {
|
|
192
236
|
return {
|
|
193
237
|
[SPAN_TYPE]: 'test',
|
|
194
|
-
[TEST_TYPE]:
|
|
238
|
+
[TEST_TYPE]: getTestTypeFromFramework(testFramework),
|
|
195
239
|
[SAMPLING_RULE_DECISION]: 1,
|
|
196
240
|
[SAMPLING_PRIORITY]: AUTO_KEEP,
|
|
197
241
|
[TEST_NAME]: name,
|
|
@@ -269,12 +313,12 @@ function getCodeOwnersForFilename (filename, entries) {
|
|
|
269
313
|
return null
|
|
270
314
|
}
|
|
271
315
|
|
|
272
|
-
function getTestLevelCommonTags (command, testFrameworkVersion) {
|
|
316
|
+
function getTestLevelCommonTags (command, testFrameworkVersion, testFramework) {
|
|
273
317
|
return {
|
|
274
318
|
[TEST_FRAMEWORK_VERSION]: testFrameworkVersion,
|
|
275
319
|
[LIBRARY_VERSION]: ddTraceVersion,
|
|
276
320
|
[TEST_COMMAND]: command,
|
|
277
|
-
[TEST_TYPE]:
|
|
321
|
+
[TEST_TYPE]: getTestTypeFromFramework(testFramework)
|
|
278
322
|
}
|
|
279
323
|
}
|
|
280
324
|
|
|
@@ -284,7 +328,7 @@ function getTestSessionCommonTags (command, testFrameworkVersion, testFramework)
|
|
|
284
328
|
[RESOURCE_NAME]: `test_session.${command}`,
|
|
285
329
|
[TEST_MODULE]: testFramework,
|
|
286
330
|
[TEST_TOOLCHAIN]: getPkgManager(),
|
|
287
|
-
...getTestLevelCommonTags(command, testFrameworkVersion)
|
|
331
|
+
...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
|
|
288
332
|
}
|
|
289
333
|
}
|
|
290
334
|
|
|
@@ -293,7 +337,7 @@ function getTestModuleCommonTags (command, testFrameworkVersion, testFramework)
|
|
|
293
337
|
[SPAN_TYPE]: 'test_module_end',
|
|
294
338
|
[RESOURCE_NAME]: `test_module.${command}`,
|
|
295
339
|
[TEST_MODULE]: testFramework,
|
|
296
|
-
...getTestLevelCommonTags(command, testFrameworkVersion)
|
|
340
|
+
...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
|
|
297
341
|
}
|
|
298
342
|
}
|
|
299
343
|
|
|
@@ -303,7 +347,7 @@ function getTestSuiteCommonTags (command, testFrameworkVersion, testSuite, testF
|
|
|
303
347
|
[RESOURCE_NAME]: `test_suite.${testSuite}`,
|
|
304
348
|
[TEST_MODULE]: testFramework,
|
|
305
349
|
[TEST_SUITE]: testSuite,
|
|
306
|
-
...getTestLevelCommonTags(command, testFrameworkVersion)
|
|
350
|
+
...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
|
|
307
351
|
}
|
|
308
352
|
}
|
|
309
353
|
|
|
@@ -13,7 +13,6 @@ const {
|
|
|
13
13
|
} = require('./tags')
|
|
14
14
|
|
|
15
15
|
const { normalizeRef } = require('./ci')
|
|
16
|
-
const log = require('../../log')
|
|
17
16
|
const { URL } = require('url')
|
|
18
17
|
|
|
19
18
|
function removeEmptyValues (tags) {
|
|
@@ -53,25 +52,6 @@ function validateGitCommitSha (gitCommitSha) {
|
|
|
53
52
|
return isValidSha1 || isValidSha256
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
function removeInvalidGitMetadata (metadata) {
|
|
57
|
-
return Object.keys(metadata).reduce((filteredTags, tag) => {
|
|
58
|
-
if (tag === GIT_REPOSITORY_URL) {
|
|
59
|
-
if (!validateGitRepositoryUrl(metadata[GIT_REPOSITORY_URL])) {
|
|
60
|
-
log.error('DD_GIT_REPOSITORY_URL must be a valid URL')
|
|
61
|
-
return filteredTags
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (tag === GIT_COMMIT_SHA) {
|
|
65
|
-
if (!validateGitCommitSha(metadata[GIT_COMMIT_SHA])) {
|
|
66
|
-
log.error('DD_GIT_COMMIT_SHA must be a full-length git SHA')
|
|
67
|
-
return filteredTags
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
filteredTags[tag] = metadata[tag]
|
|
71
|
-
return filteredTags
|
|
72
|
-
}, {})
|
|
73
|
-
}
|
|
74
|
-
|
|
75
55
|
function getUserProviderGitMetadata () {
|
|
76
56
|
const {
|
|
77
57
|
DD_GIT_COMMIT_SHA,
|
|
@@ -95,7 +75,7 @@ function getUserProviderGitMetadata () {
|
|
|
95
75
|
tag = normalizeRef(DD_GIT_BRANCH)
|
|
96
76
|
}
|
|
97
77
|
|
|
98
|
-
|
|
78
|
+
return removeEmptyValues({
|
|
99
79
|
[GIT_COMMIT_SHA]: DD_GIT_COMMIT_SHA,
|
|
100
80
|
[GIT_BRANCH]: branch,
|
|
101
81
|
[GIT_REPOSITORY_URL]: filterSensitiveInfoFromRepository(DD_GIT_REPOSITORY_URL),
|
|
@@ -108,7 +88,6 @@ function getUserProviderGitMetadata () {
|
|
|
108
88
|
[GIT_COMMIT_AUTHOR_EMAIL]: DD_GIT_COMMIT_AUTHOR_EMAIL,
|
|
109
89
|
[GIT_COMMIT_AUTHOR_DATE]: DD_GIT_COMMIT_AUTHOR_DATE
|
|
110
90
|
})
|
|
111
|
-
return removeInvalidGitMetadata(metadata)
|
|
112
91
|
}
|
|
113
92
|
|
|
114
93
|
module.exports = { getUserProviderGitMetadata, validateGitRepositoryUrl, validateGitCommitSha }
|
|
@@ -7,7 +7,6 @@ const { URL, format, pathToFileURL } = require('url')
|
|
|
7
7
|
const { AgentExporter } = require('./exporters/agent')
|
|
8
8
|
const { FileExporter } = require('./exporters/file')
|
|
9
9
|
const { ConsoleLogger } = require('./loggers/console')
|
|
10
|
-
const CpuProfiler = require('./profilers/cpu')
|
|
11
10
|
const WallProfiler = require('./profilers/wall')
|
|
12
11
|
const SpaceProfiler = require('./profilers/space')
|
|
13
12
|
const { oomExportStrategies, snapshotKinds } = require('./constants')
|
|
@@ -19,7 +18,6 @@ class Config {
|
|
|
19
18
|
const {
|
|
20
19
|
DD_PROFILING_ENABLED,
|
|
21
20
|
DD_PROFILING_PROFILERS,
|
|
22
|
-
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
23
21
|
DD_ENV,
|
|
24
22
|
DD_TAGS,
|
|
25
23
|
DD_SERVICE,
|
|
@@ -37,7 +35,9 @@ class Config {
|
|
|
37
35
|
DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
|
|
38
36
|
DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
|
|
39
37
|
DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
|
|
40
|
-
DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES
|
|
38
|
+
DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES,
|
|
39
|
+
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED,
|
|
40
|
+
DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED
|
|
41
41
|
} = process.env
|
|
42
42
|
|
|
43
43
|
const enabled = isTrue(coalesce(options.enabled, DD_PROFILING_ENABLED, true))
|
|
@@ -52,8 +52,8 @@ class Config {
|
|
|
52
52
|
Number(DD_PROFILING_UPLOAD_TIMEOUT), 60 * 1000)
|
|
53
53
|
const sourceMap = coalesce(options.sourceMap,
|
|
54
54
|
DD_PROFILING_SOURCE_MAP, true)
|
|
55
|
-
const
|
|
56
|
-
|
|
55
|
+
const endpointCollectionEnabled = coalesce(options.endpointCollection,
|
|
56
|
+
DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED, false)
|
|
57
57
|
const pprofPrefix = coalesce(options.pprofPrefix,
|
|
58
58
|
DD_PROFILING_PPROF_PREFIX, '')
|
|
59
59
|
|
|
@@ -74,7 +74,7 @@ class Config {
|
|
|
74
74
|
this.uploadTimeout = uploadTimeout
|
|
75
75
|
this.sourceMap = sourceMap
|
|
76
76
|
this.debugSourceMaps = isTrue(coalesce(options.debugSourceMaps, DD_PROFILING_DEBUG_SOURCE_MAPS, false))
|
|
77
|
-
this.
|
|
77
|
+
this.endpointCollectionEnabled = endpointCollectionEnabled
|
|
78
78
|
this.pprofPrefix = pprofPrefix
|
|
79
79
|
|
|
80
80
|
const hostname = coalesce(options.hostname, DD_AGENT_HOST) || 'localhost'
|
|
@@ -111,6 +111,8 @@ class Config {
|
|
|
111
111
|
const profilers = options.profilers
|
|
112
112
|
? options.profilers
|
|
113
113
|
: getProfilers({ DD_PROFILING_HEAP_ENABLED, DD_PROFILING_WALLTIME_ENABLED, DD_PROFILING_PROFILERS })
|
|
114
|
+
this.codeHotspotsEnabled = isTrue(coalesce(options.codeHotspotsEnabled,
|
|
115
|
+
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED, false))
|
|
114
116
|
|
|
115
117
|
this.profilers = ensureProfilers(profilers, this)
|
|
116
118
|
}
|
|
@@ -202,8 +204,6 @@ function getProfiler (name, options) {
|
|
|
202
204
|
return new WallProfiler(options)
|
|
203
205
|
case 'space':
|
|
204
206
|
return new SpaceProfiler(options)
|
|
205
|
-
case 'cpu-experimental':
|
|
206
|
-
return new CpuProfiler(options)
|
|
207
207
|
default:
|
|
208
208
|
options.logger.error(`Unknown profiler "${name}"`)
|
|
209
209
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const retry = require('retry')
|
|
4
|
-
const { request } = require('http')
|
|
4
|
+
const { request: httpRequest } = require('http')
|
|
5
|
+
const { request: httpsRequest } = require('https')
|
|
5
6
|
|
|
6
7
|
// TODO: avoid using dd-trace internals. Make this a separate module?
|
|
7
8
|
const docker = require('../../exporters/common/docker')
|
|
@@ -12,6 +13,8 @@ const version = require('../../../../../package.json').version
|
|
|
12
13
|
const containerId = docker.id()
|
|
13
14
|
|
|
14
15
|
function sendRequest (options, form, callback) {
|
|
16
|
+
const request = options.protocol === 'https:' ? httpsRequest : httpRequest
|
|
17
|
+
|
|
15
18
|
const store = storage.getStore()
|
|
16
19
|
storage.enterWith({ noop: true })
|
|
17
20
|
const req = request(options, res => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { Profiler, ServerlessProfiler } = require('./profiler')
|
|
4
|
-
const CpuProfiler = require('./profilers/cpu')
|
|
5
4
|
const WallProfiler = require('./profilers/wall')
|
|
6
5
|
const SpaceProfiler = require('./profilers/space')
|
|
7
6
|
const { AgentExporter } = require('./exporters/agent')
|
|
@@ -14,7 +13,6 @@ module.exports = {
|
|
|
14
13
|
profiler,
|
|
15
14
|
AgentExporter,
|
|
16
15
|
FileExporter,
|
|
17
|
-
CpuProfiler,
|
|
18
16
|
WallProfiler,
|
|
19
17
|
SpaceProfiler,
|
|
20
18
|
ConsoleLogger
|
|
@@ -1,16 +1,111 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { storage } = require('../../../../datadog-core')
|
|
4
|
+
|
|
5
|
+
const dc = require('../../../../diagnostics_channel')
|
|
6
|
+
|
|
7
|
+
const beforeCh = dc.channel('dd-trace:storage:before')
|
|
8
|
+
const enterCh = dc.channel('dd-trace:storage:enter')
|
|
9
|
+
|
|
10
|
+
let kSampleCount
|
|
11
|
+
|
|
12
|
+
function getActiveSpan () {
|
|
13
|
+
const store = storage.getStore()
|
|
14
|
+
return store && store.span
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getStartedSpans (context) {
|
|
18
|
+
return context._trace.started
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function generateLabels ({ spanId, rootSpanId, webTags, endpoint }) {
|
|
22
|
+
const labels = {}
|
|
23
|
+
if (spanId) {
|
|
24
|
+
labels['span id'] = spanId
|
|
25
|
+
}
|
|
26
|
+
if (rootSpanId) {
|
|
27
|
+
labels['local root span id'] = rootSpanId
|
|
28
|
+
}
|
|
29
|
+
if (webTags && Object.keys(webTags).length !== 0) {
|
|
30
|
+
labels['trace endpoint'] = endpointNameFromTags(webTags)
|
|
31
|
+
} else if (endpoint) {
|
|
32
|
+
// fallback to endpoint computed when sample was taken
|
|
33
|
+
labels['trace endpoint'] = endpoint
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return labels
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getSpanContextTags (span) {
|
|
40
|
+
return span.context()._tags
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isWebServerSpan (tags) {
|
|
44
|
+
return tags['span.type'] === 'web'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function endpointNameFromTags (tags) {
|
|
48
|
+
return tags['resource.name'] || [
|
|
49
|
+
tags['http.method'],
|
|
50
|
+
tags['http.route']
|
|
51
|
+
].filter(v => v).join(' ')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function updateContext (context, span, startedSpans, endpointCollectionEnabled) {
|
|
55
|
+
context.spanId = span.context().toSpanId()
|
|
56
|
+
const rootSpan = startedSpans[0]
|
|
57
|
+
if (rootSpan) {
|
|
58
|
+
context.rootSpanId = rootSpan.context().toSpanId()
|
|
59
|
+
if (endpointCollectionEnabled) {
|
|
60
|
+
// Find the first webspan starting from the end:
|
|
61
|
+
// There might be several webspans, for example with next.js, http plugin creates a first span
|
|
62
|
+
// and then next.js plugin creates a child span, and this child span haves the correct endpoint information.
|
|
63
|
+
for (let i = startedSpans.length - 1; i >= 0; i--) {
|
|
64
|
+
const tags = getSpanContextTags(startedSpans[i])
|
|
65
|
+
if (isWebServerSpan(tags)) {
|
|
66
|
+
context.webTags = tags
|
|
67
|
+
// endpoint may not be determined yet, but keep it as fallback
|
|
68
|
+
// if tags are not available anymore during serialization
|
|
69
|
+
context.endpoint = endpointNameFromTags(tags)
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
3
77
|
class NativeWallProfiler {
|
|
4
78
|
constructor (options = {}) {
|
|
5
79
|
this.type = 'wall'
|
|
6
|
-
this.
|
|
80
|
+
this._samplingIntervalMicros = options.samplingInterval || 1e6 / 99 // 99hz
|
|
81
|
+
this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
|
|
82
|
+
this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
|
|
83
|
+
this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
|
|
7
84
|
this._mapper = undefined
|
|
8
85
|
this._pprof = undefined
|
|
86
|
+
|
|
87
|
+
// Bind to this so the same value can be used to unsubscribe later
|
|
88
|
+
this._enter = this._enter.bind(this)
|
|
89
|
+
this._logger = options.logger
|
|
90
|
+
this._started = false
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
codeHotspotsEnabled () {
|
|
94
|
+
return this._codeHotspotsEnabled
|
|
9
95
|
}
|
|
10
96
|
|
|
11
97
|
start ({ mapper } = {}) {
|
|
98
|
+
if (this._started) return
|
|
99
|
+
|
|
100
|
+
if (this._codeHotspotsEnabled && !this._emittedFFMessage && this._logger) {
|
|
101
|
+
this._logger.debug(
|
|
102
|
+
`Wall profiler: Enable config_trace_show_breakdown_profiling_for_node feature flag to see code hotspots.`)
|
|
103
|
+
this._emittedFFMessage = true
|
|
104
|
+
}
|
|
105
|
+
|
|
12
106
|
this._mapper = mapper
|
|
13
107
|
this._pprof = require('@datadog/pprof')
|
|
108
|
+
kSampleCount = this._pprof.time.constants.kSampleCount
|
|
14
109
|
|
|
15
110
|
// pprof otherwise crashes in worker threads
|
|
16
111
|
if (!process._startProfilerIdleNotifier) {
|
|
@@ -20,11 +115,65 @@ class NativeWallProfiler {
|
|
|
20
115
|
process._stopProfilerIdleNotifier = () => {}
|
|
21
116
|
}
|
|
22
117
|
|
|
23
|
-
this.
|
|
118
|
+
this._pprof.time.start({
|
|
119
|
+
intervalMicros: this._samplingIntervalMicros,
|
|
120
|
+
durationMillis: this._flushIntervalMillis,
|
|
121
|
+
sourceMapper: this._mapper,
|
|
122
|
+
withContexts: this._codeHotspotsEnabled,
|
|
123
|
+
lineNumbers: false
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
if (this._codeHotspotsEnabled) {
|
|
127
|
+
this._profilerState = this._pprof.time.getState()
|
|
128
|
+
this._currentContext = {}
|
|
129
|
+
this._pprof.time.setContext(this._currentContext)
|
|
130
|
+
this._lastSpan = undefined
|
|
131
|
+
this._lastStartedSpans = undefined
|
|
132
|
+
this._lastSampleCount = 0
|
|
133
|
+
|
|
134
|
+
beforeCh.subscribe(this._enter)
|
|
135
|
+
enterCh.subscribe(this._enter)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this._started = true
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
_enter () {
|
|
142
|
+
if (!this._started) return
|
|
143
|
+
|
|
144
|
+
const sampleCount = this._profilerState[kSampleCount]
|
|
145
|
+
if (sampleCount !== this._lastSampleCount) {
|
|
146
|
+
this._lastSampleCount = sampleCount
|
|
147
|
+
const context = this._currentContext
|
|
148
|
+
this._currentContext = {}
|
|
149
|
+
this._pprof.time.setContext(this._currentContext)
|
|
150
|
+
|
|
151
|
+
if (this._lastSpan) {
|
|
152
|
+
updateContext(context, this._lastSpan, this._lastStartedSpans, this._endpointCollectionEnabled)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const span = getActiveSpan()
|
|
157
|
+
if (span) {
|
|
158
|
+
this._lastSpan = span
|
|
159
|
+
this._lastStartedSpans = getStartedSpans(span.context())
|
|
160
|
+
} else {
|
|
161
|
+
this._lastStartedSpans = undefined
|
|
162
|
+
this._lastSpan = undefined
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
_stop (restart) {
|
|
167
|
+
if (!this._started) return
|
|
168
|
+
if (this._codeHotspotsEnabled) {
|
|
169
|
+
// update last sample context if needed
|
|
170
|
+
this._enter()
|
|
171
|
+
this._lastSampleCount = 0
|
|
172
|
+
}
|
|
173
|
+
return this._pprof.time.stop(restart, this._codeHotspotsEnabled ? generateLabels : undefined)
|
|
24
174
|
}
|
|
25
175
|
|
|
26
176
|
profile () {
|
|
27
|
-
if (!this._stop) return
|
|
28
177
|
return this._stop(true)
|
|
29
178
|
}
|
|
30
179
|
|
|
@@ -33,14 +182,17 @@ class NativeWallProfiler {
|
|
|
33
182
|
}
|
|
34
183
|
|
|
35
184
|
stop () {
|
|
36
|
-
if (!this.
|
|
37
|
-
|
|
38
|
-
this._stop
|
|
39
|
-
|
|
185
|
+
if (!this._started) return
|
|
186
|
+
|
|
187
|
+
const profile = this._stop(false)
|
|
188
|
+
if (this._codeHotspotsEnabled) {
|
|
189
|
+
beforeCh.unsubscribe(this._enter)
|
|
190
|
+
enterCh.subscribe(this._enter)
|
|
191
|
+
this._profilerState = undefined
|
|
192
|
+
}
|
|
40
193
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this._mapper, false)
|
|
194
|
+
this._started = false
|
|
195
|
+
return profile
|
|
44
196
|
}
|
|
45
197
|
}
|
|
46
198
|
|
|
@@ -3,7 +3,7 @@ const { schemaDefinitions } = require('./schemas')
|
|
|
3
3
|
class SchemaManager {
|
|
4
4
|
constructor () {
|
|
5
5
|
this.schemas = schemaDefinitions
|
|
6
|
-
this.config = { spanAttributeSchema: 'v0',
|
|
6
|
+
this.config = { spanAttributeSchema: 'v0', spanRemoveIntegrationFromService: false }
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
get schema () {
|
|
@@ -15,7 +15,7 @@ class SchemaManager {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
get shouldUseConsistentServiceNaming () {
|
|
18
|
-
return this.config.
|
|
18
|
+
return this.config.spanRemoveIntegrationFromService && this.version === 'v0'
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
opName (type, kind, plugin, ...opNameArgs) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const SchemaDefinition = require('../definition')
|
|
2
2
|
const messaging = require('./messaging')
|
|
3
3
|
const storage = require('./storage')
|
|
4
|
+
const graphql = require('./graphql')
|
|
4
5
|
const web = require('./web')
|
|
5
6
|
|
|
6
|
-
module.exports = new SchemaDefinition({ messaging, storage, web })
|
|
7
|
+
module.exports = new SchemaDefinition({ messaging, storage, web, graphql })
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const SchemaDefinition = require('../definition')
|
|
2
2
|
const messaging = require('./messaging')
|
|
3
3
|
const storage = require('./storage')
|
|
4
|
+
const graphql = require('./graphql')
|
|
4
5
|
const web = require('./web')
|
|
5
6
|
|
|
6
|
-
module.exports = new SchemaDefinition({ messaging, storage, web })
|
|
7
|
+
module.exports = new SchemaDefinition({ messaging, storage, web, graphql })
|
|
@@ -7,8 +7,10 @@ const { sendData } = require('./send-data')
|
|
|
7
7
|
const dc = require('../../../diagnostics_channel')
|
|
8
8
|
const { fileURLToPath } = require('url')
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const savedDependenciesToSend = new Set()
|
|
11
|
+
const detectedDependencyKeys = new Set()
|
|
12
|
+
const detectedDependencyVersions = new Set()
|
|
13
|
+
|
|
12
14
|
const FILE_URI_START = `file://`
|
|
13
15
|
const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
|
|
14
16
|
|
|
@@ -18,14 +20,14 @@ function waitAndSend (config, application, host) {
|
|
|
18
20
|
if (!immediate) {
|
|
19
21
|
immediate = setImmediate(() => {
|
|
20
22
|
immediate = null
|
|
21
|
-
if (
|
|
22
|
-
const dependencies = Array.from(
|
|
23
|
-
|
|
23
|
+
if (savedDependenciesToSend.size > 0) {
|
|
24
|
+
const dependencies = Array.from(savedDependenciesToSend.values()).splice(0, 1000).map(pair => {
|
|
25
|
+
savedDependenciesToSend.delete(pair)
|
|
24
26
|
const [name, version] = pair.split(' ')
|
|
25
27
|
return { name, version }
|
|
26
28
|
})
|
|
27
29
|
sendData(config, application, host, 'app-dependencies-loaded', { dependencies })
|
|
28
|
-
if (
|
|
30
|
+
if (savedDependenciesToSend.size > 0) {
|
|
29
31
|
waitAndSend(config, application, host)
|
|
30
32
|
}
|
|
31
33
|
}
|
|
@@ -46,15 +48,24 @@ function onModuleLoad (data) {
|
|
|
46
48
|
}
|
|
47
49
|
const parseResult = filename && parse(filename)
|
|
48
50
|
const request = data.request || (parseResult && parseResult.name)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
const dependencyKey = parseResult && parseResult.basedir ? parseResult.basedir : request
|
|
52
|
+
|
|
53
|
+
if (filename && request && isDependency(filename, request) && !detectedDependencyKeys.has(dependencyKey)) {
|
|
54
|
+
detectedDependencyKeys.add(dependencyKey)
|
|
55
|
+
|
|
51
56
|
if (parseResult) {
|
|
52
57
|
const { name, basedir } = parseResult
|
|
53
58
|
if (basedir) {
|
|
54
59
|
try {
|
|
55
60
|
const { version } = requirePackageJson(basedir, module)
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
const dependencyAndVersion = `${name} ${version}`
|
|
62
|
+
|
|
63
|
+
if (!detectedDependencyVersions.has(dependencyAndVersion)) {
|
|
64
|
+
savedDependenciesToSend.add(dependencyAndVersion)
|
|
65
|
+
detectedDependencyVersions.add(dependencyAndVersion)
|
|
66
|
+
|
|
67
|
+
waitAndSend(config, application, host)
|
|
68
|
+
}
|
|
58
69
|
} catch (e) {
|
|
59
70
|
// can not read the package.json, do nothing
|
|
60
71
|
}
|
|
@@ -88,8 +99,9 @@ function stop () {
|
|
|
88
99
|
config = null
|
|
89
100
|
application = null
|
|
90
101
|
host = null
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
detectedDependencyKeys.clear()
|
|
103
|
+
savedDependenciesToSend.clear()
|
|
104
|
+
detectedDependencyVersions.clear()
|
|
93
105
|
if (moduleLoadStartChannel.hasSubscribers) {
|
|
94
106
|
moduleLoadStartChannel.unsubscribe(onModuleLoad)
|
|
95
107
|
}
|