dd-trace 2.11.0 → 2.12.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 -1
- package/index.d.ts +3 -3
- package/package.json +4 -5
- package/packages/datadog-core/src/storage/async_hooks.js +4 -4
- package/packages/datadog-core/src/storage/async_resource.js +14 -4
- package/packages/datadog-instrumentations/src/couchbase.js +166 -61
- package/packages/datadog-instrumentations/src/graphql.js +17 -5
- package/packages/datadog-instrumentations/src/mocha.js +88 -18
- package/packages/datadog-instrumentations/src/restify.js +4 -8
- package/packages/datadog-plugin-couchbase/src/index.js +8 -10
- package/packages/datadog-plugin-graphql/src/resolve.js +2 -0
- package/packages/datadog-plugin-http/src/server.js +3 -8
- package/packages/datadog-plugin-mocha/src/index.js +80 -3
- package/packages/datadog-plugin-next/src/index.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +34 -10
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +111 -15
- package/packages/dd-trace/src/plugin_manager.js +49 -33
- package/packages/dd-trace/src/plugins/util/test.js +32 -1
- package/packages/dd-trace/src/plugins/util/web.js +22 -17
- package/packages/dd-trace/src/profiling/config.js +10 -2
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -2
- package/packages/dd-trace/src/profiling/exporters/form-data.js +53 -0
- package/packages/dd-trace/src/profiling/index.js +2 -0
- package/packages/dd-trace/src/profiling/profiler.js +6 -1
- package/packages/dd-trace/src/profiling/profilers/cpu.js +126 -0
|
@@ -6,6 +6,7 @@ const { URL } = require('url')
|
|
|
6
6
|
const { AgentExporter } = require('./exporters/agent')
|
|
7
7
|
const { FileExporter } = require('./exporters/file')
|
|
8
8
|
const { ConsoleLogger } = require('./loggers/console')
|
|
9
|
+
const CpuProfiler = require('./profilers/cpu')
|
|
9
10
|
const WallProfiler = require('./profilers/wall')
|
|
10
11
|
const SpaceProfiler = require('./profilers/space')
|
|
11
12
|
const { tagger } = require('./tagger')
|
|
@@ -13,6 +14,7 @@ const { tagger } = require('./tagger')
|
|
|
13
14
|
const {
|
|
14
15
|
DD_PROFILING_ENABLED,
|
|
15
16
|
DD_PROFILING_PROFILERS,
|
|
17
|
+
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
16
18
|
DD_ENV,
|
|
17
19
|
DD_TAGS,
|
|
18
20
|
DD_SERVICE,
|
|
@@ -37,6 +39,8 @@ class Config {
|
|
|
37
39
|
DD_PROFILING_UPLOAD_TIMEOUT, 60 * 1000)
|
|
38
40
|
const sourceMap = coalesce(options.sourceMap,
|
|
39
41
|
DD_PROFILING_SOURCE_MAP, true)
|
|
42
|
+
const endpointCollection = coalesce(options.endpointCollection,
|
|
43
|
+
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED, false)
|
|
40
44
|
|
|
41
45
|
this.enabled = String(enabled) !== 'false'
|
|
42
46
|
this.service = service
|
|
@@ -53,6 +57,7 @@ class Config {
|
|
|
53
57
|
this.flushInterval = flushInterval
|
|
54
58
|
this.uploadTimeout = uploadTimeout
|
|
55
59
|
this.sourceMap = sourceMap
|
|
60
|
+
this.endpointCollection = endpointCollection
|
|
56
61
|
|
|
57
62
|
const hostname = coalesce(options.hostname, DD_AGENT_HOST, 'localhost')
|
|
58
63
|
const port = coalesce(options.port, DD_TRACE_AGENT_PORT, 8126)
|
|
@@ -64,8 +69,8 @@ class Config {
|
|
|
64
69
|
], this)
|
|
65
70
|
|
|
66
71
|
const profilers = coalesce(options.profilers, DD_PROFILING_PROFILERS, [
|
|
67
|
-
new WallProfiler(),
|
|
68
|
-
new SpaceProfiler()
|
|
72
|
+
new WallProfiler(this),
|
|
73
|
+
new SpaceProfiler(this)
|
|
69
74
|
])
|
|
70
75
|
|
|
71
76
|
this.profilers = ensureProfilers(profilers, this)
|
|
@@ -100,10 +105,13 @@ function ensureExporters (exporters, options) {
|
|
|
100
105
|
|
|
101
106
|
function getProfiler (name, options) {
|
|
102
107
|
switch (name) {
|
|
108
|
+
case 'cpu':
|
|
103
109
|
case 'wall':
|
|
104
110
|
return new WallProfiler(options)
|
|
105
111
|
case 'space':
|
|
106
112
|
return new SpaceProfiler(options)
|
|
113
|
+
case 'cpu-experimental':
|
|
114
|
+
return new CpuProfiler(options)
|
|
107
115
|
default:
|
|
108
116
|
options.logger.error(`Unknown profiler "${name}"`)
|
|
109
117
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const retry = require('retry')
|
|
4
4
|
const { request } = require('http')
|
|
5
|
-
const FormData = require('form-data')
|
|
5
|
+
const FormData = require('./form-data')
|
|
6
6
|
|
|
7
7
|
// TODO: avoid using dd-trace internals. Make this a separate module?
|
|
8
8
|
const docker = require('../../exporters/common/docker')
|
|
@@ -22,7 +22,6 @@ function sendRequest (options, form, callback) {
|
|
|
22
22
|
})
|
|
23
23
|
req.on('error', callback)
|
|
24
24
|
if (form) form.pipe(req)
|
|
25
|
-
req.end()
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
function getBody (stream, callback) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Readable } = require('stream')
|
|
4
|
+
const id = require('../../id')
|
|
5
|
+
|
|
6
|
+
class FormData extends Readable {
|
|
7
|
+
constructor () {
|
|
8
|
+
super()
|
|
9
|
+
|
|
10
|
+
this._boundary = id().toString()
|
|
11
|
+
this._data = []
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
append (key, value, options = {}) {
|
|
15
|
+
this._appendBoundary()
|
|
16
|
+
|
|
17
|
+
if (options.filename) {
|
|
18
|
+
this._appendFile(key, value, options)
|
|
19
|
+
} else {
|
|
20
|
+
this._appendMetadata(key, value, options)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getHeaders () {
|
|
25
|
+
return { 'Content-Type': 'multipart/form-data; boundary=' + this._boundary }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_appendBoundary () {
|
|
29
|
+
this._data.push(`--${this._boundary}\r\n`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_appendMetadata (key, value) {
|
|
33
|
+
this._data.push(`Content-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_appendFile (key, value, { filename, contentType = 'application/octet-stream' }) {
|
|
37
|
+
this._data.push(`Content-Disposition: form-data; name="${key}"; filename="${filename}"\r\n`)
|
|
38
|
+
this._data.push(`Content-Type: ${contentType}\r\n\r\n`)
|
|
39
|
+
this._data.push(value)
|
|
40
|
+
this._data.push('\r\n')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_read () {
|
|
44
|
+
this.push(this._data.shift())
|
|
45
|
+
|
|
46
|
+
if (this._data.length === 0) {
|
|
47
|
+
this.push(`--${this._boundary}--\r\n`)
|
|
48
|
+
this.push(null)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = FormData
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { Profiler } = require('./profiler')
|
|
4
|
+
const CpuProfiler = require('./profilers/cpu')
|
|
4
5
|
const WallProfiler = require('./profilers/wall')
|
|
5
6
|
const SpaceProfiler = require('./profilers/space')
|
|
6
7
|
const { AgentExporter } = require('./exporters/agent')
|
|
@@ -13,6 +14,7 @@ module.exports = {
|
|
|
13
14
|
profiler,
|
|
14
15
|
AgentExporter,
|
|
15
16
|
FileExporter,
|
|
17
|
+
CpuProfiler,
|
|
16
18
|
WallProfiler,
|
|
17
19
|
SpaceProfiler,
|
|
18
20
|
ConsoleLogger
|
|
@@ -91,7 +91,12 @@ class Profiler extends EventEmitter {
|
|
|
91
91
|
if (!profile) continue
|
|
92
92
|
|
|
93
93
|
profiles[profiler.type] = await profiler.encode(profile)
|
|
94
|
-
this._logger.debug(
|
|
94
|
+
this._logger.debug(() => {
|
|
95
|
+
const profileJson = JSON.stringify(profile, (key, value) => {
|
|
96
|
+
return typeof value === 'bigint' ? value.toString() : value
|
|
97
|
+
})
|
|
98
|
+
return `Collected ${profiler.type} profile: ` + profileJson
|
|
99
|
+
})
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
this._capture(this._config.flushInterval)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict'
|
|
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 afterCh = dc.channel('dd-trace:storage:after')
|
|
9
|
+
|
|
10
|
+
function getActiveSpan () {
|
|
11
|
+
const store = storage.getStore()
|
|
12
|
+
if (!store) return
|
|
13
|
+
return store.span
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getStartedSpans (activeSpan) {
|
|
17
|
+
const context = activeSpan.context()
|
|
18
|
+
if (!context) return
|
|
19
|
+
return context._trace.started
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getSpanContextTags (span) {
|
|
23
|
+
return span._context()._tags
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isWebServerSpan (tags) {
|
|
27
|
+
return tags['span.type'] === 'web'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function endpointNameFromTags (tags) {
|
|
31
|
+
return tags['resource.name'] || [
|
|
32
|
+
tags['http.method'],
|
|
33
|
+
tags['http.route']
|
|
34
|
+
].filter(v => v).join(' ')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class NativeCpuProfiler {
|
|
38
|
+
constructor (options = {}) {
|
|
39
|
+
this.type = 'cpu'
|
|
40
|
+
this._frequency = options.frequency || 99
|
|
41
|
+
this._mapper = undefined
|
|
42
|
+
this._pprof = undefined
|
|
43
|
+
this._started = false
|
|
44
|
+
this._cpuProfiler = undefined
|
|
45
|
+
this._endpointCollection = options.endpointCollection
|
|
46
|
+
|
|
47
|
+
// Bind to this so the same value can be used to unsubscribe later
|
|
48
|
+
this._enter = this._enter.bind(this)
|
|
49
|
+
this._exit = this._exit.bind(this)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_enter () {
|
|
53
|
+
if (!this._cpuProfiler) return
|
|
54
|
+
|
|
55
|
+
const active = getActiveSpan()
|
|
56
|
+
if (!active) return
|
|
57
|
+
|
|
58
|
+
const activeCtx = active._context()
|
|
59
|
+
if (!activeCtx) return
|
|
60
|
+
|
|
61
|
+
const spans = getStartedSpans(active)
|
|
62
|
+
if (!spans || !spans.length) return
|
|
63
|
+
|
|
64
|
+
const firstCtx = spans[0]._context()
|
|
65
|
+
if (!firstCtx) return
|
|
66
|
+
|
|
67
|
+
const labels = {
|
|
68
|
+
'local root span id': firstCtx.toSpanId(),
|
|
69
|
+
'span id': activeCtx.toSpanId()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (this._endpointCollection) {
|
|
73
|
+
const webServerTags = spans
|
|
74
|
+
.map(getSpanContextTags)
|
|
75
|
+
.filter(isWebServerSpan)[0]
|
|
76
|
+
|
|
77
|
+
if (webServerTags) {
|
|
78
|
+
labels['trace endpoint'] = endpointNameFromTags(webServerTags)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this._cpuProfiler.labels = labels
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_exit () {
|
|
86
|
+
if (!this._cpuProfiler) return
|
|
87
|
+
this._cpuProfiler.labels = {}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
start ({ mapper } = {}) {
|
|
91
|
+
if (this._started) return
|
|
92
|
+
this._started = true
|
|
93
|
+
|
|
94
|
+
this._mapper = mapper
|
|
95
|
+
if (!this._pprof) {
|
|
96
|
+
this._pprof = require('@datadog/pprof')
|
|
97
|
+
this._cpuProfiler = new this._pprof.CpuProfiler()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this._cpuProfiler.start(this._frequency)
|
|
101
|
+
|
|
102
|
+
this._enter()
|
|
103
|
+
beforeCh.subscribe(this._enter)
|
|
104
|
+
afterCh.subscribe(this._exit)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
profile () {
|
|
108
|
+
if (!this._started) return
|
|
109
|
+
return this._cpuProfiler.profile()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
encode (profile) {
|
|
113
|
+
return this._pprof.encode(profile)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
stop () {
|
|
117
|
+
if (!this._started) return
|
|
118
|
+
this._started = false
|
|
119
|
+
|
|
120
|
+
this._cpuProfiler.stop()
|
|
121
|
+
beforeCh.unsubscribe(this._enter)
|
|
122
|
+
afterCh.unsubscribe(this._exit)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = NativeCpuProfiler
|