dd-trace 4.46.0 → 4.47.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 +2 -0
- package/index.d.ts +20 -8
- package/package.json +9 -3
- package/packages/datadog-instrumentations/src/cucumber.js +290 -53
- package/packages/datadog-instrumentations/src/jest.js +3 -1
- package/packages/datadog-instrumentations/src/kafkajs.js +67 -31
- package/packages/datadog-instrumentations/src/microgateway-core.js +3 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +139 -54
- package/packages/datadog-instrumentations/src/mocha/utils.js +35 -15
- package/packages/datadog-instrumentations/src/mocha/worker.js +29 -1
- package/packages/datadog-instrumentations/src/openai.js +4 -2
- package/packages/datadog-instrumentations/src/pg.js +59 -4
- package/packages/datadog-instrumentations/src/vitest.js +184 -9
- package/packages/datadog-plugin-amqplib/src/consumer.js +1 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +33 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +24 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +36 -6
- package/packages/datadog-plugin-cypress/src/support.js +4 -1
- package/packages/datadog-plugin-http/src/client.js +1 -42
- package/packages/datadog-plugin-http2/src/client.js +1 -26
- package/packages/datadog-plugin-jest/src/index.js +17 -1
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +20 -0
- package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/index.js +3 -1
- package/packages/datadog-plugin-mocha/src/index.js +18 -0
- package/packages/datadog-plugin-openai/src/index.js +27 -18
- package/packages/datadog-plugin-playwright/src/index.js +9 -0
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -3
- package/packages/datadog-plugin-vitest/src/index.js +68 -3
- package/packages/dd-trace/src/appsec/addresses.js +3 -1
- package/packages/dd-trace/src/appsec/channels.js +4 -2
- package/packages/dd-trace/src/appsec/rasp/index.js +103 -0
- package/packages/dd-trace/src/appsec/rasp/sql_injection.js +86 -0
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +37 -0
- package/packages/dd-trace/src/appsec/rasp/utils.js +63 -0
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +16 -7
- package/packages/dd-trace/src/appsec/remote_config/manager.js +89 -51
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +33 -14
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +2 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -0
- package/packages/dd-trace/src/config.js +61 -10
- package/packages/dd-trace/src/constants.js +11 -1
- package/packages/dd-trace/src/data_streams_context.js +3 -0
- package/packages/dd-trace/src/datastreams/fnv.js +23 -0
- package/packages/dd-trace/src/datastreams/pathway.js +12 -5
- package/packages/dd-trace/src/datastreams/processor.js +35 -0
- package/packages/dd-trace/src/datastreams/schemas/schema.js +8 -0
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +125 -0
- package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +29 -0
- package/packages/dd-trace/src/debugger/devtools_client/config.js +24 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +57 -0
- package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +23 -0
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +164 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +28 -0
- package/packages/dd-trace/src/debugger/devtools_client/session.js +7 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +47 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +109 -0
- package/packages/dd-trace/src/debugger/index.js +92 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +29 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/payload-tagging/config/aws.json +30 -0
- package/packages/dd-trace/src/payload-tagging/config/index.js +30 -0
- package/packages/dd-trace/src/payload-tagging/index.js +93 -0
- package/packages/dd-trace/src/payload-tagging/tagging.js +83 -0
- package/packages/dd-trace/src/plugin_manager.js +11 -10
- package/packages/dd-trace/src/plugins/ci_plugin.js +33 -8
- package/packages/dd-trace/src/plugins/util/env.js +5 -2
- package/packages/dd-trace/src/plugins/util/test.js +26 -2
- package/packages/dd-trace/src/profiling/config.js +5 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns.js +13 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +16 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +16 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +24 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +16 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +48 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/net.js +24 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +108 -32
- package/packages/dd-trace/src/profiling/profilers/shared.js +5 -0
- package/packages/dd-trace/src/profiling/profilers/wall.js +9 -3
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +10 -2
- package/packages/dd-trace/src/proxy.js +10 -3
- package/packages/dd-trace/src/span_stats.js +4 -2
- package/packages/dd-trace/src/appsec/rasp.js +0 -176
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
const os = require('os')
|
|
5
5
|
const uuid = require('crypto-randomuuid') // we need to keep the old uuid dep because of cypress
|
|
6
|
-
const URL = require('url')
|
|
6
|
+
const { URL } = require('url')
|
|
7
7
|
const log = require('./log')
|
|
8
8
|
const pkg = require('./pkg')
|
|
9
9
|
const coalesce = require('koalas')
|
|
@@ -18,6 +18,7 @@ const { updateConfig } = require('./telemetry')
|
|
|
18
18
|
const telemetryMetrics = require('./telemetry/metrics')
|
|
19
19
|
const { getIsGCPFunction, getIsAzureFunction } = require('./serverless')
|
|
20
20
|
const { ORIGIN_KEY } = require('./constants')
|
|
21
|
+
const { appendRules } = require('./payload-tagging/config')
|
|
21
22
|
|
|
22
23
|
const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
|
|
23
24
|
|
|
@@ -173,6 +174,21 @@ function validateNamingVersion (versionString) {
|
|
|
173
174
|
return versionString
|
|
174
175
|
}
|
|
175
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Given a string of comma-separated paths, return the array of paths.
|
|
179
|
+
* If a blank path is provided a null is returned to signal that the feature is disabled.
|
|
180
|
+
* An empty array means the feature is enabled but that no rules need to be applied.
|
|
181
|
+
*
|
|
182
|
+
* @param {string} input
|
|
183
|
+
* @returns {[string]|null}
|
|
184
|
+
*/
|
|
185
|
+
function splitJSONPathRules (input) {
|
|
186
|
+
if (!input) return null
|
|
187
|
+
if (Array.isArray(input)) return input
|
|
188
|
+
if (input === 'all') return []
|
|
189
|
+
return input.split(',')
|
|
190
|
+
}
|
|
191
|
+
|
|
176
192
|
// Shallow clone with property name remapping
|
|
177
193
|
function remapify (input, mappings) {
|
|
178
194
|
if (!input) return
|
|
@@ -281,6 +297,26 @@ class Config {
|
|
|
281
297
|
null
|
|
282
298
|
)
|
|
283
299
|
|
|
300
|
+
const DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING = splitJSONPathRules(
|
|
301
|
+
coalesce(
|
|
302
|
+
process.env.DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING,
|
|
303
|
+
options.cloudPayloadTagging?.request,
|
|
304
|
+
''
|
|
305
|
+
))
|
|
306
|
+
|
|
307
|
+
const DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING = splitJSONPathRules(
|
|
308
|
+
coalesce(
|
|
309
|
+
process.env.DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING,
|
|
310
|
+
options.cloudPayloadTagging?.response,
|
|
311
|
+
''
|
|
312
|
+
))
|
|
313
|
+
|
|
314
|
+
const DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH = coalesce(
|
|
315
|
+
process.env.DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH,
|
|
316
|
+
options.cloudPayloadTagging?.maxDepth,
|
|
317
|
+
10
|
|
318
|
+
)
|
|
319
|
+
|
|
284
320
|
// TODO: refactor
|
|
285
321
|
this.apiKey = DD_API_KEY
|
|
286
322
|
|
|
@@ -291,6 +327,15 @@ class Config {
|
|
|
291
327
|
type: DD_INSTRUMENTATION_INSTALL_TYPE
|
|
292
328
|
}
|
|
293
329
|
|
|
330
|
+
this.cloudPayloadTagging = {
|
|
331
|
+
requestsEnabled: !!DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING,
|
|
332
|
+
responsesEnabled: !!DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING,
|
|
333
|
+
maxDepth: DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH,
|
|
334
|
+
rules: appendRules(
|
|
335
|
+
DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING, DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
|
|
294
339
|
this._applyDefaults()
|
|
295
340
|
this._applyEnvironment()
|
|
296
341
|
this._applyOptions(options)
|
|
@@ -423,6 +468,7 @@ class Config {
|
|
|
423
468
|
this._setValue(defaults, 'dogstatsd.hostname', '127.0.0.1')
|
|
424
469
|
this._setValue(defaults, 'dogstatsd.port', '8125')
|
|
425
470
|
this._setValue(defaults, 'dsmEnabled', false)
|
|
471
|
+
this._setValue(defaults, 'dynamicInstrumentationEnabled', false)
|
|
426
472
|
this._setValue(defaults, 'env', undefined)
|
|
427
473
|
this._setValue(defaults, 'experimental.enableGetRumData', false)
|
|
428
474
|
this._setValue(defaults, 'experimental.exporter', undefined)
|
|
@@ -451,6 +497,7 @@ class Config {
|
|
|
451
497
|
this._setValue(defaults, 'isGitUploadEnabled', false)
|
|
452
498
|
this._setValue(defaults, 'isIntelligentTestRunnerEnabled', false)
|
|
453
499
|
this._setValue(defaults, 'isManualApiEnabled', false)
|
|
500
|
+
this._setValue(defaults, 'ciVisibilityTestSessionName', '')
|
|
454
501
|
this._setValue(defaults, 'logInjection', false)
|
|
455
502
|
this._setValue(defaults, 'lookup', undefined)
|
|
456
503
|
this._setValue(defaults, 'memcachedCommandEnabled', false)
|
|
@@ -528,6 +575,7 @@ class Config {
|
|
|
528
575
|
DD_DBM_PROPAGATION_MODE,
|
|
529
576
|
DD_DOGSTATSD_HOSTNAME,
|
|
530
577
|
DD_DOGSTATSD_PORT,
|
|
578
|
+
DD_DYNAMIC_INSTRUMENTATION_ENABLED,
|
|
531
579
|
DD_ENV,
|
|
532
580
|
DD_EXPERIMENTAL_API_SECURITY_ENABLED,
|
|
533
581
|
DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED,
|
|
@@ -657,6 +705,7 @@ class Config {
|
|
|
657
705
|
this._setString(env, 'dogstatsd.hostname', DD_DOGSTATSD_HOSTNAME)
|
|
658
706
|
this._setString(env, 'dogstatsd.port', DD_DOGSTATSD_PORT)
|
|
659
707
|
this._setBoolean(env, 'dsmEnabled', DD_DATA_STREAMS_ENABLED)
|
|
708
|
+
this._setBoolean(env, 'dynamicInstrumentationEnabled', DD_DYNAMIC_INSTRUMENTATION_ENABLED)
|
|
660
709
|
this._setString(env, 'env', DD_ENV || tags.env)
|
|
661
710
|
this._setBoolean(env, 'experimental.enableGetRumData', DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED)
|
|
662
711
|
this._setString(env, 'experimental.exporter', DD_TRACE_EXPERIMENTAL_EXPORTER)
|
|
@@ -824,11 +873,11 @@ class Config {
|
|
|
824
873
|
this._setString(opts, 'dogstatsd.port', options.dogstatsd.port)
|
|
825
874
|
}
|
|
826
875
|
this._setBoolean(opts, 'dsmEnabled', options.dsmEnabled)
|
|
876
|
+
this._setBoolean(opts, 'dynamicInstrumentationEnabled', options.experimental?.dynamicInstrumentationEnabled)
|
|
827
877
|
this._setString(opts, 'env', options.env || tags.env)
|
|
828
|
-
this._setBoolean(opts, 'experimental.enableGetRumData',
|
|
829
|
-
|
|
830
|
-
this.
|
|
831
|
-
this._setBoolean(opts, 'experimental.runtimeId', options.experimental && options.experimental.runtimeId)
|
|
878
|
+
this._setBoolean(opts, 'experimental.enableGetRumData', options.experimental?.enableGetRumData)
|
|
879
|
+
this._setString(opts, 'experimental.exporter', options.experimental?.exporter)
|
|
880
|
+
this._setBoolean(opts, 'experimental.runtimeId', options.experimental?.runtimeId)
|
|
832
881
|
this._setValue(opts, 'flushInterval', maybeInt(options.flushInterval))
|
|
833
882
|
this._optsUnprocessed.flushInterval = options.flushInterval
|
|
834
883
|
this._setValue(opts, 'flushMinSpans', maybeInt(options.flushMinSpans))
|
|
@@ -954,10 +1003,10 @@ class Config {
|
|
|
954
1003
|
}
|
|
955
1004
|
|
|
956
1005
|
_isCiVisibilityManualApiEnabled () {
|
|
957
|
-
return
|
|
1006
|
+
return coalesce(
|
|
958
1007
|
process.env.DD_CIVISIBILITY_MANUAL_API_ENABLED,
|
|
959
|
-
|
|
960
|
-
)
|
|
1008
|
+
true
|
|
1009
|
+
)
|
|
961
1010
|
}
|
|
962
1011
|
|
|
963
1012
|
_isTraceStatsComputationEnabled () {
|
|
@@ -985,7 +1034,8 @@ class Config {
|
|
|
985
1034
|
DD_CIVISIBILITY_AGENTLESS_URL,
|
|
986
1035
|
DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED,
|
|
987
1036
|
DD_CIVISIBILITY_FLAKY_RETRY_ENABLED,
|
|
988
|
-
DD_CIVISIBILITY_FLAKY_RETRY_COUNT
|
|
1037
|
+
DD_CIVISIBILITY_FLAKY_RETRY_COUNT,
|
|
1038
|
+
DD_TEST_SESSION_NAME
|
|
989
1039
|
} = process.env
|
|
990
1040
|
|
|
991
1041
|
if (DD_CIVISIBILITY_AGENTLESS_URL) {
|
|
@@ -1000,7 +1050,8 @@ class Config {
|
|
|
1000
1050
|
coalesce(DD_CIVISIBILITY_FLAKY_RETRY_ENABLED, true))
|
|
1001
1051
|
this._setValue(calc, 'flakyTestRetriesCount', coalesce(maybeInt(DD_CIVISIBILITY_FLAKY_RETRY_COUNT), 5))
|
|
1002
1052
|
this._setBoolean(calc, 'isIntelligentTestRunnerEnabled', isTrue(this._isCiVisibilityItrEnabled()))
|
|
1003
|
-
this._setBoolean(calc, 'isManualApiEnabled', this._isCiVisibilityManualApiEnabled())
|
|
1053
|
+
this._setBoolean(calc, 'isManualApiEnabled', !isFalse(this._isCiVisibilityManualApiEnabled()))
|
|
1054
|
+
this._setString(calc, 'ciVisibilityTestSessionName', DD_TEST_SESSION_NAME)
|
|
1004
1055
|
}
|
|
1005
1056
|
this._setString(calc, 'dogstatsd.hostname', this._getHostname())
|
|
1006
1057
|
this._setBoolean(calc, 'isGitUploadEnabled',
|
|
@@ -34,5 +34,15 @@ module.exports = {
|
|
|
34
34
|
SCI_REPOSITORY_URL: '_dd.git.repository_url',
|
|
35
35
|
SCI_COMMIT_SHA: '_dd.git.commit.sha',
|
|
36
36
|
APM_TRACING_ENABLED_KEY: '_dd.apm.enabled',
|
|
37
|
-
APPSEC_PROPAGATION_KEY: '_dd.p.appsec'
|
|
37
|
+
APPSEC_PROPAGATION_KEY: '_dd.p.appsec',
|
|
38
|
+
PAYLOAD_TAG_REQUEST_PREFIX: 'aws.request.body',
|
|
39
|
+
PAYLOAD_TAG_RESPONSE_PREFIX: 'aws.response.body',
|
|
40
|
+
PAYLOAD_TAGGING_MAX_TAGS: 758,
|
|
41
|
+
SCHEMA_DEFINITION: 'schema.definition',
|
|
42
|
+
SCHEMA_WEIGHT: 'schema.weight',
|
|
43
|
+
SCHEMA_TYPE: 'schema.type',
|
|
44
|
+
SCHEMA_ID: 'schema.id',
|
|
45
|
+
SCHEMA_TOPIC: 'schema.topic',
|
|
46
|
+
SCHEMA_OPERATION: 'schema.operation',
|
|
47
|
+
SCHEMA_NAME: 'schema.name'
|
|
38
48
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { storage } = require('../../datadog-core')
|
|
2
|
+
const log = require('./log')
|
|
2
3
|
|
|
3
4
|
function getDataStreamsContext () {
|
|
4
5
|
const store = storage.getStore()
|
|
@@ -6,6 +7,8 @@ function getDataStreamsContext () {
|
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
function setDataStreamsContext (dataStreamsContext) {
|
|
10
|
+
log.debug(() => `Setting new DSM Context: ${JSON.stringify(dataStreamsContext)}.`)
|
|
11
|
+
|
|
9
12
|
if (dataStreamsContext) storage.enterWith({ ...(storage.getStore()), dataStreamsContext })
|
|
10
13
|
}
|
|
11
14
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const FNV_64_PRIME = BigInt('0x100000001B3')
|
|
2
|
+
const FNV1_64_INIT = BigInt('0xCBF29CE484222325')
|
|
3
|
+
|
|
4
|
+
function fnv (data, hvalInit, fnvPrime, fnvSize) {
|
|
5
|
+
let hval = hvalInit
|
|
6
|
+
for (const byte of data) {
|
|
7
|
+
hval = (hval * fnvPrime) % fnvSize
|
|
8
|
+
hval = hval ^ BigInt(byte)
|
|
9
|
+
}
|
|
10
|
+
return hval
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function fnv64 (data) {
|
|
14
|
+
if (!Buffer.isBuffer(data)) {
|
|
15
|
+
data = Buffer.from(data, 'utf-8')
|
|
16
|
+
}
|
|
17
|
+
const byteArray = new Uint8Array(data)
|
|
18
|
+
return fnv(byteArray, FNV1_64_INIT, FNV_64_PRIME, BigInt(2) ** BigInt(64))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
fnv64
|
|
23
|
+
}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
const crypto = require('crypto')
|
|
5
5
|
const { encodeVarint, decodeVarint } = require('./encoding')
|
|
6
6
|
const LRUCache = require('lru-cache')
|
|
7
|
+
const log = require('../log')
|
|
8
|
+
const pick = require('../../../datadog-core/src/utils/src/pick')
|
|
7
9
|
|
|
8
10
|
const options = { max: 500 }
|
|
9
11
|
const cache = new LRUCache(options)
|
|
@@ -11,6 +13,8 @@ const cache = new LRUCache(options)
|
|
|
11
13
|
const CONTEXT_PROPAGATION_KEY = 'dd-pathway-ctx'
|
|
12
14
|
const CONTEXT_PROPAGATION_KEY_BASE64 = 'dd-pathway-ctx-base64'
|
|
13
15
|
|
|
16
|
+
const logKeys = [CONTEXT_PROPAGATION_KEY, CONTEXT_PROPAGATION_KEY_BASE64]
|
|
17
|
+
|
|
14
18
|
function shaHash (checkpointString) {
|
|
15
19
|
const hash = crypto.createHash('md5').update(checkpointString).digest('hex').slice(0, 16)
|
|
16
20
|
return Buffer.from(hash, 'hex')
|
|
@@ -80,9 +84,13 @@ class DsmPathwayCodec {
|
|
|
80
84
|
return
|
|
81
85
|
}
|
|
82
86
|
carrier[CONTEXT_PROPAGATION_KEY_BASE64] = encodePathwayContextBase64(dataStreamsContext)
|
|
87
|
+
|
|
88
|
+
log.debug(() => `Injected into DSM carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
static decode (carrier) {
|
|
92
|
+
log.debug(() => `Attempting extract from DSM carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
|
|
93
|
+
|
|
86
94
|
if (carrier == null) return
|
|
87
95
|
|
|
88
96
|
let ctx
|
|
@@ -97,13 +105,12 @@ class DsmPathwayCodec {
|
|
|
97
105
|
// pass
|
|
98
106
|
}
|
|
99
107
|
// cover case where base64 context was received under wrong key
|
|
100
|
-
if (!ctx
|
|
108
|
+
if (!ctx && CONTEXT_PROPAGATION_KEY in carrier) {
|
|
109
|
+
ctx = decodePathwayContextBase64(carrier[CONTEXT_PROPAGATION_KEY])
|
|
110
|
+
}
|
|
101
111
|
}
|
|
102
|
-
return ctx
|
|
103
|
-
}
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
return CONTEXT_PROPAGATION_KEY_BASE64 in carrier || CONTEXT_PROPAGATION_KEY in carrier
|
|
113
|
+
return ctx
|
|
107
114
|
}
|
|
108
115
|
}
|
|
109
116
|
|
|
@@ -9,6 +9,9 @@ const { DataStreamsWriter } = require('./writer')
|
|
|
9
9
|
const { computePathwayHash } = require('./pathway')
|
|
10
10
|
const { types } = require('util')
|
|
11
11
|
const { PATHWAY_HASH } = require('../../../../ext/tags')
|
|
12
|
+
const { SchemaBuilder } = require('./schemas/schema_builder')
|
|
13
|
+
const { SchemaSampler } = require('./schemas/schema_sampler')
|
|
14
|
+
const log = require('../log')
|
|
12
15
|
|
|
13
16
|
const ENTRY_PARENT_HASH = Buffer.from('0000000000000000', 'hex')
|
|
14
17
|
|
|
@@ -194,6 +197,7 @@ class DataStreamsProcessor {
|
|
|
194
197
|
this.version = version || ''
|
|
195
198
|
this.sequence = 0
|
|
196
199
|
this.flushInterval = flushInterval
|
|
200
|
+
this._schemaSamplers = {}
|
|
197
201
|
|
|
198
202
|
if (this.enabled) {
|
|
199
203
|
this.timer = setInterval(this.onInterval.bind(this), flushInterval)
|
|
@@ -269,6 +273,11 @@ class DataStreamsProcessor {
|
|
|
269
273
|
closestOppositeDirectionHash = parentHash
|
|
270
274
|
closestOppositeDirectionEdgeStart = edgeStartNs
|
|
271
275
|
}
|
|
276
|
+
log.debug(
|
|
277
|
+
() => `Setting DSM Checkpoint from extracted parent context with hash: ${parentHash} and edge tags: ${edgeTags}`
|
|
278
|
+
)
|
|
279
|
+
} else {
|
|
280
|
+
log.debug(() => 'Setting DSM Checkpoint with empty parent context.')
|
|
272
281
|
}
|
|
273
282
|
const hash = computePathwayHash(this.service, this.env, edgeTags, parentHash)
|
|
274
283
|
const edgeLatencyNs = nowNs - edgeStartNs
|
|
@@ -352,6 +361,32 @@ class DataStreamsProcessor {
|
|
|
352
361
|
setUrl (url) {
|
|
353
362
|
this.writer.setUrl(url)
|
|
354
363
|
}
|
|
364
|
+
|
|
365
|
+
trySampleSchema (topic) {
|
|
366
|
+
const nowMs = Date.now()
|
|
367
|
+
|
|
368
|
+
if (!this._schemaSamplers[topic]) {
|
|
369
|
+
this._schemaSamplers[topic] = new SchemaSampler()
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const sampler = this._schemaSamplers[topic]
|
|
373
|
+
return sampler.trySample(nowMs)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
canSampleSchema (topic) {
|
|
377
|
+
const nowMs = Date.now()
|
|
378
|
+
|
|
379
|
+
if (!this._schemaSamplers[topic]) {
|
|
380
|
+
this._schemaSamplers[topic] = new SchemaSampler()
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const sampler = this._schemaSamplers[topic]
|
|
384
|
+
return sampler.canSample(nowMs)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
getSchema (schemaName, iterator) {
|
|
388
|
+
return SchemaBuilder.getSchema(schemaName, iterator)
|
|
389
|
+
}
|
|
355
390
|
}
|
|
356
391
|
|
|
357
392
|
module.exports = {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const LRUCache = require('lru-cache')
|
|
2
|
+
const { fnv64 } = require('../fnv')
|
|
3
|
+
const { Schema } = require('./schema')
|
|
4
|
+
|
|
5
|
+
const maxDepth = 10
|
|
6
|
+
const maxProperties = 1000
|
|
7
|
+
const CACHE = new LRUCache({ max: 32 })
|
|
8
|
+
|
|
9
|
+
class SchemaBuilder {
|
|
10
|
+
constructor (iterator) {
|
|
11
|
+
this.schema = new OpenApiSchema()
|
|
12
|
+
this.iterator = iterator
|
|
13
|
+
this.proerties = 0
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
addProperty (schemaName, fieldName, isArray, type, description, ref, format, enumValues) {
|
|
17
|
+
if (this.properties >= maxProperties) {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
this.properties += 1
|
|
21
|
+
let property = new OpenApiSchema.PROPERTY(type, description, ref, format, enumValues, null)
|
|
22
|
+
if (isArray) {
|
|
23
|
+
property = new OpenApiSchema.PROPERTY('array', null, null, null, null, property)
|
|
24
|
+
}
|
|
25
|
+
this.schema.components.schemas[schemaName].properties[fieldName] = property
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
build () {
|
|
30
|
+
this.iterator.iterateOverSchema(this)
|
|
31
|
+
const noNones = convertToJsonCompatible(this.schema)
|
|
32
|
+
const definition = jsonStringify(noNones)
|
|
33
|
+
const id = fnv64(Buffer.from(definition, 'utf-8')).toString()
|
|
34
|
+
return new Schema(definition, id)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
shouldExtractSchema (schemaName, depth) {
|
|
38
|
+
if (depth > maxDepth) {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
if (schemaName in this.schema.components.schemas) {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
this.schema.components.schemas[schemaName] = new OpenApiSchema.SCHEMA()
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static getSchema (schemaName, iterator) {
|
|
49
|
+
if (!CACHE.has(schemaName)) {
|
|
50
|
+
CACHE.set(schemaName, new SchemaBuilder(iterator).build())
|
|
51
|
+
}
|
|
52
|
+
return CACHE.get(schemaName)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class OpenApiSchema {
|
|
57
|
+
constructor () {
|
|
58
|
+
this.openapi = '3.0.0'
|
|
59
|
+
this.components = new OpenApiComponents()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
OpenApiSchema.SCHEMA = class {
|
|
64
|
+
constructor () {
|
|
65
|
+
this.type = 'object'
|
|
66
|
+
this.properties = {}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
OpenApiSchema.PROPERTY = class {
|
|
71
|
+
constructor (type, description = null, ref = null, format = null, enumValues = null, items = null) {
|
|
72
|
+
this.type = type
|
|
73
|
+
this.description = description
|
|
74
|
+
this.$ref = ref
|
|
75
|
+
this.format = format
|
|
76
|
+
this.enum = enumValues
|
|
77
|
+
this.items = items
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class OpenApiComponents {
|
|
82
|
+
constructor () {
|
|
83
|
+
this.schemas = {}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function convertToJsonCompatible (obj) {
|
|
88
|
+
if (Array.isArray(obj)) {
|
|
89
|
+
return obj.filter(item => item !== null).map(item => convertToJsonCompatible(item))
|
|
90
|
+
} else if (obj && typeof obj === 'object') {
|
|
91
|
+
const jsonObj = {}
|
|
92
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
93
|
+
if (value !== null) {
|
|
94
|
+
jsonObj[key] = convertToJsonCompatible(value)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return jsonObj
|
|
98
|
+
}
|
|
99
|
+
return obj
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function convertKey (key) {
|
|
103
|
+
if (key === 'enumValues') {
|
|
104
|
+
return 'enum'
|
|
105
|
+
}
|
|
106
|
+
return key
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function jsonStringify (obj, indent = 2) {
|
|
110
|
+
// made to stringify json exactly similar to python / java in order for hashing to be the same
|
|
111
|
+
const jsonString = JSON.stringify(obj, (_, value) => value, indent)
|
|
112
|
+
return jsonString.replace(/^ +/gm, ' ') // Replace leading spaces with single space
|
|
113
|
+
.replace(/\n/g, '') // Remove newlines
|
|
114
|
+
.replace(/{ /g, '{') // Remove space after '{'
|
|
115
|
+
.replace(/ }/g, '}') // Remove space before '}'
|
|
116
|
+
.replace(/\[ /g, '[') // Remove space after '['
|
|
117
|
+
.replace(/ \]/g, ']') // Remove space before ']'
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
SchemaBuilder,
|
|
122
|
+
OpenApiSchema,
|
|
123
|
+
convertToJsonCompatible,
|
|
124
|
+
convertKey
|
|
125
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const SAMPLE_INTERVAL_MILLIS = 30 * 1000
|
|
2
|
+
|
|
3
|
+
class SchemaSampler {
|
|
4
|
+
constructor () {
|
|
5
|
+
this.weight = 0
|
|
6
|
+
this.lastSampleMs = 0
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
trySample (currentTimeMs) {
|
|
10
|
+
if (currentTimeMs >= this.lastSampleMs + SAMPLE_INTERVAL_MILLIS) {
|
|
11
|
+
if (currentTimeMs >= this.lastSampleMs + SAMPLE_INTERVAL_MILLIS) {
|
|
12
|
+
this.lastSampleMs = currentTimeMs
|
|
13
|
+
const weight = this.weight
|
|
14
|
+
this.weight = 0
|
|
15
|
+
return weight
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return 0
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
canSample (currentTimeMs) {
|
|
22
|
+
this.weight += 1
|
|
23
|
+
return currentTimeMs >= this.lastSampleMs + SAMPLE_INTERVAL_MILLIS
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
SchemaSampler
|
|
29
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { workerData: { config: parentConfig, parentThreadId, configPort } } = require('node:worker_threads')
|
|
4
|
+
const { format } = require('node:url')
|
|
5
|
+
const log = require('../../log')
|
|
6
|
+
|
|
7
|
+
const config = module.exports = {
|
|
8
|
+
runtimeId: parentConfig.tags['runtime-id'],
|
|
9
|
+
service: parentConfig.service,
|
|
10
|
+
parentThreadId
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
updateUrl(parentConfig)
|
|
14
|
+
|
|
15
|
+
configPort.on('message', updateUrl)
|
|
16
|
+
configPort.on('messageerror', (err) => log.error(err))
|
|
17
|
+
|
|
18
|
+
function updateUrl (updates) {
|
|
19
|
+
config.url = updates.url || format({
|
|
20
|
+
protocol: 'http:',
|
|
21
|
+
hostname: updates.hostname || 'localhost',
|
|
22
|
+
port: updates.port
|
|
23
|
+
})
|
|
24
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { randomUUID } = require('crypto')
|
|
4
|
+
const { breakpoints } = require('./state')
|
|
5
|
+
const session = require('./session')
|
|
6
|
+
const send = require('./send')
|
|
7
|
+
const { ackEmitting } = require('./status')
|
|
8
|
+
const { parentThreadId } = require('./config')
|
|
9
|
+
const log = require('../../log')
|
|
10
|
+
const { version } = require('../../../../../package.json')
|
|
11
|
+
|
|
12
|
+
require('./remote_config')
|
|
13
|
+
|
|
14
|
+
// There doesn't seem to be an official standard for the content of these fields, so we're just populating them with
|
|
15
|
+
// something that should be useful to a Node.js developer.
|
|
16
|
+
const threadId = parentThreadId === 0 ? `pid:${process.pid}` : `pid:${process.pid};tid:${parentThreadId}`
|
|
17
|
+
const threadName = parentThreadId === 0 ? 'MainThread' : `WorkerThread:${parentThreadId}`
|
|
18
|
+
|
|
19
|
+
session.on('Debugger.paused', async ({ params }) => {
|
|
20
|
+
const start = process.hrtime.bigint()
|
|
21
|
+
const timestamp = Date.now()
|
|
22
|
+
const probes = params.hitBreakpoints.map((id) => breakpoints.get(id))
|
|
23
|
+
await session.post('Debugger.resume')
|
|
24
|
+
const diff = process.hrtime.bigint() - start // TODO: Should this be recored as telemetry?
|
|
25
|
+
|
|
26
|
+
log.debug(`Finished processing breakpoints - main thread paused for: ${Number(diff) / 1000000} ms`)
|
|
27
|
+
|
|
28
|
+
const logger = {
|
|
29
|
+
// We can safely use `location.file` from the first probe in the array, since all probes hit by `hitBreakpoints`
|
|
30
|
+
// must exist in the same file since the debugger can only pause the main thread in one location.
|
|
31
|
+
name: probes[0].location.file, // name of the class/type/file emitting the snapshot
|
|
32
|
+
method: params.callFrames[0].functionName, // name of the method/function emitting the snapshot
|
|
33
|
+
version,
|
|
34
|
+
thread_id: threadId,
|
|
35
|
+
thread_name: threadName
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// TODO: Send multiple probes in one HTTP request as an array
|
|
39
|
+
for (const probe of probes) {
|
|
40
|
+
const snapshot = {
|
|
41
|
+
id: randomUUID(),
|
|
42
|
+
timestamp,
|
|
43
|
+
probe: {
|
|
44
|
+
id: probe.id,
|
|
45
|
+
version: probe.version,
|
|
46
|
+
location: probe.location
|
|
47
|
+
},
|
|
48
|
+
language: 'javascript'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// TODO: Process template
|
|
52
|
+
send(probe.template, logger, snapshot, (err) => {
|
|
53
|
+
if (err) log.error(err)
|
|
54
|
+
else ackEmitting(probe)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { builtinModules } = require('node:module')
|
|
4
|
+
|
|
5
|
+
if (builtinModules.includes('inspector/promises')) {
|
|
6
|
+
module.exports = require('node:inspector/promises')
|
|
7
|
+
} else {
|
|
8
|
+
const inspector = require('node:inspector')
|
|
9
|
+
const { promisify } = require('node:util')
|
|
10
|
+
|
|
11
|
+
// The rest of the code in this file is lifted from:
|
|
12
|
+
// https://github.com/nodejs/node/blob/1d4d76ff3fb08f9a0c55a1d5530b46c4d5d550c7/lib/inspector/promises.js
|
|
13
|
+
class Session extends inspector.Session {
|
|
14
|
+
constructor () { super() } // eslint-disable-line no-useless-constructor
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Session.prototype.post = promisify(inspector.Session.prototype.post)
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
...inspector,
|
|
21
|
+
Session
|
|
22
|
+
}
|
|
23
|
+
}
|