dd-trace 2.41.0 → 2.42.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 +3 -3
- package/packages/datadog-core/src/storage/async_resource.js +4 -0
- package/packages/datadog-instrumentations/src/jest.js +6 -4
- package/packages/datadog-plugin-graphql/src/execute.js +6 -4
- 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/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -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/missing-header-analyzer.js +66 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +25 -8
- package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +19 -0
- package/packages/dd-trace/src/appsec/iast/index.js +5 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +4 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +17 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +1 -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 +26 -10
- 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/plugins/util/ci.js +2 -1
- package/packages/dd-trace/src/plugins/util/web.js +1 -0
- package/packages/dd-trace/src/profiling/config.js +8 -5
- package/packages/dd-trace/src/profiling/exporters/agent.js +4 -1
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +144 -4
- 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/scripts/version.js +0 -66
|
@@ -54,7 +54,11 @@ class VulnerabilityFormatter {
|
|
|
54
54
|
|
|
55
55
|
formatEvidence (type, evidence, sourcesIndexes, sources) {
|
|
56
56
|
if (!evidence.ranges) {
|
|
57
|
-
|
|
57
|
+
if (typeof evidence.value === 'undefined') {
|
|
58
|
+
return undefined
|
|
59
|
+
} else {
|
|
60
|
+
return { value: evidence.value }
|
|
61
|
+
}
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
return this._redactVulnearbilities
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
COMMAND_INJECTION: 'COMMAND_INJECTION',
|
|
3
|
+
HSTS_HEADER_MISSING: 'HSTS_HEADER_MISSING',
|
|
3
4
|
INSECURE_COOKIE: 'INSECURE_COOKIE',
|
|
4
5
|
LDAP_INJECTION: 'LDAP_INJECTION',
|
|
5
6
|
NO_HTTPONLY_COOKIE: 'NO_HTTPONLY_COOKIE',
|
|
@@ -9,5 +10,6 @@ module.exports = {
|
|
|
9
10
|
SSRF: 'SSRF',
|
|
10
11
|
UNVALIDATED_REDIRECT: 'UNVALIDATED_REDIRECT',
|
|
11
12
|
WEAK_CIPHER: 'WEAK_CIPHER',
|
|
12
|
-
WEAK_HASH: 'WEAK_HASH'
|
|
13
|
+
WEAK_HASH: 'WEAK_HASH',
|
|
14
|
+
XCONTENTTYPE_HEADER_MISSING: 'XCONTENTTYPE_HEADER_MISSING'
|
|
13
15
|
}
|
|
@@ -28,7 +28,7 @@ function addVulnerability (iastContext, vulnerability) {
|
|
|
28
28
|
|
|
29
29
|
function isValidVulnerability (vulnerability) {
|
|
30
30
|
return vulnerability && vulnerability.type &&
|
|
31
|
-
vulnerability.evidence &&
|
|
31
|
+
vulnerability.evidence &&
|
|
32
32
|
vulnerability.location && vulnerability.location.spanId
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -278,7 +278,7 @@ class Config {
|
|
|
278
278
|
process.env.DD_TRACE_EXPERIMENTAL_B3_ENABLED,
|
|
279
279
|
false
|
|
280
280
|
)
|
|
281
|
-
const defaultPropagationStyle = ['
|
|
281
|
+
const defaultPropagationStyle = ['datadog', 'tracecontext']
|
|
282
282
|
if (isTrue(DD_TRACE_B3_ENABLED)) {
|
|
283
283
|
defaultPropagationStyle.push('b3')
|
|
284
284
|
defaultPropagationStyle.push('b3 single header')
|
|
@@ -318,7 +318,10 @@ class Config {
|
|
|
318
318
|
false
|
|
319
319
|
)
|
|
320
320
|
const DD_TRACE_SPAN_ATTRIBUTE_SCHEMA = validateNamingVersion(
|
|
321
|
-
|
|
321
|
+
coalesce(
|
|
322
|
+
options.spanAttributeSchema,
|
|
323
|
+
process.env.DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
|
|
324
|
+
)
|
|
322
325
|
)
|
|
323
326
|
const DD_TRACE_PEER_SERVICE_MAPPING = coalesce(
|
|
324
327
|
options.peerServiceMapping,
|
|
@@ -326,11 +329,27 @@ class Config {
|
|
|
326
329
|
process.env.DD_TRACE_PEER_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
|
|
327
330
|
) : {}
|
|
328
331
|
)
|
|
329
|
-
|
|
332
|
+
|
|
333
|
+
const peerServiceSet = (
|
|
334
|
+
options.hasOwnProperty('spanComputePeerService') ||
|
|
335
|
+
process.env.hasOwnProperty('DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED')
|
|
336
|
+
)
|
|
337
|
+
const peerServiceValue = coalesce(
|
|
338
|
+
options.spanComputePeerService,
|
|
339
|
+
process.env.DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
const DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED = (
|
|
343
|
+
DD_TRACE_SPAN_ATTRIBUTE_SCHEMA === 'v0'
|
|
344
|
+
// In v0, peer service is computed only if it is explicitly set to true
|
|
345
|
+
? peerServiceSet && isTrue(peerServiceValue)
|
|
346
|
+
// In >v0, peer service is false only if it is explicitly set to false
|
|
347
|
+
: (peerServiceSet ? !isFalse(peerServiceValue) : true)
|
|
348
|
+
)
|
|
330
349
|
|
|
331
350
|
const DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED = coalesce(
|
|
332
|
-
|
|
333
|
-
|
|
351
|
+
options.spanRemoveIntegrationFromService,
|
|
352
|
+
isTrue(process.env.DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED)
|
|
334
353
|
)
|
|
335
354
|
const DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH = coalesce(
|
|
336
355
|
process.env.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
|
|
@@ -562,11 +581,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
562
581
|
exporters: DD_PROFILING_EXPORTERS
|
|
563
582
|
}
|
|
564
583
|
this.spanAttributeSchema = DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
|
|
565
|
-
this.spanComputePeerService =
|
|
566
|
-
|
|
567
|
-
: true
|
|
568
|
-
)
|
|
569
|
-
this.traceRemoveIntegrationServiceNamesEnabled = DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED
|
|
584
|
+
this.spanComputePeerService = DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED
|
|
585
|
+
this.spanRemoveIntegrationFromService = DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED
|
|
570
586
|
this.peerServiceMapping = DD_TRACE_PEER_SERVICE_MAPPING
|
|
571
587
|
this.lookup = options.lookup
|
|
572
588
|
this.startupLogs = isTrue(DD_TRACE_STARTUP_LOGS)
|
|
@@ -127,4 +127,12 @@ class ExternalLogger {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
class NoopExternalLogger {
|
|
131
|
+
log () { }
|
|
132
|
+
enqueue () { }
|
|
133
|
+
shutdown () { }
|
|
134
|
+
flush () { }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports.ExternalLogger = ExternalLogger
|
|
138
|
+
module.exports.NoopExternalLogger = NoopExternalLogger
|
|
@@ -107,7 +107,7 @@ function extractTags (trace, span) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
setSingleSpanIngestionTags(trace, context.
|
|
110
|
+
setSingleSpanIngestionTags(trace, context._spanSampling)
|
|
111
111
|
|
|
112
112
|
addTag(trace.meta, trace.metrics, 'language', 'javascript')
|
|
113
113
|
addTag(trace.meta, trace.metrics, PROCESS_ID, process.pid)
|
|
@@ -86,6 +86,13 @@ exports.datadog = function datadog (lambdaHandler) {
|
|
|
86
86
|
const context = extractContext(args)
|
|
87
87
|
|
|
88
88
|
checkTimeout(context)
|
|
89
|
-
|
|
89
|
+
const result = lambdaHandler.apply(this, args)
|
|
90
|
+
if (result && typeof result.then === 'function') {
|
|
91
|
+
return result.then((res) => {
|
|
92
|
+
clearTimeout(__lambdaTimeout)
|
|
93
|
+
return res
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
return result
|
|
90
97
|
}
|
|
91
98
|
}
|
|
@@ -10,6 +10,7 @@ const { timeInputToHrTime } = require('@opentelemetry/core')
|
|
|
10
10
|
const tracer = require('../../')
|
|
11
11
|
const DatadogSpan = require('../opentracing/span')
|
|
12
12
|
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../constants')
|
|
13
|
+
const { SERVICE_NAME, RESOURCE_NAME } = require('../../../../ext/tags')
|
|
13
14
|
|
|
14
15
|
const SpanContext = require('./span_context')
|
|
15
16
|
|
|
@@ -40,7 +41,8 @@ class Span {
|
|
|
40
41
|
hostname: _tracer._hostname,
|
|
41
42
|
integrationName: 'otel',
|
|
42
43
|
tags: {
|
|
43
|
-
|
|
44
|
+
[SERVICE_NAME]: _tracer._service,
|
|
45
|
+
[RESOURCE_NAME]: spanName
|
|
44
46
|
}
|
|
45
47
|
}, _tracer._debug)
|
|
46
48
|
|
|
@@ -12,7 +12,8 @@ class DatadogSpanContext {
|
|
|
12
12
|
this._name = props.name
|
|
13
13
|
this._isFinished = props.isFinished || false
|
|
14
14
|
this._tags = props.tags || {}
|
|
15
|
-
this._sampling =
|
|
15
|
+
this._sampling = props.sampling || {}
|
|
16
|
+
this._spanSampling = undefined
|
|
16
17
|
this._baggageItems = props.baggageItems || {}
|
|
17
18
|
this._traceparent = props.traceparent
|
|
18
19
|
this._tracestate = props.tracestate
|
|
@@ -399,6 +399,7 @@ module.exports = {
|
|
|
399
399
|
BITBUCKET_BRANCH,
|
|
400
400
|
BITBUCKET_COMMIT,
|
|
401
401
|
BITBUCKET_GIT_SSH_ORIGIN,
|
|
402
|
+
BITBUCKET_GIT_HTTP_ORIGIN,
|
|
402
403
|
BITBUCKET_TAG,
|
|
403
404
|
BITBUCKET_PIPELINE_UUID,
|
|
404
405
|
BITBUCKET_CLONE_DIR
|
|
@@ -416,7 +417,7 @@ module.exports = {
|
|
|
416
417
|
[CI_PIPELINE_URL]: url,
|
|
417
418
|
[GIT_BRANCH]: BITBUCKET_BRANCH,
|
|
418
419
|
[GIT_TAG]: BITBUCKET_TAG,
|
|
419
|
-
[GIT_REPOSITORY_URL]: BITBUCKET_GIT_SSH_ORIGIN,
|
|
420
|
+
[GIT_REPOSITORY_URL]: BITBUCKET_GIT_SSH_ORIGIN || BITBUCKET_GIT_HTTP_ORIGIN,
|
|
420
421
|
[CI_WORKSPACE_PATH]: BITBUCKET_CLONE_DIR,
|
|
421
422
|
[CI_PIPELINE_ID]: BITBUCKET_PIPELINE_UUID && BITBUCKET_PIPELINE_UUID.replace(/{|}/gm, '')
|
|
422
423
|
}
|
|
@@ -18,7 +18,6 @@ class Config {
|
|
|
18
18
|
const {
|
|
19
19
|
DD_PROFILING_ENABLED,
|
|
20
20
|
DD_PROFILING_PROFILERS,
|
|
21
|
-
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
22
21
|
DD_ENV,
|
|
23
22
|
DD_TAGS,
|
|
24
23
|
DD_SERVICE,
|
|
@@ -36,7 +35,9 @@ class Config {
|
|
|
36
35
|
DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
|
|
37
36
|
DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
|
|
38
37
|
DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
|
|
39
|
-
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
|
|
40
41
|
} = process.env
|
|
41
42
|
|
|
42
43
|
const enabled = isTrue(coalesce(options.enabled, DD_PROFILING_ENABLED, true))
|
|
@@ -51,8 +52,8 @@ class Config {
|
|
|
51
52
|
Number(DD_PROFILING_UPLOAD_TIMEOUT), 60 * 1000)
|
|
52
53
|
const sourceMap = coalesce(options.sourceMap,
|
|
53
54
|
DD_PROFILING_SOURCE_MAP, true)
|
|
54
|
-
const
|
|
55
|
-
|
|
55
|
+
const endpointCollectionEnabled = coalesce(options.endpointCollection,
|
|
56
|
+
DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED, false)
|
|
56
57
|
const pprofPrefix = coalesce(options.pprofPrefix,
|
|
57
58
|
DD_PROFILING_PPROF_PREFIX, '')
|
|
58
59
|
|
|
@@ -73,7 +74,7 @@ class Config {
|
|
|
73
74
|
this.uploadTimeout = uploadTimeout
|
|
74
75
|
this.sourceMap = sourceMap
|
|
75
76
|
this.debugSourceMaps = isTrue(coalesce(options.debugSourceMaps, DD_PROFILING_DEBUG_SOURCE_MAPS, false))
|
|
76
|
-
this.
|
|
77
|
+
this.endpointCollectionEnabled = endpointCollectionEnabled
|
|
77
78
|
this.pprofPrefix = pprofPrefix
|
|
78
79
|
|
|
79
80
|
const hostname = coalesce(options.hostname, DD_AGENT_HOST) || 'localhost'
|
|
@@ -110,6 +111,8 @@ class Config {
|
|
|
110
111
|
const profilers = options.profilers
|
|
111
112
|
? options.profilers
|
|
112
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))
|
|
113
116
|
|
|
114
117
|
this.profilers = ensureProfilers(profilers, this)
|
|
115
118
|
}
|
|
@@ -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,23 +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
80
|
this._samplingIntervalMicros = options.samplingInterval || 1e6 / 99 // 99hz
|
|
7
81
|
this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
|
|
8
82
|
this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
|
|
83
|
+
this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
|
|
9
84
|
this._mapper = undefined
|
|
10
85
|
this._pprof = undefined
|
|
11
86
|
|
|
87
|
+
// Bind to this so the same value can be used to unsubscribe later
|
|
88
|
+
this._enter = this._enter.bind(this)
|
|
12
89
|
this._logger = options.logger
|
|
13
90
|
this._started = false
|
|
14
91
|
}
|
|
15
92
|
|
|
93
|
+
codeHotspotsEnabled () {
|
|
94
|
+
return this._codeHotspotsEnabled
|
|
95
|
+
}
|
|
96
|
+
|
|
16
97
|
start ({ mapper } = {}) {
|
|
17
98
|
if (this._started) return
|
|
18
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
|
+
|
|
19
106
|
this._mapper = mapper
|
|
20
107
|
this._pprof = require('@datadog/pprof')
|
|
108
|
+
kSampleCount = this._pprof.time.constants.kSampleCount
|
|
21
109
|
|
|
22
110
|
// pprof otherwise crashes in worker threads
|
|
23
111
|
if (!process._startProfilerIdleNotifier) {
|
|
@@ -31,16 +119,62 @@ class NativeWallProfiler {
|
|
|
31
119
|
intervalMicros: this._samplingIntervalMicros,
|
|
32
120
|
durationMillis: this._flushIntervalMillis,
|
|
33
121
|
sourceMapper: this._mapper,
|
|
34
|
-
|
|
122
|
+
withContexts: this._codeHotspotsEnabled,
|
|
35
123
|
lineNumbers: false
|
|
36
124
|
})
|
|
37
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
|
+
|
|
38
138
|
this._started = true
|
|
39
139
|
}
|
|
40
140
|
|
|
41
|
-
|
|
141
|
+
_enter () {
|
|
42
142
|
if (!this._started) return
|
|
43
|
-
|
|
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)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
profile () {
|
|
177
|
+
return this._stop(true)
|
|
44
178
|
}
|
|
45
179
|
|
|
46
180
|
encode (profile) {
|
|
@@ -50,7 +184,13 @@ class NativeWallProfiler {
|
|
|
50
184
|
stop () {
|
|
51
185
|
if (!this._started) return
|
|
52
186
|
|
|
53
|
-
const profile = this.
|
|
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
|
+
}
|
|
193
|
+
|
|
54
194
|
this._started = false
|
|
55
195
|
return profile
|
|
56
196
|
}
|
|
@@ -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
|
}
|
|
@@ -25,6 +25,10 @@ function mapToJsonArray (map) {
|
|
|
25
25
|
return Array.from(map.values()).map(v => v.toJSON())
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function hasPoints (metric) {
|
|
29
|
+
return metric.points.length > 0
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
class Metric {
|
|
29
33
|
constructor (namespace, metric, common, tags) {
|
|
30
34
|
this.namespace = namespace.toString()
|
|
@@ -172,10 +176,16 @@ class MetricsCollection extends Map {
|
|
|
172
176
|
|
|
173
177
|
toJSON () {
|
|
174
178
|
if (!this.size) return
|
|
179
|
+
|
|
180
|
+
const series = mapToJsonArray(this)
|
|
181
|
+
.filter(hasPoints)
|
|
182
|
+
|
|
183
|
+
if (!series.length) return
|
|
184
|
+
|
|
175
185
|
const { namespace } = this
|
|
176
186
|
return {
|
|
177
187
|
namespace,
|
|
178
|
-
series
|
|
188
|
+
series
|
|
179
189
|
}
|
|
180
190
|
}
|
|
181
191
|
}
|