dd-trace 5.92.0 → 5.93.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/package.json +2 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -0
- package/packages/dd-trace/src/config/supported-configurations.json +2 -2
- package/packages/dd-trace/src/crashtracking/index.js +7 -1
- package/packages/dd-trace/src/exporters/common/docker.js +1 -0
- package/packages/dd-trace/src/exporters/common/request.js +26 -17
- package/packages/dd-trace/src/priority_sampler.js +6 -3
- package/packages/dd-trace/src/profiling/profiler.js +78 -47
- package/packages/dd-trace/src/proxy.js +4 -3
- package/packages/dd-trace/src/tracer_metadata.js +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.93.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
"import-in-the-middle": "^3.0.0"
|
|
139
139
|
},
|
|
140
140
|
"optionalDependencies": {
|
|
141
|
-
"@datadog/libdatadog": "0.
|
|
141
|
+
"@datadog/libdatadog": "0.9.2",
|
|
142
142
|
"@datadog/native-appsec": "11.0.1",
|
|
143
143
|
"@datadog/native-iast-taint-tracking": "4.1.0",
|
|
144
144
|
"@datadog/native-metrics": "3.1.1",
|
|
@@ -154,6 +154,11 @@ class BaseAwsSdkPlugin extends ClientPlugin {
|
|
|
154
154
|
})
|
|
155
155
|
|
|
156
156
|
this.finish(ctx)
|
|
157
|
+
|
|
158
|
+
if (IS_SERVERLESS) {
|
|
159
|
+
const peerStore = storage('peerServerless').getStore()
|
|
160
|
+
if (peerStore) delete peerStore.peerHostname
|
|
161
|
+
}
|
|
157
162
|
})
|
|
158
163
|
|
|
159
164
|
this.addBind(`apm:aws:response:start:${this.serviceIdentifier}`, ctx => {
|
|
@@ -744,9 +744,9 @@
|
|
|
744
744
|
],
|
|
745
745
|
"DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED": [
|
|
746
746
|
{
|
|
747
|
-
"implementation": "
|
|
747
|
+
"implementation": "B",
|
|
748
748
|
"type": "boolean",
|
|
749
|
-
"default": "
|
|
749
|
+
"default": "true",
|
|
750
750
|
"configurationNames": [
|
|
751
751
|
"propagateProcessTags.enabled"
|
|
752
752
|
]
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { existsSync } = require('node:fs')
|
|
3
4
|
const { isMainThread } = require('worker_threads')
|
|
4
5
|
const log = require('../log')
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
// libdatadog v29 crashtracker segfaults during init on ARM64 musl (Alpine).
|
|
8
|
+
// The segfault bypasses JS try/catch so we must avoid loading it entirely.
|
|
9
|
+
// See: https://github.com/DataDog/libdatadog-nodejs/issues/114
|
|
10
|
+
const isArm64Musl = process.arch === 'arm64' && existsSync('/etc/alpine-release')
|
|
11
|
+
|
|
12
|
+
if (isMainThread && !isArm64Musl) {
|
|
7
13
|
try {
|
|
8
14
|
module.exports = require('./crashtracker')
|
|
9
15
|
} catch (e) {
|
|
@@ -14,9 +14,9 @@ const { urlToHttpOptions } = require('./url-to-http-options-polyfill')
|
|
|
14
14
|
const docker = require('./docker')
|
|
15
15
|
const { httpAgent, httpsAgent } = require('./agents')
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const maxActiveBufferSize = 1024 * 1024 * 64
|
|
18
18
|
|
|
19
|
-
let
|
|
19
|
+
let activeBufferSize = 0
|
|
20
20
|
|
|
21
21
|
function parseUrl (urlObjOrString) {
|
|
22
22
|
if (urlObjOrString !== null && typeof urlObjOrString === 'object') return urlToHttpOptions(urlObjOrString)
|
|
@@ -50,7 +50,22 @@ function request (data, options, callback) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
if (data instanceof Readable) {
|
|
54
|
+
const chunks = []
|
|
55
|
+
|
|
56
|
+
data
|
|
57
|
+
.on('data', (data) => {
|
|
58
|
+
chunks.push(data)
|
|
59
|
+
})
|
|
60
|
+
.on('end', () => {
|
|
61
|
+
request(Buffer.concat(chunks), options, callback)
|
|
62
|
+
})
|
|
63
|
+
.on('error', (err) => {
|
|
64
|
+
callback(err)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return
|
|
68
|
+
}
|
|
54
69
|
|
|
55
70
|
// The timeout should be kept low to avoid excessive queueing.
|
|
56
71
|
const timeout = options.timeout || 2000
|
|
@@ -58,12 +73,10 @@ function request (data, options, callback) {
|
|
|
58
73
|
const client = isSecure ? https : http
|
|
59
74
|
let dataArray = data
|
|
60
75
|
|
|
61
|
-
if (!
|
|
62
|
-
|
|
63
|
-
dataArray = [data]
|
|
64
|
-
}
|
|
65
|
-
options.headers['Content-Length'] = byteLength(dataArray)
|
|
76
|
+
if (!Array.isArray(data)) {
|
|
77
|
+
dataArray = [data]
|
|
66
78
|
}
|
|
79
|
+
options.headers['Content-Length'] = byteLength(dataArray)
|
|
67
80
|
|
|
68
81
|
docker.inject(options.headers)
|
|
69
82
|
|
|
@@ -126,14 +139,14 @@ function request (data, options, callback) {
|
|
|
126
139
|
return callback(null)
|
|
127
140
|
}
|
|
128
141
|
|
|
129
|
-
|
|
142
|
+
activeBufferSize += options.headers['Content-Length'] ?? 0
|
|
130
143
|
|
|
131
144
|
storage('legacy').run({ noop: true }, () => {
|
|
132
145
|
let finished = false
|
|
133
146
|
const finalize = () => {
|
|
134
147
|
if (finished) return
|
|
135
148
|
finished = true
|
|
136
|
-
|
|
149
|
+
activeBufferSize -= options.headers['Content-Length'] ?? 0
|
|
137
150
|
}
|
|
138
151
|
|
|
139
152
|
const req = client.request(options, (res) => onResponse(res, finalize))
|
|
@@ -158,12 +171,8 @@ function request (data, options, callback) {
|
|
|
158
171
|
}
|
|
159
172
|
})
|
|
160
173
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
} else {
|
|
164
|
-
for (const buffer of dataArray) req.write(buffer)
|
|
165
|
-
req.end()
|
|
166
|
-
}
|
|
174
|
+
for (const buffer of dataArray) req.write(buffer)
|
|
175
|
+
req.end()
|
|
167
176
|
})
|
|
168
177
|
}
|
|
169
178
|
|
|
@@ -183,7 +192,7 @@ function byteLength (data) {
|
|
|
183
192
|
|
|
184
193
|
Object.defineProperty(request, 'writable', {
|
|
185
194
|
get () {
|
|
186
|
-
return
|
|
195
|
+
return activeBufferSize < maxActiveBufferSize
|
|
187
196
|
},
|
|
188
197
|
})
|
|
189
198
|
|
|
@@ -37,13 +37,16 @@ const {
|
|
|
37
37
|
const DEFAULT_KEY = 'service:,env:'
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Formats a sampling rate as a string with up to 6
|
|
40
|
+
* Formats a sampling rate as a string with up to 6 decimal digits and no trailing zeros.
|
|
41
41
|
*
|
|
42
42
|
* @param {number} rate
|
|
43
|
-
* @returns {string}
|
|
44
43
|
*/
|
|
45
44
|
function formatKnuthRate (rate) {
|
|
46
|
-
|
|
45
|
+
const string = Number(rate).toFixed(6)
|
|
46
|
+
for (let i = string.length - 1; i > 0; i--) {
|
|
47
|
+
if (string[i] === '0') continue
|
|
48
|
+
return string.slice(0, i + (string[i] === '.' ? 0 : 1))
|
|
49
|
+
}
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
const defaultSampler = new Sampler(AUTO_KEEP)
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { EventEmitter } = require('events')
|
|
4
|
-
const { promisify } = require('util')
|
|
5
|
-
const zlib = require('zlib')
|
|
6
4
|
const dc = require('dc-polyfill')
|
|
7
5
|
const crashtracker = require('../crashtracking')
|
|
8
6
|
const log = require('../log')
|
|
@@ -34,6 +32,14 @@ function findWebSpan (startedSpans, spanId) {
|
|
|
34
32
|
return false
|
|
35
33
|
}
|
|
36
34
|
|
|
35
|
+
const MISSING_SOURCE_MAPS_TOKEN = 'dd:has-missing-map-files'
|
|
36
|
+
|
|
37
|
+
function profileHasMissingSourceMaps (profile) {
|
|
38
|
+
const strings = profile?.stringTable?.strings
|
|
39
|
+
if (!strings) return false
|
|
40
|
+
return profile.comment?.some(idx => strings[idx] === MISSING_SOURCE_MAPS_TOKEN) ?? false
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
function processInfo (infos, info, type) {
|
|
38
44
|
if (Object.keys(info).length > 0) {
|
|
39
45
|
infos[type] = info
|
|
@@ -42,6 +48,7 @@ function processInfo (infos, info, type) {
|
|
|
42
48
|
|
|
43
49
|
class Profiler extends EventEmitter {
|
|
44
50
|
#compressionFn
|
|
51
|
+
#compressionFnInitialized = false
|
|
45
52
|
#compressionOptions
|
|
46
53
|
#config
|
|
47
54
|
#enabled = false
|
|
@@ -50,7 +57,6 @@ class Profiler extends EventEmitter {
|
|
|
50
57
|
#logger
|
|
51
58
|
#profileSeq = 0
|
|
52
59
|
#spanFinishListener
|
|
53
|
-
#sourceMapCount = 0
|
|
54
60
|
#timer
|
|
55
61
|
|
|
56
62
|
constructor () {
|
|
@@ -116,11 +122,13 @@ class Profiler extends EventEmitter {
|
|
|
116
122
|
reportHostname,
|
|
117
123
|
}
|
|
118
124
|
|
|
119
|
-
|
|
125
|
+
try {
|
|
126
|
+
return this._start(options)
|
|
127
|
+
} catch (err) {
|
|
120
128
|
logError(logger, 'Error starting profiler. For troubleshooting tips, see ' +
|
|
121
129
|
'<https://dtdg.co/nodejs-profiler-troubleshooting>', err)
|
|
122
130
|
return false
|
|
123
|
-
}
|
|
131
|
+
}
|
|
124
132
|
}
|
|
125
133
|
|
|
126
134
|
get enabled () {
|
|
@@ -131,7 +139,50 @@ class Profiler extends EventEmitter {
|
|
|
131
139
|
logError(this.#logger, err)
|
|
132
140
|
}
|
|
133
141
|
|
|
134
|
-
|
|
142
|
+
#getCompressionFn () {
|
|
143
|
+
if (!this.#compressionFnInitialized) {
|
|
144
|
+
this.#compressionFnInitialized = true
|
|
145
|
+
try {
|
|
146
|
+
const { promisify } = require('util')
|
|
147
|
+
const zlib = require('zlib')
|
|
148
|
+
const { method, level: clevel } = this.#config.uploadCompression
|
|
149
|
+
switch (method) {
|
|
150
|
+
case 'gzip':
|
|
151
|
+
this.#compressionFn = promisify(zlib.gzip)
|
|
152
|
+
if (clevel !== undefined) {
|
|
153
|
+
this.#compressionOptions = {
|
|
154
|
+
level: clevel,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
break
|
|
158
|
+
case 'zstd':
|
|
159
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
160
|
+
if (typeof zlib.zstdCompress === 'function') {
|
|
161
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
162
|
+
this.#compressionFn = promisify(zlib.zstdCompress)
|
|
163
|
+
if (clevel !== undefined) {
|
|
164
|
+
this.#compressionOptions = {
|
|
165
|
+
params: {
|
|
166
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
167
|
+
[zlib.constants.ZSTD_c_compressionLevel]: clevel,
|
|
168
|
+
},
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
const zstdCompress = require('@datadog/libdatadog').load('datadog-js-zstd').zstd_compress
|
|
173
|
+
const level = clevel ?? 0 // 0 is zstd default compression level
|
|
174
|
+
this.#compressionFn = (buffer) => Promise.resolve(Buffer.from(zstdCompress(buffer, level)))
|
|
175
|
+
}
|
|
176
|
+
break
|
|
177
|
+
}
|
|
178
|
+
} catch (err) {
|
|
179
|
+
this.#logError(err)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return this.#compressionFn
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_start (options) {
|
|
135
186
|
if (this.enabled) return true
|
|
136
187
|
|
|
137
188
|
const config = this.#config = new Config(options)
|
|
@@ -148,45 +199,21 @@ class Profiler extends EventEmitter {
|
|
|
148
199
|
setLogger(config.logger)
|
|
149
200
|
|
|
150
201
|
if (config.sourceMap) {
|
|
151
|
-
mapper =
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const clevel = config.uploadCompression.level
|
|
163
|
-
switch (config.uploadCompression.method) {
|
|
164
|
-
case 'gzip':
|
|
165
|
-
this.#compressionFn = promisify(zlib.gzip)
|
|
166
|
-
if (clevel !== undefined) {
|
|
167
|
-
this.#compressionOptions = {
|
|
168
|
-
level: clevel,
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
break
|
|
172
|
-
case 'zstd':
|
|
173
|
-
if (typeof zlib.zstdCompress === 'function') { // eslint-disable-line n/no-unsupported-features/node-builtins
|
|
174
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
175
|
-
this.#compressionFn = promisify(zlib.zstdCompress)
|
|
176
|
-
if (clevel !== undefined) {
|
|
177
|
-
this.#compressionOptions = {
|
|
178
|
-
params: {
|
|
179
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
180
|
-
[zlib.constants.ZSTD_c_compressionLevel]: clevel,
|
|
181
|
-
},
|
|
182
|
-
}
|
|
202
|
+
mapper = new SourceMapper(config.debugSourceMaps)
|
|
203
|
+
mapper.loadDirectory(process.cwd())
|
|
204
|
+
.then(() => {
|
|
205
|
+
if (config.debugSourceMaps) {
|
|
206
|
+
const count = mapper.infoMap.size
|
|
207
|
+
this.#logger.debug(() => {
|
|
208
|
+
return count === 0
|
|
209
|
+
? 'Found no source maps'
|
|
210
|
+
: `Found source maps for following files: [${[...mapper.infoMap.keys()].join(', ')}]`
|
|
211
|
+
})
|
|
183
212
|
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
break
|
|
213
|
+
})
|
|
214
|
+
.catch((err) => {
|
|
215
|
+
this.#logError(err)
|
|
216
|
+
})
|
|
190
217
|
}
|
|
191
218
|
} catch (err) {
|
|
192
219
|
this.#logError(err)
|
|
@@ -295,7 +322,7 @@ class Profiler extends EventEmitter {
|
|
|
295
322
|
return {
|
|
296
323
|
serverless: this.serverless,
|
|
297
324
|
settings: this.#config.systemInfoReport,
|
|
298
|
-
|
|
325
|
+
hasMissingSourceMaps: false,
|
|
299
326
|
}
|
|
300
327
|
}
|
|
301
328
|
|
|
@@ -332,15 +359,19 @@ class Profiler extends EventEmitter {
|
|
|
332
359
|
|
|
333
360
|
const encodedProfiles = {}
|
|
334
361
|
const infos = this.#createInitialInfos()
|
|
362
|
+
const compressionFn = this.#getCompressionFn()
|
|
335
363
|
|
|
336
364
|
// encode and export asynchronously
|
|
337
365
|
await Promise.all(profiles.map(async ({ profiler, profile, info }) => {
|
|
338
366
|
try {
|
|
339
367
|
const encoded = await profiler.encode(profile)
|
|
340
|
-
const compressed = encoded instanceof Buffer &&
|
|
341
|
-
? await
|
|
368
|
+
const compressed = encoded instanceof Buffer && compressionFn !== undefined
|
|
369
|
+
? await compressionFn(encoded, this.#compressionOptions)
|
|
342
370
|
: encoded
|
|
343
371
|
encodedProfiles[profiler.type] = compressed
|
|
372
|
+
if (profileHasMissingSourceMaps(profile)) {
|
|
373
|
+
infos.hasMissingSourceMaps = true
|
|
374
|
+
}
|
|
344
375
|
processInfo(infos, info, profiler.type)
|
|
345
376
|
this.#logger.debug(() => {
|
|
346
377
|
const profileJson = JSON.stringify(profile, (_, value) => {
|
|
@@ -180,7 +180,7 @@ class Tracer extends NoopProxy {
|
|
|
180
180
|
if (config.profiling.enabled === 'true') {
|
|
181
181
|
this._profilerStarted = this._startProfiler(config)
|
|
182
182
|
} else {
|
|
183
|
-
this._profilerStarted =
|
|
183
|
+
this._profilerStarted = false
|
|
184
184
|
if (config.profiling.enabled === 'auto') {
|
|
185
185
|
const { SSIHeuristics } = require('./profiling/ssi-heuristics')
|
|
186
186
|
const ssiHeuristics = new SSIHeuristics(config)
|
|
@@ -252,6 +252,7 @@ class Tracer extends NoopProxy {
|
|
|
252
252
|
'Error starting profiler. For troubleshooting tips, see <https://dtdg.co/nodejs-profiler-troubleshooting>',
|
|
253
253
|
e
|
|
254
254
|
)
|
|
255
|
+
return false
|
|
255
256
|
}
|
|
256
257
|
}
|
|
257
258
|
|
|
@@ -329,11 +330,11 @@ class Tracer extends NoopProxy {
|
|
|
329
330
|
* @override
|
|
330
331
|
*/
|
|
331
332
|
profilerStarted () {
|
|
332
|
-
if (
|
|
333
|
+
if (this._profilerStarted === undefined) {
|
|
333
334
|
// injection hardening: this is only ever invoked from tests.
|
|
334
335
|
throw new Error('profilerStarted() must be called after init()')
|
|
335
336
|
}
|
|
336
|
-
return this._profilerStarted
|
|
337
|
+
return Promise.resolve(this._profilerStarted)
|
|
337
338
|
}
|
|
338
339
|
|
|
339
340
|
/**
|
|
@@ -11,13 +11,22 @@ function storeConfig (config) {
|
|
|
11
11
|
return
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
const { containerId } = require('./exporters/common/docker')
|
|
15
|
+
const processTags = require('./process-tags')
|
|
16
|
+
|
|
17
|
+
const processTagsSerialized = config.propagateProcessTags?.enabled
|
|
18
|
+
? (processTags.serialized || null)
|
|
19
|
+
: null
|
|
20
|
+
|
|
14
21
|
const metadata = new processDiscovery.TracerMetadata(
|
|
15
22
|
config.tags['runtime-id'],
|
|
16
23
|
tracerVersion,
|
|
17
24
|
config.hostname,
|
|
18
25
|
config.service || null,
|
|
19
26
|
config.env || null,
|
|
20
|
-
config.version || null
|
|
27
|
+
config.version || null,
|
|
28
|
+
processTagsSerialized,
|
|
29
|
+
containerId || null
|
|
21
30
|
)
|
|
22
31
|
|
|
23
32
|
return processDiscovery.storeMetadata(metadata)
|