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.
Files changed (110) hide show
  1. package/LICENSE-3rdparty.csv +3 -2
  2. package/README.md +3 -3
  3. package/ext/kinds.d.ts +1 -0
  4. package/ext/kinds.js +2 -1
  5. package/ext/tags.d.ts +2 -1
  6. package/ext/tags.js +6 -1
  7. package/index.d.ts +29 -0
  8. package/package.json +11 -10
  9. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  10. package/packages/datadog-esbuild/index.js +1 -20
  11. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  12. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  13. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  14. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  15. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
  16. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  17. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  18. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  19. package/packages/datadog-instrumentations/src/http/client.js +10 -0
  20. package/packages/datadog-instrumentations/src/jest.js +11 -5
  21. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  22. package/packages/datadog-instrumentations/src/next.js +18 -6
  23. package/packages/datadog-instrumentations/src/restify.js +14 -1
  24. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  25. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  26. package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
  27. package/packages/datadog-plugin-http/src/client.js +19 -2
  28. package/packages/datadog-plugin-kafkajs/src/consumer.js +59 -6
  29. package/packages/datadog-plugin-kafkajs/src/producer.js +64 -6
  30. package/packages/datadog-plugin-next/src/index.js +40 -14
  31. package/packages/dd-trace/src/appsec/activation.js +29 -0
  32. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  33. package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
  34. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  35. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  36. package/packages/dd-trace/src/appsec/channels.js +5 -2
  37. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  38. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  39. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  40. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  41. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  42. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  43. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
  44. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
  45. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  46. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  47. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  48. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  49. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  50. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  51. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  52. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  53. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  54. package/packages/dd-trace/src/appsec/index.js +33 -32
  55. package/packages/dd-trace/src/appsec/recommended.json +1737 -120
  56. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  57. package/packages/dd-trace/src/appsec/remote_config/index.js +36 -15
  58. package/packages/dd-trace/src/appsec/reporter.js +50 -34
  59. package/packages/dd-trace/src/appsec/rule_manager.js +9 -6
  60. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  61. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  62. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  63. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
  64. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +75 -56
  65. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +22 -6
  66. package/packages/dd-trace/src/config.js +48 -7
  67. package/packages/dd-trace/src/datastreams/processor.js +166 -26
  68. package/packages/dd-trace/src/format.js +6 -1
  69. package/packages/dd-trace/src/id.js +12 -0
  70. package/packages/dd-trace/src/iitm.js +1 -1
  71. package/packages/dd-trace/src/log/channels.js +1 -1
  72. package/packages/dd-trace/src/noop/proxy.js +4 -0
  73. package/packages/dd-trace/src/opentelemetry/span.js +95 -2
  74. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
  75. package/packages/dd-trace/src/opentracing/propagation/text_map.js +14 -5
  76. package/packages/dd-trace/src/opentracing/span.js +6 -0
  77. package/packages/dd-trace/src/opentracing/span_context.js +5 -2
  78. package/packages/dd-trace/src/plugin_manager.js +1 -1
  79. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -1
  80. package/packages/dd-trace/src/plugins/database.js +1 -1
  81. package/packages/dd-trace/src/plugins/index.js +1 -0
  82. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  83. package/packages/dd-trace/src/plugins/util/ci.js +6 -19
  84. package/packages/dd-trace/src/plugins/util/git.js +4 -3
  85. package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
  86. package/packages/dd-trace/src/plugins/util/test.js +3 -2
  87. package/packages/dd-trace/src/plugins/util/url.js +26 -0
  88. package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
  89. package/packages/dd-trace/src/profiler.js +5 -3
  90. package/packages/dd-trace/src/profiling/config.js +26 -2
  91. package/packages/dd-trace/src/profiling/profiler.js +17 -10
  92. package/packages/dd-trace/src/profiling/profilers/events.js +264 -0
  93. package/packages/dd-trace/src/profiling/profilers/shared.js +39 -0
  94. package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
  95. package/packages/dd-trace/src/profiling/profilers/wall.js +121 -58
  96. package/packages/dd-trace/src/proxy.js +25 -1
  97. package/packages/dd-trace/src/ritm.js +1 -1
  98. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  99. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  100. package/packages/dd-trace/src/span_processor.js +4 -0
  101. package/packages/dd-trace/src/spanleak.js +98 -0
  102. package/packages/dd-trace/src/startup-log.js +7 -1
  103. package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
  104. package/packages/dd-trace/src/telemetry/index.js +136 -44
  105. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  106. package/packages/dd-trace/src/telemetry/send-data.js +47 -5
  107. package/packages/dd-trace/src/tracer.js +8 -2
  108. package/scripts/install_plugin_modules.js +11 -3
  109. package/packages/diagnostics_channel/index.js +0 -3
  110. 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 DD_TRACE_TELEMETRY_ENABLED = coalesce(
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
- false
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(DD_TRACE_TELEMETRY_ENABLED),
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 ? safeJsonParse(maybeFile(DD_APPSEC_RULES)) : require('./appsec/recommended.json'),
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 StatsBucket extends Map {
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(key, new StatsPoint(checkpoint.hash, checkpoint.parentHash, checkpoint.edgeTags)) // StatsPoint
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 serialized = this._serializeBuckets()
96
- if (!serialized) return
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: serialized,
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
- recordCheckpoint (checkpoint) {
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
- const bucketTime = Math.round(checkpoint.currentTimestamp - (checkpoint.currentTimestamp % this.bucketSizeNs))
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
- hash: hash,
161
- edgeStartNs: edgeStartNs,
162
- pathwayStartNs: pathwayStartNs,
163
- previousDirection: direction,
164
- closestOppositeDirectionHash: closestOppositeDirectionHash,
165
- closestOppositeDirectionEdgeStart: closestOppositeDirectionEdgeStart
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
- for (const stats of bucket.values()) {
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 serializedBuckets
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
- ENTRY_PARENT_HASH
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('../../diagnostics_channel')
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')
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { channel } = require('../../../diagnostics_channel')
3
+ const { channel } = require('dc-polyfill')
4
4
 
5
5
  const Level = {
6
6
  Debug: 'debug',
@@ -20,6 +20,10 @@ class Tracer {
20
20
  return this
21
21
  }
22
22
 
23
+ profilerStarted () {
24
+ return Promise.resolve(false)
25
+ }
26
+
23
27
  trace (name, options, fn) {
24
28
  if (!fn) {
25
29
  fn = options
@@ -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
- const span = new Span(
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) {