dd-trace 5.96.0 → 5.97.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/index.d.ts +34 -0
- package/package.json +9 -7
- package/packages/datadog-esbuild/index.js +20 -9
- package/packages/datadog-instrumentations/src/child_process.js +7 -17
- package/packages/datadog-instrumentations/src/crypto.js +1 -2
- package/packages/datadog-instrumentations/src/cucumber.js +4 -1
- package/packages/datadog-instrumentations/src/cypress-config.js +324 -0
- package/packages/datadog-instrumentations/src/cypress.js +86 -4
- package/packages/datadog-instrumentations/src/dns.js +1 -2
- package/packages/datadog-instrumentations/src/express.js +4 -4
- package/packages/datadog-instrumentations/src/fs.js +27 -29
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +41 -13
- package/packages/datadog-instrumentations/src/helpers/hook.js +31 -6
- package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -19
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -13
- package/packages/datadog-instrumentations/src/helpers/register.js +103 -142
- package/packages/datadog-instrumentations/src/http/client.js +2 -3
- package/packages/datadog-instrumentations/src/http/server.js +2 -5
- package/packages/datadog-instrumentations/src/http2/client.js +1 -3
- package/packages/datadog-instrumentations/src/http2/server.js +1 -3
- package/packages/datadog-instrumentations/src/jest.js +13 -4
- package/packages/datadog-instrumentations/src/limitd-client.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +4 -1
- package/packages/datadog-instrumentations/src/net.js +2 -8
- package/packages/datadog-instrumentations/src/pino.js +1 -1
- package/packages/datadog-instrumentations/src/playwright.js +4 -1
- package/packages/datadog-instrumentations/src/prisma.js +1 -2
- package/packages/datadog-instrumentations/src/selenium.js +4 -1
- package/packages/datadog-instrumentations/src/sequelize.js +1 -1
- package/packages/datadog-instrumentations/src/url.js +1 -3
- package/packages/datadog-instrumentations/src/vitest.js +5 -1
- package/packages/datadog-instrumentations/src/vm.js +1 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +4 -3
- package/packages/datadog-plugin-cucumber/src/index.js +7 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +57 -5
- package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +4 -2
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +31 -4
- package/packages/datadog-plugin-mocha/src/index.js +5 -2
- package/packages/datadog-plugin-next/src/index.js +2 -14
- package/packages/datadog-plugin-openai/src/services.js +1 -0
- package/packages/datadog-webpack/index.js +3 -3
- package/packages/dd-trace/index.js +12 -10
- package/packages/dd-trace/src/agent/url.js +2 -2
- package/packages/dd-trace/src/aiguard/sdk.js +4 -0
- package/packages/dd-trace/src/appsec/blocking.js +3 -0
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/remote_config.js +1 -0
- package/packages/dd-trace/src/appsec/sdk/index.js +4 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +6 -1
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
- package/packages/dd-trace/src/config/defaults.js +316 -146
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -1
- package/packages/dd-trace/src/config/helper.js +59 -10
- package/packages/dd-trace/src/config/index.js +569 -1505
- package/packages/dd-trace/src/config/parsers.js +256 -0
- package/packages/dd-trace/src/config/remote_config.js +59 -2
- package/packages/dd-trace/src/config/supported-configurations.json +350 -433
- package/packages/dd-trace/src/crashtracking/crashtracker.js +7 -1
- package/packages/dd-trace/src/crashtracking/index.js +1 -7
- package/packages/dd-trace/src/debugger/index.js +1 -1
- package/packages/dd-trace/src/dogstatsd.js +12 -9
- package/packages/dd-trace/src/encode/0.4.js +1 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +7 -1
- package/packages/dd-trace/src/exporters/common/request.js +9 -0
- package/packages/dd-trace/src/exporters/common/writer.js +12 -2
- package/packages/dd-trace/src/heap_snapshots.js +3 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/lambda/runtime/ritm.js +6 -6
- package/packages/dd-trace/src/llmobs/index.js +4 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +60 -12
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +4 -2
- package/packages/dd-trace/src/llmobs/sdk.js +12 -8
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +9 -6
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -0
- package/packages/dd-trace/src/llmobs/writers/util.js +3 -0
- package/packages/dd-trace/src/log/index.js +26 -55
- package/packages/dd-trace/src/log/writer.js +7 -19
- package/packages/dd-trace/src/noop/proxy.js +8 -0
- package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +9 -4
- package/packages/dd-trace/src/payload-tagging/config/index.js +6 -5
- package/packages/dd-trace/src/plugin_manager.js +8 -6
- package/packages/dd-trace/src/plugins/ci_plugin.js +4 -0
- package/packages/dd-trace/src/plugins/plugin.js +7 -4
- package/packages/dd-trace/src/process-tags/index.js +3 -0
- package/packages/dd-trace/src/profiler.js +27 -2
- package/packages/dd-trace/src/profiling/config.js +73 -241
- package/packages/dd-trace/src/profiling/exporter_cli.js +1 -4
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +6 -2
- package/packages/dd-trace/src/profiling/profiler.js +56 -44
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +89 -6
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +4 -1
- package/packages/dd-trace/src/propagation-hash/index.js +2 -1
- package/packages/dd-trace/src/proxy.js +32 -3
- package/packages/dd-trace/src/remote_config/index.js +3 -0
- package/packages/dd-trace/src/require-package-json.js +8 -4
- package/packages/dd-trace/src/ritm.js +58 -26
- package/packages/dd-trace/src/runtime_metrics/index.js +3 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +3 -0
- package/packages/dd-trace/src/sampler.js +1 -1
- package/packages/dd-trace/src/standalone/index.js +3 -0
- package/packages/dd-trace/src/telemetry/index.js +2 -3
- package/packages/dd-trace/src/telemetry/send-data.js +5 -19
- package/packages/dd-trace/src/telemetry/session-propagation.js +19 -44
- package/packages/dd-trace/src/telemetry/telemetry.js +28 -171
- package/packages/dd-trace/src/util.js +0 -9
|
@@ -108,6 +108,8 @@ class NativeWallProfiler {
|
|
|
108
108
|
#captureSpanData = false
|
|
109
109
|
#codeHotspotsEnabled = false
|
|
110
110
|
#cpuProfilingEnabled = false
|
|
111
|
+
#customLabelsActive = false
|
|
112
|
+
#customLabelKeys
|
|
111
113
|
#endpointCollectionEnabled = false
|
|
112
114
|
#flushIntervalMillis = 0
|
|
113
115
|
#logger
|
|
@@ -245,7 +247,24 @@ class NativeWallProfiler {
|
|
|
245
247
|
// context -- we simply can't tell which one it might've been across all
|
|
246
248
|
// possible async context frames.
|
|
247
249
|
if (this.#asyncContextFrameEnabled) {
|
|
248
|
-
this.#
|
|
250
|
+
if (this.#customLabelsActive) {
|
|
251
|
+
// Custom labels may be active in this async context. The current CPED
|
|
252
|
+
// context could be a 2-element array [profilingContext, customLabels].
|
|
253
|
+
// Replace the profiling context while preserving the custom labels.
|
|
254
|
+
// This flag is monotonic (once set, stays true) because async
|
|
255
|
+
// continuations from runWithLabels can fire at any time after the
|
|
256
|
+
// synchronous runWithLabels call has returned.
|
|
257
|
+
const current = this.#pprof.time.getContext()
|
|
258
|
+
if (Array.isArray(current)) {
|
|
259
|
+
if (current[0] !== sampleContext) {
|
|
260
|
+
this.#pprof.time.setContext([sampleContext, current[1]])
|
|
261
|
+
}
|
|
262
|
+
} else if (current !== sampleContext) {
|
|
263
|
+
this.#pprof.time.setContext(sampleContext)
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
this.#pprof.time.setContext(sampleContext)
|
|
267
|
+
}
|
|
249
268
|
} else {
|
|
250
269
|
const sampleCount = this._profilerState[kSampleCount]
|
|
251
270
|
if (sampleCount !== this._lastSampleCount) {
|
|
@@ -344,6 +363,13 @@ class NativeWallProfiler {
|
|
|
344
363
|
const lowCardinalityLabels = Object.keys(getThreadLabels())
|
|
345
364
|
lowCardinalityLabels.push(TRACE_ENDPOINT_LABEL)
|
|
346
365
|
|
|
366
|
+
// Custom labels are expected to be low-cardinality (e.g. customer tier, region)
|
|
367
|
+
if (this.#customLabelKeys) {
|
|
368
|
+
for (const key of this.#customLabelKeys) {
|
|
369
|
+
lowCardinalityLabels.push(key)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
347
373
|
const profile = this.#pprof.time.stop(restart, this.#boundGenerateLabels, lowCardinalityLabels)
|
|
348
374
|
|
|
349
375
|
if (restart) {
|
|
@@ -383,7 +409,29 @@ class NativeWallProfiler {
|
|
|
383
409
|
return getThreadLabels()
|
|
384
410
|
}
|
|
385
411
|
|
|
386
|
-
|
|
412
|
+
// Native profiler doesn't set context.context for some samples, such as idle samples or when
|
|
413
|
+
// the context was otherwise unavailable when the sample was taken. Note that with ACF, we don't
|
|
414
|
+
// use the "ref" indirection.
|
|
415
|
+
let ref
|
|
416
|
+
let customLabels
|
|
417
|
+
const cctx = context.context
|
|
418
|
+
if (this.#asyncContextFrameEnabled) {
|
|
419
|
+
// When custom labels are active with ACF, context.context is a 2-element array:
|
|
420
|
+
// [profilingContext, customLabels]. Otherwise it's a plain object.
|
|
421
|
+
if (Array.isArray(cctx)) {
|
|
422
|
+
[ref, customLabels] = cctx
|
|
423
|
+
} else {
|
|
424
|
+
ref = cctx
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
ref = cctx?.ref
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Custom labels are spread first so that internal labels always take
|
|
431
|
+
// precedence and overwrite them.
|
|
432
|
+
const labels = customLabels === undefined
|
|
433
|
+
? { ...getThreadLabels() }
|
|
434
|
+
: { ...customLabels, ...getThreadLabels() }
|
|
387
435
|
|
|
388
436
|
if (this.#timelineEnabled) {
|
|
389
437
|
// Incoming timestamps are in microseconds, we emit nanos.
|
|
@@ -395,10 +443,6 @@ class NativeWallProfiler {
|
|
|
395
443
|
labels['async id'] = asyncId
|
|
396
444
|
}
|
|
397
445
|
|
|
398
|
-
// Native profiler doesn't set context.context for some samples, such as idle samples or when
|
|
399
|
-
// the context was otherwise unavailable when the sample was taken. Note that with async context
|
|
400
|
-
// frame, we don't use the "ref" indirection.
|
|
401
|
-
const ref = this.#asyncContextFrameEnabled ? context.context : context.context?.ref
|
|
402
446
|
if (typeof ref !== 'object') {
|
|
403
447
|
return labels
|
|
404
448
|
}
|
|
@@ -421,6 +465,45 @@ class NativeWallProfiler {
|
|
|
421
465
|
return labels
|
|
422
466
|
}
|
|
423
467
|
|
|
468
|
+
/**
|
|
469
|
+
* Sets the custom label keys used for pprof low-cardinality deduplication.
|
|
470
|
+
* Called once by the top-level Profiler when keys are declared.
|
|
471
|
+
*
|
|
472
|
+
* @param {Iterable<string>} keys
|
|
473
|
+
*/
|
|
474
|
+
setCustomLabelKeys (keys) {
|
|
475
|
+
this.#customLabelKeys = keys
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Runs a function with custom profiling labels attached to all wall profiler
|
|
480
|
+
* samples taken during its execution. Labels are key-value pairs that appear
|
|
481
|
+
* in the pprof output and can be used to filter flame graphs in the Datadog UI.
|
|
482
|
+
*
|
|
483
|
+
* Requires AsyncContextFrame (ACF) to be enabled. Supports nesting: inner
|
|
484
|
+
* calls merge labels with outer calls, with inner values taking precedence.
|
|
485
|
+
*
|
|
486
|
+
* @param {Record<string, string | number>} labels - Custom labels to attach
|
|
487
|
+
* @param {function(): T} fn - Function to execute with the labels
|
|
488
|
+
* @returns {T} The return value of fn
|
|
489
|
+
* @template T
|
|
490
|
+
*/
|
|
491
|
+
runWithLabels (labels, fn) {
|
|
492
|
+
if (!this.#asyncContextFrameEnabled || !this.#withContexts) {
|
|
493
|
+
return fn()
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Read current context; merge custom labels if already in a runWithLabels scope
|
|
497
|
+
const current = this.#pprof.time.getContext()
|
|
498
|
+
const isCurrentArray = Array.isArray(current)
|
|
499
|
+
const customLabels = isCurrentArray ? { ...current[1], ...labels } : labels
|
|
500
|
+
|
|
501
|
+
const profilingContext = (isCurrentArray ? current[0] : current) ?? {}
|
|
502
|
+
|
|
503
|
+
this.#customLabelsActive = true
|
|
504
|
+
return this.#pprof.time.runWithContext([profilingContext, customLabels], fn)
|
|
505
|
+
}
|
|
506
|
+
|
|
424
507
|
profile (restart) {
|
|
425
508
|
return this.#stop(restart)
|
|
426
509
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const dc = require('dc-polyfill')
|
|
3
|
+
const dc = /** @type {typeof import('diagnostics_channel')} */ (require('dc-polyfill'))
|
|
4
4
|
const log = require('../log')
|
|
5
5
|
|
|
6
6
|
// If the process lives for at least 30 seconds, it's considered long-lived
|
|
@@ -10,6 +10,9 @@ const DEFAULT_LONG_LIVED_THRESHOLD = 30_000
|
|
|
10
10
|
* This class embodies the SSI profiler-triggering heuristics under SSI.
|
|
11
11
|
*/
|
|
12
12
|
class SSIHeuristics {
|
|
13
|
+
/**
|
|
14
|
+
* @param {import('../config/config-base')} config - Tracer configuration
|
|
15
|
+
*/
|
|
13
16
|
constructor (config) {
|
|
14
17
|
const longLivedThreshold = config.profiling.longLivedThreshold || DEFAULT_LONG_LIVED_THRESHOLD
|
|
15
18
|
if (typeof longLivedThreshold !== 'number' || longLivedThreshold <= 0) {
|
|
@@ -17,11 +17,12 @@ class PropagationHashManager {
|
|
|
17
17
|
_cachedHash = null
|
|
18
18
|
_cachedHashString = null
|
|
19
19
|
_cachedHashBase64 = null
|
|
20
|
+
/** @type {import('../config/config-base') | null} */
|
|
20
21
|
_config = null
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Configure the propagation hash manager with tracer config
|
|
24
|
-
* @param {
|
|
25
|
+
* @param {import('../config/config-base')} config - Tracer configuration
|
|
25
26
|
*/
|
|
26
27
|
configure (config) {
|
|
27
28
|
this._config = config
|
|
@@ -27,9 +27,12 @@ class LazyModule {
|
|
|
27
27
|
this.provider = provider
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
/**
|
|
31
|
+
* @param {import('./config/config-base')} config - Tracer configuration
|
|
32
|
+
*/
|
|
33
|
+
enable (config, ...args) {
|
|
31
34
|
this.module = this.provider()
|
|
32
|
-
this.module.enable(...args)
|
|
35
|
+
this.module.enable(config, ...args)
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
disable () {
|
|
@@ -238,12 +241,16 @@ class Tracer extends NoopProxy {
|
|
|
238
241
|
getDynamicInstrumentationClient(config)
|
|
239
242
|
}
|
|
240
243
|
} catch (e) {
|
|
241
|
-
log.error('Error
|
|
244
|
+
log.error('Error initializing tracer', e)
|
|
245
|
+
// TODO: Should we stop everything started so far?
|
|
242
246
|
}
|
|
243
247
|
|
|
244
248
|
return this
|
|
245
249
|
}
|
|
246
250
|
|
|
251
|
+
/**
|
|
252
|
+
* @param {import('./config/config-base')} config - Tracer configuration
|
|
253
|
+
*/
|
|
247
254
|
_startProfiler (config) {
|
|
248
255
|
// do not stop tracer initialization if the profiler fails to be imported
|
|
249
256
|
try {
|
|
@@ -257,6 +264,9 @@ class Tracer extends NoopProxy {
|
|
|
257
264
|
}
|
|
258
265
|
}
|
|
259
266
|
|
|
267
|
+
/**
|
|
268
|
+
* @param {import('./config/config-base')} config - Tracer configuration
|
|
269
|
+
*/
|
|
260
270
|
#updateTracing (config) {
|
|
261
271
|
if (config.tracing !== false) {
|
|
262
272
|
if (config.appsec.enabled) {
|
|
@@ -330,6 +340,25 @@ class Tracer extends NoopProxy {
|
|
|
330
340
|
}
|
|
331
341
|
}
|
|
332
342
|
|
|
343
|
+
/**
|
|
344
|
+
* @override
|
|
345
|
+
*/
|
|
346
|
+
get profiling () {
|
|
347
|
+
// Lazily require the profiler module and cache the result. If profiling
|
|
348
|
+
// is not enabled, runWithLabels still works as a passthrough (just calls fn()).
|
|
349
|
+
const profilerModule = require('./profiler')
|
|
350
|
+
const profiling = {
|
|
351
|
+
setCustomLabelKeys (keys) {
|
|
352
|
+
profilerModule.setCustomLabelKeys(keys)
|
|
353
|
+
},
|
|
354
|
+
runWithLabels (labels, fn) {
|
|
355
|
+
return profilerModule.runWithLabels(labels, fn)
|
|
356
|
+
},
|
|
357
|
+
}
|
|
358
|
+
Reflect.defineProperty(this, 'profiling', { value: profiling, configurable: true, enumerable: true })
|
|
359
|
+
return profiling
|
|
360
|
+
}
|
|
361
|
+
|
|
333
362
|
/**
|
|
334
363
|
* @override
|
|
335
364
|
*/
|
|
@@ -25,6 +25,9 @@ class RemoteConfig {
|
|
|
25
25
|
#products = new Set()
|
|
26
26
|
#batchHandlers = new Map()
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @param {import('../config/config-base')} config - Tracer configuration
|
|
30
|
+
*/
|
|
28
31
|
constructor (config) {
|
|
29
32
|
const pollInterval = Math.floor(config.remoteConfig.pollInterval * 1000)
|
|
30
33
|
|
|
@@ -21,10 +21,14 @@ function requirePackageJson (name, module) {
|
|
|
21
21
|
}
|
|
22
22
|
for (const modulePath of module.paths) {
|
|
23
23
|
const candidate = path.join(modulePath, name, 'package.json')
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
// fs.existsSync is faster than fs.readFileSync due to not throwing an error if the file does not exist.
|
|
25
|
+
// The race condition should also not matter here as the time window is very small.
|
|
26
|
+
if (fs.existsSync(candidate)) {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(fs.readFileSync(candidate, 'utf8'))
|
|
29
|
+
} catch {
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
28
32
|
}
|
|
29
33
|
}
|
|
30
34
|
throw new Error(`could not find ${name}/package.json`)
|
|
@@ -22,19 +22,44 @@ let patchedRequire = null
|
|
|
22
22
|
const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
|
|
23
23
|
const moduleLoadEndChannel = dc.channel('dd-trace:moduleLoadEnd')
|
|
24
24
|
|
|
25
|
+
function stripNodePrefix (name) {
|
|
26
|
+
if (typeof name !== 'string') return name
|
|
27
|
+
return name.startsWith('node:') ? name.slice(5) : name
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const builtinModules = new Set(Module.builtinModules.map(stripNodePrefix))
|
|
31
|
+
|
|
32
|
+
function isBuiltinModuleName (name) {
|
|
33
|
+
if (typeof name !== 'string') return false
|
|
34
|
+
return builtinModules.has(stripNodePrefix(name))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeModuleName (name) {
|
|
38
|
+
if (typeof name !== 'string') return name
|
|
39
|
+
const stripped = stripNodePrefix(name)
|
|
40
|
+
return builtinModules.has(stripped) ? stripped : name
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @overload
|
|
45
|
+
* @param {string[]} modules list of modules to hook into
|
|
46
|
+
* @param {object} options hook options
|
|
47
|
+
* @param {Function} onrequire callback to be executed upon encountering module
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* @overload
|
|
51
|
+
* @param {string[]} modules list of modules to hook into
|
|
52
|
+
* @param {Function} onrequire callback to be executed upon encountering module
|
|
53
|
+
*/
|
|
25
54
|
function Hook (modules, options, onrequire) {
|
|
26
55
|
if (!(this instanceof Hook)) return new Hook(modules, options, onrequire)
|
|
27
|
-
if (typeof
|
|
28
|
-
onrequire = modules
|
|
29
|
-
modules = null
|
|
30
|
-
options = {}
|
|
31
|
-
} else if (typeof options === 'function') {
|
|
56
|
+
if (typeof options === 'function') {
|
|
32
57
|
onrequire = options
|
|
33
58
|
options = {}
|
|
34
59
|
}
|
|
35
60
|
|
|
36
|
-
modules
|
|
37
|
-
options
|
|
61
|
+
modules ??= []
|
|
62
|
+
options ??= {}
|
|
38
63
|
|
|
39
64
|
this.modules = modules
|
|
40
65
|
this.options = options
|
|
@@ -63,32 +88,34 @@ function Hook (modules, options, onrequire) {
|
|
|
63
88
|
*/
|
|
64
89
|
let filename
|
|
65
90
|
try {
|
|
66
|
-
// @ts-expect-error Module._resolveFilename is not typed
|
|
91
|
+
// @ts-expect-error - Module._resolveFilename is not typed
|
|
67
92
|
filename = Module._resolveFilename(request, this)
|
|
68
93
|
} catch {
|
|
69
94
|
return _origRequire.apply(this, arguments)
|
|
70
95
|
}
|
|
71
|
-
|
|
96
|
+
|
|
97
|
+
const builtin = isBuiltinModuleName(filename)
|
|
98
|
+
const moduleId = builtin ? normalizeModuleName(filename) : filename
|
|
72
99
|
let name, basedir, hooks
|
|
73
100
|
// return known patched modules immediately
|
|
74
|
-
if (cache[
|
|
75
|
-
const externalCacheEntry = require.cache[filename]
|
|
101
|
+
if (cache[moduleId]) {
|
|
76
102
|
// require.cache was potentially altered externally
|
|
77
|
-
|
|
78
|
-
|
|
103
|
+
const cacheEntry = require.cache[filename]
|
|
104
|
+
if (cacheEntry && cacheEntry.exports !== cache[filename].original) {
|
|
105
|
+
return cacheEntry.exports
|
|
79
106
|
}
|
|
80
107
|
|
|
81
|
-
return cache[
|
|
108
|
+
return cache[moduleId].exports
|
|
82
109
|
}
|
|
83
110
|
|
|
84
111
|
// Check if this module has a patcher in-progress already.
|
|
85
112
|
// Otherwise, mark this module as patching in-progress.
|
|
86
|
-
const patched = patching[
|
|
113
|
+
const patched = patching[moduleId]
|
|
87
114
|
if (patched) {
|
|
88
115
|
// If it's already patched, just return it as-is.
|
|
89
116
|
return origRequire.apply(this, arguments)
|
|
90
117
|
}
|
|
91
|
-
patching[
|
|
118
|
+
patching[moduleId] = true
|
|
92
119
|
|
|
93
120
|
const payload = {
|
|
94
121
|
filename,
|
|
@@ -107,12 +134,12 @@ function Hook (modules, options, onrequire) {
|
|
|
107
134
|
|
|
108
135
|
// The module has already been loaded,
|
|
109
136
|
// so the patching mark can be cleaned up.
|
|
110
|
-
delete patching[
|
|
137
|
+
delete patching[moduleId]
|
|
111
138
|
|
|
112
|
-
if (
|
|
113
|
-
hooks = moduleHooks[
|
|
139
|
+
if (builtin) {
|
|
140
|
+
hooks = moduleHooks[moduleId]
|
|
114
141
|
if (!hooks) return exports // abort if module name isn't on whitelist
|
|
115
|
-
name =
|
|
142
|
+
name = moduleId
|
|
116
143
|
} else {
|
|
117
144
|
const inAWSLambda = getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME') !== undefined
|
|
118
145
|
const hasLambdaHandler = getValueFromEnvSources('DD_LAMBDA_HANDLER') !== undefined
|
|
@@ -129,7 +156,8 @@ function Hook (modules, options, onrequire) {
|
|
|
129
156
|
hooks = moduleHooks[name]
|
|
130
157
|
if (!hooks) return exports // abort if module name isn't on whitelist
|
|
131
158
|
|
|
132
|
-
//
|
|
159
|
+
// figure out if this is the main module file, or a file inside the module
|
|
160
|
+
// @ts-expect-error - Module._resolveLookupPaths is meant to be internal and is not typed
|
|
133
161
|
const paths = Module._resolveLookupPaths(name, this, true)
|
|
134
162
|
if (!paths) {
|
|
135
163
|
// abort if _resolveLookupPaths return null
|
|
@@ -138,7 +166,7 @@ function Hook (modules, options, onrequire) {
|
|
|
138
166
|
|
|
139
167
|
let res
|
|
140
168
|
try {
|
|
141
|
-
// @ts-expect-error Module._findPath is not typed
|
|
169
|
+
// @ts-expect-error - Module._findPath is meant to be internal and is not typed
|
|
142
170
|
res = Module._findPath(name, [basedir, ...paths])
|
|
143
171
|
} catch {
|
|
144
172
|
// case where the file specified in package.json "main" doesn't exist
|
|
@@ -163,17 +191,21 @@ function Hook (modules, options, onrequire) {
|
|
|
163
191
|
|
|
164
192
|
// ensure that the cache entry is assigned a value before calling
|
|
165
193
|
// onrequire, in case calling onrequire requires the same module.
|
|
166
|
-
cache[
|
|
167
|
-
cache[
|
|
194
|
+
cache[moduleId] = { exports }
|
|
195
|
+
cache[moduleId].original = exports
|
|
168
196
|
|
|
169
197
|
for (const hook of hooks) {
|
|
170
|
-
cache[
|
|
198
|
+
cache[moduleId].exports = hook(cache[moduleId].exports, name, basedir)
|
|
171
199
|
}
|
|
172
200
|
|
|
173
|
-
return cache[
|
|
201
|
+
return cache[moduleId].exports
|
|
174
202
|
}
|
|
175
203
|
}
|
|
176
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Reset the Ritm hook. This is used to reset the hook after a test.
|
|
207
|
+
* TODO: Remove this and instead use proxyquire to reset the hook.
|
|
208
|
+
*/
|
|
177
209
|
Hook.reset = function () {
|
|
178
210
|
Module.prototype.require = origRequire
|
|
179
211
|
patchedRequire = null
|
|
@@ -35,6 +35,9 @@ let eventLoopDelayObserver = null
|
|
|
35
35
|
// https://github.com/DataDog/dogweb/blob/prod/integration/node/node_metadata.csv
|
|
36
36
|
|
|
37
37
|
module.exports = {
|
|
38
|
+
/**
|
|
39
|
+
* @param {import('../config/config-base')} config - Tracer configuration
|
|
40
|
+
*/
|
|
38
41
|
start (config) {
|
|
39
42
|
this.stop()
|
|
40
43
|
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
@@ -42,7 +42,7 @@ class Sampler {
|
|
|
42
42
|
/**
|
|
43
43
|
* Determines whether a trace/span should be sampled based on the configured sampling rate.
|
|
44
44
|
*
|
|
45
|
-
* @param {Span|SpanContext} span - The span or span context to evaluate.
|
|
45
|
+
* @param {import("../../..").Span|import("../../..").SpanContext} span - The span or span context to evaluate.
|
|
46
46
|
* @returns {boolean} `true` if the trace/span should be sampled, otherwise `false`.
|
|
47
47
|
*/
|
|
48
48
|
isSampled (span) {
|
|
@@ -11,6 +11,9 @@ const startCh = channel('dd-trace:span:start')
|
|
|
11
11
|
const injectCh = channel('dd-trace:span:inject')
|
|
12
12
|
const extractCh = channel('dd-trace:span:extract')
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @param {import('../config/config-base')} config - Tracer configuration
|
|
16
|
+
*/
|
|
14
17
|
function configure (config) {
|
|
15
18
|
if (startCh.hasSubscribers) startCh.unsubscribe(onSpanStart)
|
|
16
19
|
if (injectCh.hasSubscribers) injectCh.unsubscribe(onSpanInject)
|
|
@@ -5,15 +5,14 @@ let telemetry
|
|
|
5
5
|
// Lazy load the telemetry module to avoid the performance impact of loading it unconditionally
|
|
6
6
|
module.exports = {
|
|
7
7
|
start (config, ...args) {
|
|
8
|
+
if (!config.telemetry.enabled) return
|
|
8
9
|
telemetry ??= require('./telemetry')
|
|
9
10
|
telemetry.start(config, ...args)
|
|
10
11
|
},
|
|
11
|
-
stop () {
|
|
12
|
-
telemetry?.stop()
|
|
13
|
-
},
|
|
14
12
|
// This might be called before `start` so we have to trigger loading the
|
|
15
13
|
// underlying module here as well.
|
|
16
14
|
updateConfig (changes, config, ...args) {
|
|
15
|
+
if (!config.telemetry.enabled) return
|
|
17
16
|
telemetry ??= require('./telemetry')
|
|
18
17
|
telemetry.updateConfig(changes, config, ...args)
|
|
19
18
|
},
|
|
@@ -62,19 +62,6 @@ const { getValueFromEnvSources } = require('../config/helper')
|
|
|
62
62
|
* kernel_name?: string
|
|
63
63
|
* } & Record<string, unknown>} TelemetryHost
|
|
64
64
|
*/
|
|
65
|
-
/**
|
|
66
|
-
* @typedef {{
|
|
67
|
-
* hostname?: string,
|
|
68
|
-
* port?: string | number,
|
|
69
|
-
* url?: string | URL,
|
|
70
|
-
* site?: string,
|
|
71
|
-
* apiKey?: string,
|
|
72
|
-
* isCiVisibility?: boolean,
|
|
73
|
-
* spanAttributeSchema?: string,
|
|
74
|
-
* tags: Record<string, string>,
|
|
75
|
-
* telemetry?: { debug?: boolean }
|
|
76
|
-
* }} TelemetryConfig
|
|
77
|
-
*/
|
|
78
65
|
/**
|
|
79
66
|
* @callback SendDataCallback
|
|
80
67
|
* @param {Error | null | undefined} error
|
|
@@ -85,23 +72,22 @@ const { getValueFromEnvSources } = require('../config/helper')
|
|
|
85
72
|
let agentTelemetry = true
|
|
86
73
|
|
|
87
74
|
/**
|
|
88
|
-
* @param {
|
|
75
|
+
* @param {import('../config/config-base')} config
|
|
89
76
|
* @param {TelemetryApplication} application
|
|
90
77
|
* @param {TelemetryRequestType} reqType
|
|
91
78
|
* @returns {Record<string, string>}
|
|
92
79
|
*/
|
|
93
80
|
function getHeaders (config, application, reqType) {
|
|
94
|
-
const sessionId = config.tags['runtime-id']
|
|
95
81
|
const headers = {
|
|
96
82
|
'content-type': 'application/json',
|
|
97
83
|
'dd-telemetry-api-version': 'v2',
|
|
98
84
|
'dd-telemetry-request-type': reqType,
|
|
99
85
|
'dd-client-library-language': application.language_name,
|
|
100
86
|
'dd-client-library-version': application.tracer_version,
|
|
101
|
-
'dd-session-id':
|
|
87
|
+
'dd-session-id': config.tags['runtime-id'],
|
|
102
88
|
}
|
|
103
|
-
if (config.
|
|
104
|
-
headers['dd-root-session-id'] = config.
|
|
89
|
+
if (config.DD_ROOT_JS_SESSION_ID) {
|
|
90
|
+
headers['dd-root-session-id'] = config.DD_ROOT_JS_SESSION_ID
|
|
105
91
|
}
|
|
106
92
|
const debug = config.telemetry && config.telemetry.debug
|
|
107
93
|
if (debug) {
|
|
@@ -141,7 +127,7 @@ function getPayload (payload) {
|
|
|
141
127
|
|
|
142
128
|
// TODO(BridgeAR): Simplify this code. A lot does not need to be recalculated on every call.
|
|
143
129
|
/**
|
|
144
|
-
* @param {
|
|
130
|
+
* @param {import('../config/config-base')} config
|
|
145
131
|
* @param {TelemetryApplication} application
|
|
146
132
|
* @param {TelemetryHost} host
|
|
147
133
|
* @param {TelemetryRequestType} reqType
|
|
@@ -1,53 +1,37 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const dc = require('dc-polyfill')
|
|
4
|
-
|
|
3
|
+
const dc = /** @type {typeof import('diagnostics_channel')} */ (require('dc-polyfill'))
|
|
5
4
|
const childProcessChannel = dc.tracingChannel('datadog:child_process:execution')
|
|
6
5
|
|
|
7
6
|
let subscribed = false
|
|
8
|
-
let rootSessionId
|
|
9
7
|
let runtimeId
|
|
10
8
|
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
const base = existingEnv == null ? process.env : existingEnv
|
|
14
|
-
return {
|
|
15
|
-
...base,
|
|
16
|
-
DD_ROOT_JS_SESSION_ID: rootSessionId,
|
|
17
|
-
DD_PARENT_JS_SESSION_ID: runtimeId,
|
|
18
|
-
}
|
|
9
|
+
function isOptionsObject (value) {
|
|
10
|
+
return value != null && typeof value === 'object' && !Array.isArray(value) && value
|
|
19
11
|
}
|
|
20
12
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
if (args[1] != null && typeof args[1] === 'object') {
|
|
26
|
-
return { index: 1, exists: true }
|
|
27
|
-
}
|
|
28
|
-
if (!shell && args[2] != null && typeof args[2] === 'object') {
|
|
29
|
-
return { index: 2, exists: true }
|
|
30
|
-
}
|
|
31
|
-
return { index: shell ? 1 : 2, exists: false }
|
|
13
|
+
function getEnvWithRuntimeId (env) {
|
|
14
|
+
// eslint-disable-next-line eslint-rules/eslint-process-env
|
|
15
|
+
return { ...(env ?? process.env), DD_ROOT_JS_SESSION_ID: runtimeId }
|
|
32
16
|
}
|
|
33
17
|
|
|
34
18
|
function onChildProcessStart (context) {
|
|
35
|
-
if (!context.callArgs) return
|
|
36
|
-
|
|
37
19
|
const args = context.callArgs
|
|
38
|
-
|
|
20
|
+
if (!args) return
|
|
39
21
|
|
|
40
|
-
|
|
41
|
-
|
|
22
|
+
const index = Array.isArray(args[1]) || (!context.shell && !isOptionsObject(args[1])) ? 2 : 1
|
|
23
|
+
const options = isOptionsObject(args[index]) ? args[index] : undefined
|
|
24
|
+
|
|
25
|
+
if (options) {
|
|
26
|
+
args[index] = { ...options, env: getEnvWithRuntimeId(options.env) }
|
|
42
27
|
return
|
|
43
28
|
}
|
|
44
29
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (!context.shell && !Array.isArray(args[1])) {
|
|
30
|
+
if (index === 2 && !Array.isArray(args[1])) {
|
|
48
31
|
args.splice(1, 0, [])
|
|
49
32
|
}
|
|
50
33
|
|
|
34
|
+
const opts = { env: getEnvWithRuntimeId() }
|
|
51
35
|
if (typeof args[index] === 'function') {
|
|
52
36
|
args.splice(index, 0, opts)
|
|
53
37
|
} else {
|
|
@@ -55,24 +39,15 @@ function onChildProcessStart (context) {
|
|
|
55
39
|
}
|
|
56
40
|
}
|
|
57
41
|
|
|
58
|
-
const handler = { start: onChildProcessStart }
|
|
59
|
-
|
|
60
42
|
function start (config) {
|
|
61
43
|
if (!config.telemetry?.enabled || subscribed) return
|
|
62
44
|
subscribed = true
|
|
63
45
|
|
|
64
|
-
|
|
65
|
-
runtimeId = config.tags['runtime-id']
|
|
66
|
-
|
|
67
|
-
childProcessChannel.subscribe(handler)
|
|
68
|
-
}
|
|
46
|
+
runtimeId = config.DD_ROOT_JS_SESSION_ID || config.tags['runtime-id']
|
|
69
47
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
subscribed = false
|
|
74
|
-
rootSessionId = undefined
|
|
75
|
-
runtimeId = undefined
|
|
48
|
+
childProcessChannel.subscribe(
|
|
49
|
+
/** @type {import('diagnostics_channel').TracingChannelSubscribers<object>} */ ({ start: onChildProcessStart })
|
|
50
|
+
)
|
|
76
51
|
}
|
|
77
52
|
|
|
78
|
-
module.exports = { start
|
|
53
|
+
module.exports = { start }
|