dd-trace 4.18.0 → 4.22.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 +3 -2
- package/README.md +3 -3
- package/ext/kinds.d.ts +1 -0
- package/ext/kinds.js +2 -1
- package/ext/tags.d.ts +2 -1
- package/ext/tags.js +6 -1
- package/index.d.ts +29 -0
- package/package.json +11 -10
- package/packages/datadog-core/src/storage/async_resource.js +1 -1
- package/packages/datadog-esbuild/index.js +1 -20
- package/packages/datadog-instrumentations/src/aerospike.js +47 -0
- package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
- package/packages/datadog-instrumentations/src/graphql.js +18 -4
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/http/client.js +10 -0
- package/packages/datadog-instrumentations/src/jest.js +11 -5
- package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
- package/packages/datadog-instrumentations/src/next.js +18 -6
- package/packages/datadog-instrumentations/src/restify.js +14 -1
- package/packages/datadog-instrumentations/src/rhea.js +15 -9
- package/packages/datadog-plugin-aerospike/src/index.js +113 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
- package/packages/datadog-plugin-http/src/client.js +19 -2
- package/packages/datadog-plugin-kafkajs/src/consumer.js +59 -6
- package/packages/datadog-plugin-kafkajs/src/producer.js +64 -6
- package/packages/datadog-plugin-next/src/index.js +40 -14
- package/packages/dd-trace/src/appsec/activation.js +29 -0
- package/packages/dd-trace/src/appsec/addresses.js +3 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
- package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
- package/packages/dd-trace/src/appsec/blocking.js +95 -43
- package/packages/dd-trace/src/appsec/channels.js +5 -2
- package/packages/dd-trace/src/appsec/graphql.js +146 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
- package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/index.js +33 -32
- package/packages/dd-trace/src/appsec/recommended.json +1737 -120
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +36 -15
- package/packages/dd-trace/src/appsec/reporter.js +50 -34
- package/packages/dd-trace/src/appsec/rule_manager.js +9 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +75 -56
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +22 -6
- package/packages/dd-trace/src/config.js +48 -7
- package/packages/dd-trace/src/datastreams/processor.js +166 -26
- package/packages/dd-trace/src/format.js +6 -1
- package/packages/dd-trace/src/id.js +12 -0
- package/packages/dd-trace/src/iitm.js +1 -1
- package/packages/dd-trace/src/log/channels.js +1 -1
- package/packages/dd-trace/src/noop/proxy.js +4 -0
- package/packages/dd-trace/src/opentelemetry/span.js +95 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +14 -5
- package/packages/dd-trace/src/opentracing/span.js +6 -0
- package/packages/dd-trace/src/opentracing/span_context.js +5 -2
- package/packages/dd-trace/src/plugin_manager.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +2 -1
- package/packages/dd-trace/src/plugins/database.js +1 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci.js +6 -19
- package/packages/dd-trace/src/plugins/util/git.js +4 -3
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
- package/packages/dd-trace/src/plugins/util/test.js +3 -2
- package/packages/dd-trace/src/plugins/util/url.js +26 -0
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
- package/packages/dd-trace/src/profiler.js +5 -3
- package/packages/dd-trace/src/profiling/config.js +26 -2
- package/packages/dd-trace/src/profiling/profiler.js +17 -10
- package/packages/dd-trace/src/profiling/profilers/events.js +264 -0
- package/packages/dd-trace/src/profiling/profilers/shared.js +39 -0
- package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +121 -58
- package/packages/dd-trace/src/proxy.js +25 -1
- package/packages/dd-trace/src/ritm.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
- package/packages/dd-trace/src/span_processor.js +4 -0
- package/packages/dd-trace/src/spanleak.js +98 -0
- package/packages/dd-trace/src/startup-log.js +7 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
- package/packages/dd-trace/src/telemetry/index.js +136 -44
- package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
- package/packages/dd-trace/src/telemetry/send-data.js +47 -5
- package/packages/dd-trace/src/tracer.js +8 -2
- package/scripts/install_plugin_modules.js +11 -3
- package/packages/diagnostics_channel/index.js +0 -3
- package/packages/diagnostics_channel/src/index.js +0 -121
|
@@ -235,8 +235,9 @@ class Config {
|
|
|
235
235
|
|
|
236
236
|
const inServerlessEnvironment = inAWSLambda || isGCPFunction || isAzureFunctionConsumptionPlan
|
|
237
237
|
|
|
238
|
-
const
|
|
239
|
-
process.env.DD_TRACE_TELEMETRY_ENABLED,
|
|
238
|
+
const DD_INSTRUMENTATION_TELEMETRY_ENABLED = coalesce(
|
|
239
|
+
process.env.DD_TRACE_TELEMETRY_ENABLED, // for backward compatibility
|
|
240
|
+
process.env.DD_INSTRUMENTATION_TELEMETRY_ENABLED, // to comply with instrumentation telemetry specs
|
|
240
241
|
!inServerlessEnvironment
|
|
241
242
|
)
|
|
242
243
|
const DD_TELEMETRY_HEARTBEAT_INTERVAL = process.env.DD_TELEMETRY_HEARTBEAT_INTERVAL
|
|
@@ -308,6 +309,10 @@ class Config {
|
|
|
308
309
|
options.tracePropagationStyle,
|
|
309
310
|
defaultPropagationStyle
|
|
310
311
|
)
|
|
312
|
+
const DD_TRACE_PROPAGATION_EXTRACT_FIRST = coalesce(
|
|
313
|
+
process.env.DD_TRACE_PROPAGATION_EXTRACT_FIRST,
|
|
314
|
+
false
|
|
315
|
+
)
|
|
311
316
|
const DD_TRACE_RUNTIME_ID_ENABLED = coalesce(
|
|
312
317
|
options.experimental && options.experimental.runtimeId,
|
|
313
318
|
process.env.DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED,
|
|
@@ -367,10 +372,11 @@ class Config {
|
|
|
367
372
|
isGCPFunction || isAzureFunctionConsumptionPlan
|
|
368
373
|
)
|
|
369
374
|
|
|
375
|
+
// the tracer generates 128 bit IDs by default as of v5
|
|
370
376
|
const DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED = coalesce(
|
|
371
377
|
options.traceId128BitGenerationEnabled,
|
|
372
378
|
process.env.DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED,
|
|
373
|
-
|
|
379
|
+
true
|
|
374
380
|
)
|
|
375
381
|
|
|
376
382
|
const DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED = coalesce(
|
|
@@ -393,7 +399,6 @@ class Config {
|
|
|
393
399
|
appsec.enabled,
|
|
394
400
|
process.env.DD_APPSEC_ENABLED && isTrue(process.env.DD_APPSEC_ENABLED)
|
|
395
401
|
)
|
|
396
|
-
|
|
397
402
|
const DD_APPSEC_RULES = coalesce(
|
|
398
403
|
appsec.rules,
|
|
399
404
|
process.env.DD_APPSEC_RULES
|
|
@@ -430,11 +435,25 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
430
435
|
maybeFile(appsec.blockedTemplateJson),
|
|
431
436
|
maybeFile(process.env.DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON)
|
|
432
437
|
)
|
|
438
|
+
const DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON = coalesce(
|
|
439
|
+
maybeFile(appsec.blockedTemplateGraphql),
|
|
440
|
+
maybeFile(process.env.DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON)
|
|
441
|
+
)
|
|
433
442
|
const DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING = coalesce(
|
|
434
443
|
appsec.eventTracking && appsec.eventTracking.mode,
|
|
435
444
|
process.env.DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING,
|
|
436
445
|
'safe'
|
|
437
446
|
).toLowerCase()
|
|
447
|
+
const DD_EXPERIMENTAL_API_SECURITY_ENABLED = coalesce(
|
|
448
|
+
appsec?.apiSecurity?.enabled,
|
|
449
|
+
isTrue(process.env.DD_EXPERIMENTAL_API_SECURITY_ENABLED),
|
|
450
|
+
false
|
|
451
|
+
)
|
|
452
|
+
const DD_API_SECURITY_REQUEST_SAMPLE_RATE = coalesce(
|
|
453
|
+
appsec?.apiSecurity?.requestSampling,
|
|
454
|
+
parseFloat(process.env.DD_API_SECURITY_REQUEST_SAMPLE_RATE),
|
|
455
|
+
0.1
|
|
456
|
+
)
|
|
438
457
|
|
|
439
458
|
const remoteConfigOptions = options.remoteConfig || {}
|
|
440
459
|
const DD_REMOTE_CONFIGURATION_ENABLED = coalesce(
|
|
@@ -459,6 +478,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
459
478
|
DD_IAST_ENABLED
|
|
460
479
|
)
|
|
461
480
|
|
|
481
|
+
const DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED = coalesce(
|
|
482
|
+
process.env.DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED,
|
|
483
|
+
true
|
|
484
|
+
)
|
|
485
|
+
|
|
462
486
|
const defaultIastRequestSampling = 30
|
|
463
487
|
const iastRequestSampling = coalesce(
|
|
464
488
|
parseInt(iastOptions?.requestSampling),
|
|
@@ -520,6 +544,12 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
520
544
|
true
|
|
521
545
|
)
|
|
522
546
|
|
|
547
|
+
// 0: disabled, 1: logging, 2: garbage collection + logging
|
|
548
|
+
const DD_TRACE_SPAN_LEAK_DEBUG = coalesce(
|
|
549
|
+
process.env.DD_TRACE_SPAN_LEAK_DEBUG,
|
|
550
|
+
0
|
|
551
|
+
)
|
|
552
|
+
|
|
523
553
|
const ingestion = options.ingestion || {}
|
|
524
554
|
const dogstatsd = coalesce(options.dogstatsd, {})
|
|
525
555
|
const sampler = {
|
|
@@ -577,6 +607,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
577
607
|
inject: DD_TRACE_PROPAGATION_STYLE_INJECT,
|
|
578
608
|
extract: DD_TRACE_PROPAGATION_STYLE_EXTRACT
|
|
579
609
|
}
|
|
610
|
+
this.tracePropagationExtractFirst = isTrue(DD_TRACE_PROPAGATION_EXTRACT_FIRST)
|
|
580
611
|
this.experimental = {
|
|
581
612
|
runtimeId: isTrue(DD_TRACE_RUNTIME_ID_ENABLED),
|
|
582
613
|
exporter: DD_TRACE_EXPORTER,
|
|
@@ -598,17 +629,18 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
598
629
|
this.startupLogs = isTrue(DD_TRACE_STARTUP_LOGS)
|
|
599
630
|
// Disabled for CI Visibility's agentless
|
|
600
631
|
this.telemetry = {
|
|
601
|
-
enabled: DD_TRACE_EXPORTER !== 'datadog' && isTrue(
|
|
632
|
+
enabled: DD_TRACE_EXPORTER !== 'datadog' && isTrue(DD_INSTRUMENTATION_TELEMETRY_ENABLED),
|
|
602
633
|
heartbeatInterval: DD_TELEMETRY_HEARTBEAT_INTERVAL,
|
|
603
634
|
debug: isTrue(DD_TELEMETRY_DEBUG),
|
|
604
635
|
logCollection: isTrue(DD_TELEMETRY_LOG_COLLECTION_ENABLED),
|
|
605
|
-
metrics: isTrue(DD_TELEMETRY_METRICS_ENABLED)
|
|
636
|
+
metrics: isTrue(DD_TELEMETRY_METRICS_ENABLED),
|
|
637
|
+
dependencyCollection: DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED
|
|
606
638
|
}
|
|
607
639
|
this.protocolVersion = DD_TRACE_AGENT_PROTOCOL_VERSION
|
|
608
640
|
this.tagsHeaderMaxLength = parseInt(DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH)
|
|
609
641
|
this.appsec = {
|
|
610
642
|
enabled: DD_APPSEC_ENABLED,
|
|
611
|
-
rules: DD_APPSEC_RULES
|
|
643
|
+
rules: DD_APPSEC_RULES,
|
|
612
644
|
customRulesProvided: !!DD_APPSEC_RULES,
|
|
613
645
|
rateLimit: DD_APPSEC_TRACE_RATE_LIMIT,
|
|
614
646
|
wafTimeout: DD_APPSEC_WAF_TIMEOUT,
|
|
@@ -616,11 +648,18 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
616
648
|
obfuscatorValueRegex: DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
|
|
617
649
|
blockedTemplateHtml: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML,
|
|
618
650
|
blockedTemplateJson: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON,
|
|
651
|
+
blockedTemplateGraphql: DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON,
|
|
619
652
|
eventTracking: {
|
|
620
653
|
enabled: ['extended', 'safe'].includes(DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING),
|
|
621
654
|
mode: DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING
|
|
655
|
+
},
|
|
656
|
+
apiSecurity: {
|
|
657
|
+
enabled: DD_EXPERIMENTAL_API_SECURITY_ENABLED,
|
|
658
|
+
// Coerce value between 0 and 1
|
|
659
|
+
requestSampling: Math.min(1, Math.max(0, DD_API_SECURITY_REQUEST_SAMPLE_RATE))
|
|
622
660
|
}
|
|
623
661
|
}
|
|
662
|
+
|
|
624
663
|
this.remoteConfig = {
|
|
625
664
|
enabled: DD_REMOTE_CONFIGURATION_ENABLED,
|
|
626
665
|
pollInterval: DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS
|
|
@@ -694,6 +733,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
694
733
|
this.isGCPFunction = isGCPFunction
|
|
695
734
|
this.isAzureFunctionConsumptionPlan = isAzureFunctionConsumptionPlan
|
|
696
735
|
|
|
736
|
+
this.spanLeakDebug = Number(DD_TRACE_SPAN_LEAK_DEBUG)
|
|
737
|
+
|
|
697
738
|
tagger.add(this.tags, {
|
|
698
739
|
service: this.service,
|
|
699
740
|
env: this.env,
|
|
@@ -4,12 +4,16 @@ const pkg = require('../../../../package.json')
|
|
|
4
4
|
const Uint64 = require('int64-buffer').Uint64BE
|
|
5
5
|
|
|
6
6
|
const { LogCollapsingLowestDenseDDSketch } = require('@datadog/sketches-js')
|
|
7
|
-
|
|
7
|
+
const { encodePathwayContext } = require('./pathway')
|
|
8
8
|
const { DataStreamsWriter } = require('./writer')
|
|
9
9
|
const { computePathwayHash } = require('./pathway')
|
|
10
|
+
const { types } = require('util')
|
|
11
|
+
const { PATHWAY_HASH } = require('../../../../ext/tags')
|
|
12
|
+
|
|
10
13
|
const ENTRY_PARENT_HASH = Buffer.from('0000000000000000', 'hex')
|
|
11
14
|
|
|
12
15
|
const HIGH_ACCURACY_DISTRIBUTION = 0.0075
|
|
16
|
+
const CONTEXT_PROPAGATION_KEY = 'dd-pathway-ctx'
|
|
13
17
|
|
|
14
18
|
class StatsPoint {
|
|
15
19
|
constructor (hash, parentHash, edgeTags) {
|
|
@@ -18,6 +22,7 @@ class StatsPoint {
|
|
|
18
22
|
this.edgeTags = edgeTags
|
|
19
23
|
this.edgeLatency = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
|
|
20
24
|
this.pathwayLatency = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
|
|
25
|
+
this.payloadSize = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
addLatencies (checkpoint) {
|
|
@@ -25,6 +30,7 @@ class StatsPoint {
|
|
|
25
30
|
const pathwayLatencySec = checkpoint.pathwayLatencyNs / 1e9
|
|
26
31
|
this.edgeLatency.accept(edgeLatencySec)
|
|
27
32
|
this.pathwayLatency.accept(pathwayLatencySec)
|
|
33
|
+
this.payloadSize.accept(checkpoint.payloadSize)
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
encode () {
|
|
@@ -33,22 +39,105 @@ class StatsPoint {
|
|
|
33
39
|
ParentHash: this.parentHash,
|
|
34
40
|
EdgeTags: this.edgeTags,
|
|
35
41
|
EdgeLatency: this.edgeLatency.toProto(),
|
|
36
|
-
PathwayLatency: this.pathwayLatency.toProto()
|
|
42
|
+
PathwayLatency: this.pathwayLatency.toProto(),
|
|
43
|
+
PayloadSize: this.payloadSize.toProto()
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
}
|
|
40
47
|
|
|
41
|
-
class
|
|
48
|
+
class Backlog {
|
|
49
|
+
constructor ({ offset, ...tags }) {
|
|
50
|
+
this._tags = Object.keys(tags).sort().map(key => `${key}:${tags[key]}`)
|
|
51
|
+
this._hash = this._tags.join(',')
|
|
52
|
+
this._offset = offset
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get hash () { return this._hash }
|
|
56
|
+
|
|
57
|
+
get offset () { return this._offset }
|
|
58
|
+
|
|
59
|
+
get tags () { return this._tags }
|
|
60
|
+
|
|
61
|
+
encode () {
|
|
62
|
+
return {
|
|
63
|
+
Tags: this.tags,
|
|
64
|
+
Value: this.offset
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class StatsBucket {
|
|
70
|
+
constructor () {
|
|
71
|
+
this._checkpoints = new Map()
|
|
72
|
+
this._backlogs = new Map()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get checkpoints () {
|
|
76
|
+
return this._checkpoints
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get backlogs () {
|
|
80
|
+
return this._backlogs
|
|
81
|
+
}
|
|
82
|
+
|
|
42
83
|
forCheckpoint (checkpoint) {
|
|
43
84
|
const key = checkpoint.hash
|
|
44
|
-
if (!this.has(key)) {
|
|
45
|
-
this.set(
|
|
85
|
+
if (!this._checkpoints.has(key)) {
|
|
86
|
+
this._checkpoints.set(
|
|
87
|
+
key, new StatsPoint(checkpoint.hash, checkpoint.parentHash, checkpoint.edgeTags)
|
|
88
|
+
)
|
|
46
89
|
}
|
|
47
90
|
|
|
48
|
-
return this.get(key)
|
|
91
|
+
return this._checkpoints.get(key)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Conditionally add a backlog to the bucket. If there is currently an offset
|
|
96
|
+
* matching the backlog's tags, overwrite the offset IFF the backlog's offset
|
|
97
|
+
* is greater than the recorded offset.
|
|
98
|
+
*
|
|
99
|
+
* @typedef {{[key: string]: string}} BacklogData
|
|
100
|
+
* @property {number} offset
|
|
101
|
+
*
|
|
102
|
+
* @param {BacklogData} backlogData
|
|
103
|
+
* @returns {Backlog}
|
|
104
|
+
*/
|
|
105
|
+
forBacklog (backlogData) {
|
|
106
|
+
const backlog = new Backlog(backlogData)
|
|
107
|
+
const existingBacklog = this._backlogs.get(backlog.hash)
|
|
108
|
+
if (existingBacklog !== undefined) {
|
|
109
|
+
if (existingBacklog.offset > backlog.offset) {
|
|
110
|
+
return existingBacklog
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this._backlogs.set(backlog.hash, backlog)
|
|
114
|
+
return backlog
|
|
49
115
|
}
|
|
50
116
|
}
|
|
51
117
|
|
|
118
|
+
function getSizeOrZero (obj) {
|
|
119
|
+
if (typeof obj === 'string') {
|
|
120
|
+
return Buffer.from(obj, 'utf-8').length
|
|
121
|
+
}
|
|
122
|
+
if (types.isArrayBuffer(obj)) {
|
|
123
|
+
return obj.byteLength
|
|
124
|
+
}
|
|
125
|
+
if (Buffer.isBuffer(obj)) {
|
|
126
|
+
return obj.length
|
|
127
|
+
}
|
|
128
|
+
return 0
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function getHeadersSize (headers) {
|
|
132
|
+
if (headers === undefined) return 0
|
|
133
|
+
return Object.entries(headers).reduce((prev, [key, val]) => getSizeOrZero(key) + getSizeOrZero(val) + prev, 0)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getMessageSize (message) {
|
|
137
|
+
const { key, value, headers } = message
|
|
138
|
+
return getSizeOrZero(key) + getSizeOrZero(value) + getHeadersSize(headers)
|
|
139
|
+
}
|
|
140
|
+
|
|
52
141
|
class TimeBuckets extends Map {
|
|
53
142
|
forTime (time) {
|
|
54
143
|
if (!this.has(time)) {
|
|
@@ -92,12 +181,12 @@ class DataStreamsProcessor {
|
|
|
92
181
|
}
|
|
93
182
|
|
|
94
183
|
onInterval () {
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
184
|
+
const { Stats } = this._serializeBuckets()
|
|
185
|
+
if (Stats.length === 0) return
|
|
97
186
|
const payload = {
|
|
98
187
|
Env: this.env,
|
|
99
188
|
Service: this.service,
|
|
100
|
-
Stats
|
|
189
|
+
Stats,
|
|
101
190
|
TracerVersion: pkg.version,
|
|
102
191
|
Version: this.version,
|
|
103
192
|
Lang: 'javascript'
|
|
@@ -105,15 +194,28 @@ class DataStreamsProcessor {
|
|
|
105
194
|
this.writer.flush(payload)
|
|
106
195
|
}
|
|
107
196
|
|
|
108
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Given a timestamp in nanoseconds, compute and return the closest TimeBucket
|
|
199
|
+
* @param {number} timestamp
|
|
200
|
+
* @returns {StatsBucket}
|
|
201
|
+
*/
|
|
202
|
+
bucketFromTimestamp (timestamp) {
|
|
203
|
+
const bucketTime = Math.round(timestamp - (timestamp % this.bucketSizeNs))
|
|
204
|
+
return this.buckets.forTime(bucketTime)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
recordCheckpoint (checkpoint, span = null) {
|
|
109
208
|
if (!this.enabled) return
|
|
110
|
-
|
|
111
|
-
this.buckets.forTime(bucketTime)
|
|
209
|
+
this.bucketFromTimestamp(checkpoint.currentTimestamp)
|
|
112
210
|
.forCheckpoint(checkpoint)
|
|
113
211
|
.addLatencies(checkpoint)
|
|
212
|
+
// set DSM pathway hash on span to enable related traces feature on DSM tab, convert from buffer to uint64
|
|
213
|
+
if (span) {
|
|
214
|
+
span.setTag(PATHWAY_HASH, checkpoint.hash.readBigUInt64BE(0).toString())
|
|
215
|
+
}
|
|
114
216
|
}
|
|
115
217
|
|
|
116
|
-
setCheckpoint (edgeTags, ctx = null) {
|
|
218
|
+
setCheckpoint (edgeTags, span, ctx = null, payloadSize = 0) {
|
|
117
219
|
if (!this.enabled) return null
|
|
118
220
|
const nowNs = Date.now() * 1e6
|
|
119
221
|
const direction = edgeTags.find(t => t.startsWith('direction:'))
|
|
@@ -147,45 +249,78 @@ class DataStreamsProcessor {
|
|
|
147
249
|
const hash = computePathwayHash(this.service, this.env, edgeTags, parentHash)
|
|
148
250
|
const edgeLatencyNs = nowNs - edgeStartNs
|
|
149
251
|
const pathwayLatencyNs = nowNs - pathwayStartNs
|
|
252
|
+
const dataStreamsContext = {
|
|
253
|
+
hash: hash,
|
|
254
|
+
edgeStartNs: edgeStartNs,
|
|
255
|
+
pathwayStartNs: pathwayStartNs,
|
|
256
|
+
previousDirection: direction,
|
|
257
|
+
closestOppositeDirectionHash: closestOppositeDirectionHash,
|
|
258
|
+
closestOppositeDirectionEdgeStart: closestOppositeDirectionEdgeStart
|
|
259
|
+
}
|
|
260
|
+
if (direction === 'direction:out') {
|
|
261
|
+
// Add the header for this now, as the callee doesn't have access to context when producing
|
|
262
|
+
payloadSize += getSizeOrZero(encodePathwayContext(dataStreamsContext))
|
|
263
|
+
payloadSize += CONTEXT_PROPAGATION_KEY.length
|
|
264
|
+
}
|
|
150
265
|
const checkpoint = {
|
|
151
266
|
currentTimestamp: nowNs,
|
|
152
267
|
parentHash: parentHash,
|
|
153
268
|
hash: hash,
|
|
154
269
|
edgeTags: edgeTags,
|
|
155
270
|
edgeLatencyNs: edgeLatencyNs,
|
|
156
|
-
pathwayLatencyNs: pathwayLatencyNs
|
|
271
|
+
pathwayLatencyNs: pathwayLatencyNs,
|
|
272
|
+
payloadSize: payloadSize
|
|
157
273
|
}
|
|
158
|
-
this.recordCheckpoint(checkpoint)
|
|
159
|
-
return
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
274
|
+
this.recordCheckpoint(checkpoint, span)
|
|
275
|
+
return dataStreamsContext
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
recordOffset ({ timestamp, ...backlogData }) {
|
|
279
|
+
if (!this.enabled) return
|
|
280
|
+
return this.bucketFromTimestamp(timestamp)
|
|
281
|
+
.forBacklog(backlogData)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
setOffset (offsetObj) {
|
|
285
|
+
if (!this.enabled) return
|
|
286
|
+
const nowNs = Date.now() * 1e6
|
|
287
|
+
const backlogData = {
|
|
288
|
+
...offsetObj,
|
|
289
|
+
timestamp: nowNs
|
|
166
290
|
}
|
|
291
|
+
this.recordOffset(backlogData)
|
|
167
292
|
}
|
|
168
293
|
|
|
169
294
|
_serializeBuckets () {
|
|
295
|
+
// TimeBuckets
|
|
170
296
|
const serializedBuckets = []
|
|
171
297
|
|
|
172
298
|
for (const [ timeNs, bucket ] of this.buckets.entries()) {
|
|
173
299
|
const points = []
|
|
174
300
|
|
|
175
|
-
|
|
301
|
+
// bucket: StatsBucket
|
|
302
|
+
// stats: StatsPoint
|
|
303
|
+
for (const stats of bucket._checkpoints.values()) {
|
|
176
304
|
points.push(stats.encode())
|
|
177
305
|
}
|
|
178
306
|
|
|
307
|
+
const backlogs = []
|
|
308
|
+
for (const backlog of bucket._backlogs.values()) {
|
|
309
|
+
backlogs.push(backlog.encode())
|
|
310
|
+
}
|
|
179
311
|
serializedBuckets.push({
|
|
180
312
|
Start: new Uint64(timeNs),
|
|
181
313
|
Duration: new Uint64(this.bucketSizeNs),
|
|
182
|
-
Stats: points
|
|
314
|
+
Stats: points,
|
|
315
|
+
Backlogs: backlogs
|
|
183
316
|
})
|
|
184
317
|
}
|
|
185
318
|
|
|
186
319
|
this.buckets.clear()
|
|
187
320
|
|
|
188
|
-
return
|
|
321
|
+
return {
|
|
322
|
+
Stats: serializedBuckets
|
|
323
|
+
}
|
|
189
324
|
}
|
|
190
325
|
}
|
|
191
326
|
|
|
@@ -193,6 +328,11 @@ module.exports = {
|
|
|
193
328
|
DataStreamsProcessor: DataStreamsProcessor,
|
|
194
329
|
StatsPoint: StatsPoint,
|
|
195
330
|
StatsBucket: StatsBucket,
|
|
331
|
+
Backlog,
|
|
196
332
|
TimeBuckets,
|
|
197
|
-
|
|
333
|
+
getMessageSize,
|
|
334
|
+
getHeadersSize,
|
|
335
|
+
getSizeOrZero,
|
|
336
|
+
ENTRY_PARENT_HASH,
|
|
337
|
+
CONTEXT_PROPAGATION_KEY
|
|
198
338
|
}
|
|
@@ -14,7 +14,7 @@ const SPAN_SAMPLING_MECHANISM = constants.SPAN_SAMPLING_MECHANISM
|
|
|
14
14
|
const SPAN_SAMPLING_RULE_RATE = constants.SPAN_SAMPLING_RULE_RATE
|
|
15
15
|
const SPAN_SAMPLING_MAX_PER_SECOND = constants.SPAN_SAMPLING_MAX_PER_SECOND
|
|
16
16
|
const SAMPLING_MECHANISM_SPAN = constants.SAMPLING_MECHANISM_SPAN
|
|
17
|
-
const { MEASURED, BASE_SERVICE } = tags
|
|
17
|
+
const { MEASURED, BASE_SERVICE, ANALYTICS } = tags
|
|
18
18
|
const ORIGIN_KEY = constants.ORIGIN_KEY
|
|
19
19
|
const HOSTNAME_KEY = constants.HOSTNAME_KEY
|
|
20
20
|
const TOP_LEVEL_KEY = constants.TOP_LEVEL_KEY
|
|
@@ -24,6 +24,7 @@ const ERROR_STACK = constants.ERROR_STACK
|
|
|
24
24
|
const ERROR_TYPE = constants.ERROR_TYPE
|
|
25
25
|
|
|
26
26
|
const map = {
|
|
27
|
+
'operation.name': 'name',
|
|
27
28
|
'service.name': 'service',
|
|
28
29
|
'span.type': 'type',
|
|
29
30
|
'resource.name': 'resource'
|
|
@@ -83,6 +84,7 @@ function extractTags (trace, span) {
|
|
|
83
84
|
|
|
84
85
|
for (const tag in tags) {
|
|
85
86
|
switch (tag) {
|
|
87
|
+
case 'operation.name':
|
|
86
88
|
case 'service.name':
|
|
87
89
|
case 'span.type':
|
|
88
90
|
case 'resource.name':
|
|
@@ -92,6 +94,9 @@ function extractTags (trace, span) {
|
|
|
92
94
|
case 'http.status_code':
|
|
93
95
|
addTag(trace.meta, {}, tag, tags[tag] && String(tags[tag]))
|
|
94
96
|
break
|
|
97
|
+
case 'analytics.event':
|
|
98
|
+
addTag({}, trace.metrics, ANALYTICS, tags[tag] === undefined || tags[tag] ? 1 : 0)
|
|
99
|
+
break
|
|
95
100
|
case HOSTNAME_KEY:
|
|
96
101
|
case MEASURED:
|
|
97
102
|
addTag({}, trace.metrics, tag, tags[tag] === undefined || tags[tag] ? 1 : 0)
|
|
@@ -42,6 +42,18 @@ class Identifier {
|
|
|
42
42
|
toJSON () {
|
|
43
43
|
return this.toString()
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
equals (other) {
|
|
47
|
+
const length = this._buffer.length
|
|
48
|
+
const otherLength = other._buffer.length
|
|
49
|
+
|
|
50
|
+
// Only compare the bytes available in both IDs.
|
|
51
|
+
for (let i = length, j = otherLength; i >= 0 && j >= 0; i--, j--) {
|
|
52
|
+
if (this._buffer[i] !== other._buffer[j]) return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
// Create a buffer, using an optional hexadecimal value if provided.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const semver = require('semver')
|
|
4
4
|
const logger = require('./log')
|
|
5
5
|
const { addHook } = require('import-in-the-middle')
|
|
6
|
-
const dc = require('
|
|
6
|
+
const dc = require('dc-polyfill')
|
|
7
7
|
|
|
8
8
|
if (semver.satisfies(process.versions.node, '>=14.13.1')) {
|
|
9
9
|
const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
|
|
@@ -11,6 +11,7 @@ const tracer = require('../../')
|
|
|
11
11
|
const DatadogSpan = require('../opentracing/span')
|
|
12
12
|
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../constants')
|
|
13
13
|
const { SERVICE_NAME, RESOURCE_NAME } = require('../../../../ext/tags')
|
|
14
|
+
const kinds = require('../../../../ext/kinds')
|
|
14
15
|
|
|
15
16
|
const SpanContext = require('./span_context')
|
|
16
17
|
|
|
@@ -19,6 +20,93 @@ function hrTimeToMilliseconds (time) {
|
|
|
19
20
|
return time[0] * 1e3 + time[1] / 1e6
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
const spanKindNames = {
|
|
24
|
+
[api.SpanKind.INTERNAL]: kinds.INTERNAL,
|
|
25
|
+
[api.SpanKind.SERVER]: kinds.SERVER,
|
|
26
|
+
[api.SpanKind.CLIENT]: kinds.CLIENT,
|
|
27
|
+
[api.SpanKind.PRODUCER]: kinds.PRODUCER,
|
|
28
|
+
[api.SpanKind.CONSUMER]: kinds.CONSUMER
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Several of these attributes are not yet supported by the Node.js OTel API.
|
|
33
|
+
* We check for old equivalents where we can, but not all had equivalents.
|
|
34
|
+
*/
|
|
35
|
+
function spanNameMapper (spanName, kind, attributes) {
|
|
36
|
+
if (spanName) return spanName
|
|
37
|
+
|
|
38
|
+
const opName = attributes['operation.name']
|
|
39
|
+
if (opName) return opName
|
|
40
|
+
|
|
41
|
+
const { INTERNAL, SERVER, CLIENT } = api.SpanKind
|
|
42
|
+
|
|
43
|
+
// HTTP server and client requests
|
|
44
|
+
// TODO: Drop http.method when http.request.method is supported.
|
|
45
|
+
for (const key of ['http.method', 'http.request.method']) {
|
|
46
|
+
if (key in attributes) {
|
|
47
|
+
if (kind === SERVER) {
|
|
48
|
+
return 'http.server.request'
|
|
49
|
+
}
|
|
50
|
+
if (kind === CLIENT) {
|
|
51
|
+
return 'http.client.request'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Databases
|
|
57
|
+
const dbSystem = attributes['db.system']
|
|
58
|
+
if (dbSystem && kind === CLIENT) {
|
|
59
|
+
return `${dbSystem}.query`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Messaging
|
|
63
|
+
const msgSys = attributes['messaging.system']
|
|
64
|
+
const msgOp = attributes['messaging.operation']
|
|
65
|
+
if (msgSys && msgOp && kind !== INTERNAL) {
|
|
66
|
+
return `${msgSys}.${msgOp}`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// RPC (and AWS)
|
|
70
|
+
const rpcSystem = attributes['rpc.system']
|
|
71
|
+
if (rpcSystem) {
|
|
72
|
+
if (kind === CLIENT) {
|
|
73
|
+
return rpcSystem === 'aws-api'
|
|
74
|
+
? `aws.${attributes['rpc.service'] || 'client'}.request`
|
|
75
|
+
: `${rpcSystem}.client.request`
|
|
76
|
+
}
|
|
77
|
+
if (kind === SERVER) {
|
|
78
|
+
return `${rpcSystem}.server.request`
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// FaaS
|
|
83
|
+
const faasProvider = attributes['faas.invoked_provider']
|
|
84
|
+
const faasName = attributes['faas.invoked_name']
|
|
85
|
+
const faasTrigger = attributes['faas.trigger']
|
|
86
|
+
if (kind === CLIENT && faasProvider && faasName) {
|
|
87
|
+
return `${faasProvider}.${faasName}.invoke`
|
|
88
|
+
}
|
|
89
|
+
if (kind === SERVER && faasTrigger) {
|
|
90
|
+
return `${faasTrigger}.invoke`
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// GraphQL
|
|
94
|
+
// NOTE: Not part of Semantic Convention spec yet, but is used in the GraphQL
|
|
95
|
+
// integration.
|
|
96
|
+
const isGraphQL = 'graphql.operation.type' in attributes
|
|
97
|
+
if (isGraphQL) return 'graphql.server.request'
|
|
98
|
+
|
|
99
|
+
// Network
|
|
100
|
+
// TODO: Doesn't exist yet. No equivalent.
|
|
101
|
+
const protocol = attributes['network.protocol.name']
|
|
102
|
+
const protocolPrefix = protocol ? `${protocol}.` : ''
|
|
103
|
+
if (kind === SERVER) return `${protocolPrefix}server.request`
|
|
104
|
+
if (kind === CLIENT) return `${protocolPrefix}client.request`
|
|
105
|
+
|
|
106
|
+
// If all else fails, default to stringified span.kind.
|
|
107
|
+
return spanKindNames[kind]
|
|
108
|
+
}
|
|
109
|
+
|
|
22
110
|
class Span {
|
|
23
111
|
constructor (
|
|
24
112
|
parentTracer,
|
|
@@ -27,7 +115,8 @@ class Span {
|
|
|
27
115
|
spanContext,
|
|
28
116
|
kind,
|
|
29
117
|
links = [],
|
|
30
|
-
timeInput
|
|
118
|
+
timeInput,
|
|
119
|
+
attributes
|
|
31
120
|
) {
|
|
32
121
|
const { _tracer } = tracer
|
|
33
122
|
|
|
@@ -35,7 +124,7 @@ class Span {
|
|
|
35
124
|
const startTime = hrTimeToMilliseconds(hrStartTime)
|
|
36
125
|
|
|
37
126
|
this._ddSpan = new DatadogSpan(_tracer, _tracer._processor, _tracer._prioritySampler, {
|
|
38
|
-
operationName: spanName,
|
|
127
|
+
operationName: spanNameMapper(spanName, kind, attributes),
|
|
39
128
|
context: spanContext._ddContext,
|
|
40
129
|
startTime,
|
|
41
130
|
hostname: _tracer._hostname,
|
|
@@ -46,6 +135,10 @@ class Span {
|
|
|
46
135
|
}
|
|
47
136
|
}, _tracer._debug)
|
|
48
137
|
|
|
138
|
+
if (attributes) {
|
|
139
|
+
this.setAttributes(attributes)
|
|
140
|
+
}
|
|
141
|
+
|
|
49
142
|
this._parentTracer = parentTracer
|
|
50
143
|
this._context = context
|
|
51
144
|
|
|
@@ -78,23 +78,22 @@ class Tracer {
|
|
|
78
78
|
// return api.trace.wrapSpanContext(spanContext)
|
|
79
79
|
// }
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
return new Span(
|
|
82
82
|
this,
|
|
83
83
|
context,
|
|
84
84
|
name,
|
|
85
85
|
spanContext,
|
|
86
86
|
spanKind,
|
|
87
87
|
links,
|
|
88
|
-
options.startTime
|
|
88
|
+
options.startTime,
|
|
89
|
+
|
|
90
|
+
// Set initial span attributes. The attributes object may have been mutated
|
|
91
|
+
// by the sampler, so we sanitize the merged attributes before setting them.
|
|
92
|
+
sanitizeAttributes(
|
|
93
|
+
// Object.assign(attributes, samplingResult.attributes)
|
|
94
|
+
attributes
|
|
95
|
+
)
|
|
89
96
|
)
|
|
90
|
-
// Set initial span attributes. The attributes object may have been mutated
|
|
91
|
-
// by the sampler, so we sanitize the merged attributes before setting them.
|
|
92
|
-
const initAttributes = sanitizeAttributes(
|
|
93
|
-
// Object.assign(attributes, samplingResult.attributes)
|
|
94
|
-
attributes
|
|
95
|
-
)
|
|
96
|
-
span.setAttributes(initAttributes)
|
|
97
|
-
return span
|
|
98
97
|
}
|
|
99
98
|
|
|
100
99
|
startActiveSpan (name, options, context, fn) {
|