dd-trace 2.30.1 → 2.32.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 (97) hide show
  1. package/LICENSE-3rdparty.csv +1 -1
  2. package/esbuild.js +3 -0
  3. package/index.d.ts +10 -9
  4. package/package.json +12 -12
  5. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  6. package/packages/datadog-esbuild/index.js +9 -2
  7. package/packages/datadog-instrumentations/src/body-parser.js +15 -9
  8. package/packages/datadog-instrumentations/src/cucumber.js +11 -1
  9. package/packages/datadog-instrumentations/src/express.js +32 -0
  10. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  11. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  12. package/packages/datadog-instrumentations/src/http/server.js +2 -1
  13. package/packages/datadog-instrumentations/src/jest.js +6 -3
  14. package/packages/datadog-instrumentations/src/mocha.js +19 -2
  15. package/packages/datadog-instrumentations/src/playwright.js +5 -2
  16. package/packages/datadog-plugin-amqp10/src/consumer.js +1 -3
  17. package/packages/datadog-plugin-amqp10/src/producer.js +1 -3
  18. package/packages/datadog-plugin-amqplib/src/client.js +4 -3
  19. package/packages/datadog-plugin-amqplib/src/consumer.js +1 -3
  20. package/packages/datadog-plugin-amqplib/src/producer.js +1 -3
  21. package/packages/datadog-plugin-cucumber/src/index.js +6 -4
  22. package/packages/datadog-plugin-cypress/src/plugin.js +5 -1
  23. package/packages/datadog-plugin-cypress/src/support.js +4 -0
  24. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -3
  25. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -3
  26. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -3
  27. package/packages/datadog-plugin-http/src/client.js +1 -1
  28. package/packages/datadog-plugin-http/src/server.js +2 -2
  29. package/packages/datadog-plugin-http2/src/server.js +0 -5
  30. package/packages/datadog-plugin-jest/src/index.js +10 -5
  31. package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -4
  32. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -3
  33. package/packages/datadog-plugin-mocha/src/index.js +9 -4
  34. package/packages/datadog-plugin-playwright/src/index.js +6 -5
  35. package/packages/datadog-plugin-redis/src/index.js +16 -5
  36. package/packages/datadog-plugin-rhea/src/consumer.js +1 -3
  37. package/packages/datadog-plugin-rhea/src/producer.js +1 -5
  38. package/packages/dd-trace/src/appsec/addresses.js +0 -3
  39. package/packages/dd-trace/src/appsec/blocked_templates.js +2 -9
  40. package/packages/dd-trace/src/appsec/blocking.js +1 -1
  41. package/packages/dd-trace/src/appsec/{gateway/channels.js → channels.js} +4 -4
  42. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  43. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +21 -13
  44. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
  45. package/packages/dd-trace/src/appsec/iast/telemetry/logs.js +1 -1
  46. package/packages/dd-trace/src/appsec/index.js +87 -79
  47. package/packages/dd-trace/src/appsec/recommended.json +448 -121
  48. package/packages/dd-trace/src/appsec/remote_config/apply_states.js +7 -0
  49. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -0
  50. package/packages/dd-trace/src/appsec/remote_config/index.js +30 -11
  51. package/packages/dd-trace/src/appsec/remote_config/manager.js +33 -12
  52. package/packages/dd-trace/src/appsec/reporter.js +27 -58
  53. package/packages/dd-trace/src/appsec/rule_manager.js +160 -32
  54. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +4 -12
  55. package/packages/dd-trace/src/appsec/waf/index.js +75 -0
  56. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +57 -0
  57. package/packages/dd-trace/src/appsec/waf/waf_manager.js +66 -0
  58. package/packages/dd-trace/src/config.js +18 -1
  59. package/packages/dd-trace/src/dcitm.js +2 -0
  60. package/packages/dd-trace/src/encode/0.4.js +12 -4
  61. package/packages/dd-trace/src/encode/tags-processors.js +40 -68
  62. package/packages/dd-trace/src/exporters/common/request.js +2 -2
  63. package/packages/dd-trace/src/format.js +2 -1
  64. package/packages/dd-trace/src/iitm.js +1 -1
  65. package/packages/dd-trace/src/log/channels.js +11 -12
  66. package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
  67. package/packages/dd-trace/src/plugin_manager.js +3 -1
  68. package/packages/dd-trace/src/plugins/client.js +3 -2
  69. package/packages/dd-trace/src/plugins/consumer.js +17 -2
  70. package/packages/dd-trace/src/plugins/inbound.js +7 -0
  71. package/packages/dd-trace/src/plugins/{outgoing.js → outbound.js} +2 -2
  72. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  73. package/packages/dd-trace/src/plugins/producer.js +17 -2
  74. package/packages/dd-trace/src/plugins/server.js +2 -2
  75. package/packages/dd-trace/src/plugins/tracing.js +11 -0
  76. package/packages/dd-trace/src/plugins/util/test.js +19 -1
  77. package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -1
  78. package/packages/dd-trace/src/ritm.js +1 -1
  79. package/packages/dd-trace/src/service-naming/index.js +41 -0
  80. package/packages/dd-trace/src/service-naming/schemas/definition.js +28 -0
  81. package/packages/dd-trace/src/service-naming/schemas/index.js +6 -0
  82. package/packages/dd-trace/src/service-naming/schemas/v0.js +66 -0
  83. package/packages/dd-trace/src/service-naming/schemas/v1.js +58 -0
  84. package/packages/dd-trace/src/span_stats.js +1 -1
  85. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  86. package/packages/dd-trace/src/telemetry/index.js +1 -1
  87. package/packages/diagnostics_channel/index.js +3 -0
  88. package/packages/diagnostics_channel/src/index.js +57 -0
  89. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +0 -137
  90. package/packages/dd-trace/src/appsec/callbacks/index.js +0 -7
  91. package/packages/dd-trace/src/appsec/gateway/als.js +0 -6
  92. package/packages/dd-trace/src/appsec/gateway/engine/engine.js +0 -140
  93. package/packages/dd-trace/src/appsec/gateway/engine/index.js +0 -51
  94. package/packages/dd-trace/src/appsec/gateway/engine/runner.js +0 -42
  95. package/packages/dd-trace/src/instrumenter.js +0 -203
  96. package/packages/dd-trace/src/loader.js +0 -131
  97. package/packages/dd-trace/src/plugins/incoming.js +0 -7
@@ -0,0 +1,57 @@
1
+ 'use strict'
2
+
3
+ const log = require('../../log')
4
+ const Reporter = require('../reporter')
5
+
6
+ class WAFContextWrapper {
7
+ constructor (ddwafContext, requiredAddresses, wafTimeout, rulesInfo) {
8
+ this.ddwafContext = ddwafContext
9
+ this.requiredAddresses = requiredAddresses
10
+ this.wafTimeout = wafTimeout
11
+ this.rulesInfo = rulesInfo
12
+ }
13
+
14
+ run (params) {
15
+ const inputs = {}
16
+ let someInputAdded = false
17
+
18
+ // TODO: possible optimizaion: only send params that haven't already been sent with same value to this wafContext
19
+ for (const key of Object.keys(params)) {
20
+ if (this.requiredAddresses.has(key)) {
21
+ inputs[key] = params[key]
22
+ someInputAdded = true
23
+ }
24
+ }
25
+
26
+ if (!someInputAdded) return
27
+
28
+ try {
29
+ const start = process.hrtime.bigint()
30
+
31
+ const result = this.ddwafContext.run(inputs, this.wafTimeout)
32
+
33
+ const end = process.hrtime.bigint()
34
+
35
+ Reporter.reportMetrics({
36
+ duration: result.totalRuntime / 1e3,
37
+ durationExt: parseInt(end - start) / 1e3,
38
+ rulesVersion: this.rulesInfo.version
39
+ })
40
+
41
+ if (result.data && result.data !== '[]') {
42
+ Reporter.reportAttack(result.data)
43
+ }
44
+
45
+ return result.actions
46
+ } catch (err) {
47
+ log.error('Error while running the AppSec WAF')
48
+ log.error(err)
49
+ }
50
+ }
51
+
52
+ dispose () {
53
+ this.ddwafContext.dispose()
54
+ }
55
+ }
56
+
57
+ module.exports = WAFContextWrapper
@@ -0,0 +1,66 @@
1
+ 'use strict'
2
+
3
+ const log = require('../../log')
4
+ const Reporter = require('../reporter')
5
+ const WAFContextWrapper = require('./waf_context_wrapper')
6
+
7
+ const contexts = new WeakMap()
8
+
9
+ class WAFManager {
10
+ constructor (rules, config) {
11
+ this.config = config
12
+ this.wafTimeout = config.wafTimeout
13
+ this.ddwaf = this._loadDDWAF(rules)
14
+ this._reportMetrics()
15
+ }
16
+
17
+ _loadDDWAF (rules) {
18
+ try {
19
+ // require in `try/catch` because this can throw at require time
20
+ const { DDWAF } = require('@datadog/native-appsec')
21
+
22
+ const { obfuscatorKeyRegex, obfuscatorValueRegex } = this.config
23
+ return new DDWAF(rules, { obfuscatorKeyRegex, obfuscatorValueRegex })
24
+ } catch (err) {
25
+ log.error('AppSec could not load native package. In-app WAF features will not be available.')
26
+
27
+ throw err
28
+ }
29
+ }
30
+
31
+ _reportMetrics () {
32
+ Reporter.metricsQueue.set('_dd.appsec.waf.version', this.ddwaf.constructor.version())
33
+
34
+ const { loaded, failed, errors } = this.ddwaf.rulesInfo
35
+
36
+ Reporter.metricsQueue.set('_dd.appsec.event_rules.loaded', loaded)
37
+ Reporter.metricsQueue.set('_dd.appsec.event_rules.error_count', failed)
38
+ if (failed) Reporter.metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(errors))
39
+
40
+ Reporter.metricsQueue.set('manual.keep', 'true')
41
+ }
42
+
43
+ getWAFContext (req) {
44
+ let wafContext = contexts.get(req)
45
+
46
+ if (!wafContext) {
47
+ wafContext = new WAFContextWrapper(
48
+ this.ddwaf.createContext(),
49
+ this.ddwaf.requiredAddresses,
50
+ this.wafTimeout,
51
+ this.ddwaf.rulesInfo
52
+ )
53
+ contexts.set(req, wafContext)
54
+ }
55
+
56
+ return wafContext
57
+ }
58
+
59
+ destroy () {
60
+ if (this.ddwaf) {
61
+ this.ddwaf.dispose()
62
+ }
63
+ }
64
+ }
65
+
66
+ module.exports = WAFManager
@@ -31,6 +31,19 @@ function safeJsonParse (input) {
31
31
  }
32
32
  }
33
33
 
34
+ const namingVersions = ['v0', 'v1']
35
+ const defaultVersion = 'v0'
36
+
37
+ function validateNamingVersion (versionString) {
38
+ if (!namingVersions.includes(versionString)) {
39
+ log.warn(
40
+ `Unexpected input for config.spanAttributeSchema, picked default ${defaultVersion}`
41
+ )
42
+ return defaultVersion
43
+ }
44
+ return versionString
45
+ }
46
+
34
47
  // Shallow clone with property name remapping
35
48
  function remapify (input, mappings) {
36
49
  if (!input) return
@@ -257,7 +270,9 @@ class Config {
257
270
  process.env.DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED,
258
271
  false
259
272
  )
260
-
273
+ const DD_TRACE_SPAN_ATTRIBUTE_SCHEMA = validateNamingVersion(
274
+ process.env.DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
275
+ )
261
276
  const DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH = coalesce(
262
277
  process.env.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
263
278
  '512'
@@ -464,6 +479,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
464
479
  sourceMap: !isFalse(DD_PROFILING_SOURCE_MAP),
465
480
  exporters: DD_PROFILING_EXPORTERS
466
481
  }
482
+ this.spanAttributeSchema = DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
467
483
  this.lookup = options.lookup
468
484
  this.startupLogs = isTrue(DD_TRACE_STARTUP_LOGS)
469
485
  // Disabled for CI Visibility's agentless
@@ -477,6 +493,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
477
493
  this.appsec = {
478
494
  enabled: DD_APPSEC_ENABLED,
479
495
  rules: DD_APPSEC_RULES ? safeJsonParse(maybeFile(DD_APPSEC_RULES)) : require('./appsec/recommended.json'),
496
+ customRulesProvided: !!DD_APPSEC_RULES,
480
497
  rateLimit: DD_APPSEC_TRACE_RATE_LIMIT,
481
498
  wafTimeout: DD_APPSEC_WAF_TIMEOUT,
482
499
  obfuscatorKeyRegex: DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ // TODO: Figure out why we can't use the internal version.
4
+ // eslint-disable-next-line n/no-restricted-require
3
5
  const dc = require('diagnostics_channel')
4
6
 
5
7
  const CHANNEL_PREFIX = 'dd-trace:bundledModuleLoadStart'
@@ -3,6 +3,8 @@
3
3
  const { truncateSpan, normalizeSpan } = require('./tags-processors')
4
4
  const Chunk = require('./chunk')
5
5
  const log = require('../log')
6
+ const { isTrue } = require('../util')
7
+ const coalesce = require('koalas')
6
8
 
7
9
  const SOFT_LIMIT = 8 * 1024 * 1024 // 8MB
8
10
 
@@ -24,6 +26,10 @@ class AgentEncoder {
24
26
  this._stringBytes = new Chunk()
25
27
  this._writer = writer
26
28
  this._reset()
29
+ this._debugEncoding = isTrue(coalesce(
30
+ process.env.DD_TRACE_ENCODING_DEBUG,
31
+ false
32
+ ))
27
33
  }
28
34
 
29
35
  count () {
@@ -40,11 +46,13 @@ class AgentEncoder {
40
46
 
41
47
  const end = bytes.length
42
48
 
43
- log.debug(() => {
44
- const hex = bytes.buffer.subarray(start, end).toString('hex').match(/../g).join(' ')
49
+ if (this._debugEncoding) {
50
+ log.debug(() => {
51
+ const hex = bytes.buffer.subarray(start, end).toString('hex').match(/../g).join(' ')
45
52
 
46
- return `Adding encoded trace to buffer: ${hex}`
47
- })
53
+ return `Adding encoded trace to buffer: ${hex}`
54
+ })
55
+ }
48
56
 
49
57
  // we can go over the soft limit since the agent has a 50MB hard limit
50
58
  if (this._traceBytes.length > this._limit || this._stringBytes.length > this._limit) {
@@ -9,8 +9,6 @@ const MAX_META_KEY_LENGTH = 200
9
9
  const MAX_META_VALUE_LENGTH = 25000
10
10
  // MAX_METRIC_KEY_LENGTH the maximum length of a metric name key
11
11
  const MAX_METRIC_KEY_LENGTH = MAX_META_KEY_LENGTH
12
- // MAX_METRIC_VALUE_LENGTH the maximum length of a metric name value
13
- const MAX_METRIC_VALUE_LENGTH = MAX_META_VALUE_LENGTH
14
12
 
15
13
  // From agent normalizer:
16
14
  // https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize.go
@@ -25,80 +23,55 @@ const MAX_SERVICE_LENGTH = 100
25
23
  // MAX_TYPE_LENGTH the maximum length a span type can have
26
24
  const MAX_TYPE_LENGTH = 100
27
25
 
28
- const fromEntries = Object.fromEntries || (entries =>
29
- entries.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {}))
30
-
31
- function truncateToLength (value, maxLength) {
32
- if (!value) {
33
- return value
34
- }
35
- if (value.length > maxLength) {
36
- return `${value.slice(0, maxLength)}...`
37
- }
38
- return value
39
- }
26
+ // TODO (bengl) Pretty much everything in this file should happen in
27
+ // `format.js`, so that we're not iterating over all the spans and modifying
28
+ // them yet again.
40
29
 
41
30
  // normally the agent truncates the resource and parses it in certain scenarios (e.g. SQL Queries)
42
31
  function truncateSpan (span, shouldTruncateResourceName = true) {
43
- return fromEntries(Object.entries(span).map(([key, value]) => {
44
- switch (key) {
45
- case 'resource':
46
- return ['resource', shouldTruncateResourceName ? truncateToLength(value, MAX_RESOURCE_NAME_LENGTH) : value]
47
- case 'meta':
48
- return ['meta', fromEntries(Object.entries(value).map(([metaKey, metaValue]) =>
49
- [truncateToLength(metaKey, MAX_META_KEY_LENGTH), truncateToLength(metaValue, MAX_META_VALUE_LENGTH)]
50
- ))]
51
- case 'metrics':
52
- return ['metrics', fromEntries(Object.entries(value).map(([metricsKey, metricsValue]) =>
53
- [truncateToLength(metricsKey, MAX_METRIC_KEY_LENGTH), truncateToLength(metricsValue, MAX_METRIC_VALUE_LENGTH)]
54
- ))]
55
- default:
56
- return [key, value]
32
+ if (shouldTruncateResourceName && span.resource && span.resource.length > MAX_RESOURCE_NAME_LENGTH) {
33
+ span.resource = `${span.resource.slice(0, MAX_RESOURCE_NAME_LENGTH)}...`
34
+ }
35
+ for (let metaKey in span.meta) {
36
+ const val = span.meta[metaKey]
37
+ if (metaKey.length > MAX_META_KEY_LENGTH) {
38
+ delete span.meta[metaKey]
39
+ metaKey = `${metaKey.slice(0, MAX_META_KEY_LENGTH)}...`
40
+ span.metrics[metaKey] = val
41
+ }
42
+ if (val && val.length > MAX_META_VALUE_LENGTH) {
43
+ span.meta[metaKey] = `${val.slice(0, MAX_META_VALUE_LENGTH)}...`
44
+ }
45
+ }
46
+ for (let metricsKey in span.metrics) {
47
+ const val = span.metrics[metricsKey]
48
+ if (metricsKey.length > MAX_METRIC_KEY_LENGTH) {
49
+ delete span.metrics[metricsKey]
50
+ metricsKey = `${metricsKey.slice(0, MAX_METRIC_KEY_LENGTH)}...`
51
+ span.metrics[metricsKey] = val
57
52
  }
58
- }))
53
+ }
54
+
55
+ return span
59
56
  }
60
57
 
61
58
  function normalizeSpan (span) {
62
- const normalizedSpan = fromEntries(Object.entries(span).map(([key, value]) => {
63
- switch (key) {
64
- case 'service':
65
- if (!value) {
66
- return [key, DEFAULT_SERVICE_NAME]
67
- }
68
- if (value.length > MAX_SERVICE_LENGTH) {
69
- return [key, value.slice(0, MAX_SERVICE_LENGTH)]
70
- }
71
- break
72
- case 'name':
73
- if (!value) {
74
- return [key, DEFAULT_SPAN_NAME]
75
- }
76
- if (value.length > MAX_NAME_LENGTH) {
77
- return [key, value.slice(0, MAX_NAME_LENGTH)]
78
- }
79
- break
80
- case 'resource':
81
- if (!value) {
82
- return [key, span.name || DEFAULT_SPAN_NAME]
83
- }
84
- break
85
- case 'type':
86
- if (!value) {
87
- return [key, value]
88
- }
89
- if (value.length > MAX_TYPE_LENGTH) {
90
- return [key, value.slice(0, MAX_TYPE_LENGTH)]
91
- }
92
- }
93
- return [key, value]
94
- }))
95
- if (!normalizedSpan.service) {
96
- normalizedSpan.service = DEFAULT_SERVICE_NAME
59
+ span.service = span.service || DEFAULT_SERVICE_NAME
60
+ if (span.service.length > MAX_SERVICE_LENGTH) {
61
+ span.service = span.service.slice(0, MAX_SERVICE_LENGTH)
97
62
  }
98
- if (!normalizedSpan.name) {
99
- normalizedSpan.name = DEFAULT_SPAN_NAME
63
+ span.name = span.name || DEFAULT_SPAN_NAME
64
+ if (span.name.length > MAX_NAME_LENGTH) {
65
+ span.name = span.name.slice(0, MAX_NAME_LENGTH)
100
66
  }
101
- return normalizedSpan
67
+ if (!span.resource) {
68
+ span.resource = span.name
69
+ }
70
+ if (span.type && span.type.length > MAX_TYPE_LENGTH) {
71
+ span.type = span.type.slice(0, MAX_TYPE_LENGTH)
72
+ }
73
+
74
+ return span
102
75
  }
103
76
 
104
77
  module.exports = {
@@ -107,7 +80,6 @@ module.exports = {
107
80
  MAX_META_KEY_LENGTH,
108
81
  MAX_META_VALUE_LENGTH,
109
82
  MAX_METRIC_KEY_LENGTH,
110
- MAX_METRIC_VALUE_LENGTH,
111
83
  MAX_NAME_LENGTH,
112
84
  MAX_SERVICE_LENGTH,
113
85
  MAX_TYPE_LENGTH,
@@ -54,13 +54,13 @@ function request (data, options, callback) {
54
54
  }
55
55
 
56
56
  if (options.url) {
57
- const url = typeof options.url === 'object' ? options.url : fromUrlString(options.url)
57
+ const url = typeof options.url === 'object' ? urlToOptions(options.url) : fromUrlString(options.url)
58
58
  if (url.protocol === 'unix:') {
59
59
  options.socketPath = url.pathname
60
60
  } else {
61
61
  if (!options.path) options.path = url.path
62
62
  options.protocol = url.protocol
63
- options.hostname = url.hostname
63
+ options.hostname = url.hostname // for IPv6 this should be '::1' and not '[::1]'
64
64
  options.port = url.port
65
65
  }
66
66
  }
@@ -146,7 +146,8 @@ function extractError (trace, error) {
146
146
  trace.error = 1
147
147
 
148
148
  if (isError(error)) {
149
- addTag(trace.meta, trace.metrics, ERROR_MESSAGE, error.message)
149
+ // AggregateError only has a code and no message.
150
+ addTag(trace.meta, trace.metrics, ERROR_MESSAGE, error.message || error.code)
150
151
  addTag(trace.meta, trace.metrics, ERROR_TYPE, error.name)
151
152
  addTag(trace.meta, trace.metrics, ERROR_STACK, error.stack)
152
153
  }
@@ -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('../../diagnostics_channel')
7
7
 
8
8
  if (semver.satisfies(process.versions.node, '^12.20.0 || >=14.13.1')) {
9
9
  const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const dc = require('diagnostics_channel')
3
+ const { channel } = require('../../../diagnostics_channel')
4
4
 
5
5
  const Level = {
6
6
  Debug: 'debug',
@@ -11,19 +11,18 @@ const Level = {
11
11
 
12
12
  const defaultLevel = Level.Debug
13
13
 
14
- class LogChannel extends dc.Channel {
15
- constructor (name, logLevel) {
16
- super(`datadog:log:${name}`)
17
- this.logLevel = logLevel
18
- }
19
- }
20
-
21
14
  // based on: https://github.com/trentm/node-bunyan#levels
22
15
  const logChannels = {
23
- [Level.Debug]: new LogChannel(Level.Debug, 20),
24
- [Level.Info]: new LogChannel(Level.Info, 30),
25
- [Level.Warn]: new LogChannel(Level.Warn, 40),
26
- [Level.Error]: new LogChannel(Level.Error, 50)
16
+ [Level.Debug]: createLogChannel(Level.Debug, 20),
17
+ [Level.Info]: createLogChannel(Level.Info, 30),
18
+ [Level.Warn]: createLogChannel(Level.Warn, 40),
19
+ [Level.Error]: createLogChannel(Level.Error, 50)
20
+ }
21
+
22
+ function createLogChannel (name, logLevel) {
23
+ const logChannel = channel(`datadog:log:${name}`)
24
+ logChannel.logLevel = logLevel
25
+ return logChannel
27
26
  }
28
27
 
29
28
  function getChannelLogLevel (level) {
@@ -343,10 +343,10 @@ class TextMapPropagator {
343
343
  spanContext._trace.origin = value
344
344
  break
345
345
  case 't.dm': {
346
- const mechanism = parseInt(value, 10)
346
+ const mechanism = -Math.abs(parseInt(value, 10))
347
347
  if (Number.isInteger(mechanism)) {
348
348
  spanContext._sampling.mechanism = mechanism
349
- spanContext._trace.tags['_dd.p.dm'] = mechanism
349
+ spanContext._trace.tags['_dd.p.dm'] = String(mechanism)
350
350
  }
351
351
  break
352
352
  }
@@ -1,9 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { channel } = require('diagnostics_channel')
3
+ const { channel } = require('../../diagnostics_channel')
4
4
  const { isFalse } = require('./util')
5
5
  const plugins = require('./plugins')
6
6
  const log = require('./log')
7
+ const Nomenclature = require('./service-naming')
7
8
 
8
9
  const loadChannel = channel('dd-trace:instrumentation:load')
9
10
 
@@ -96,6 +97,7 @@ module.exports = class PluginManager {
96
97
  // like instrumenter.enable()
97
98
  configure (config = {}) {
98
99
  this._tracerConfig = config
100
+ Nomenclature.configure(config)
99
101
 
100
102
  for (const name in pluginClasses) {
101
103
  this.loadPlugin(name)
@@ -1,9 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const OutgoingPlugin = require('./outgoing')
3
+ const OutboundPlugin = require('./outbound')
4
4
 
5
- class ClientPlugin extends OutgoingPlugin {
5
+ class ClientPlugin extends OutboundPlugin {
6
6
  static get operation () { return 'request' }
7
+ static get kind () { return 'client' }
7
8
  }
8
9
 
9
10
  module.exports = ClientPlugin
@@ -1,9 +1,24 @@
1
1
  'use strict'
2
2
 
3
- const IncomingPlugin = require('./incoming')
3
+ const InboundPlugin = require('./inbound')
4
4
 
5
- class ConsumerPlugin extends IncomingPlugin {
5
+ class ConsumerPlugin extends InboundPlugin {
6
6
  static get operation () { return 'receive' }
7
+ static get kind () { return 'consumer' }
8
+ static get type () { return 'messaging' }
9
+
10
+ startSpan (options) {
11
+ const spanDefaults = {
12
+ service: this.config.service || this.serviceName(),
13
+ kind: this.constructor.kind
14
+ }
15
+ Object.keys(spanDefaults).forEach(
16
+ key => {
17
+ if (!options[key]) options[key] = spanDefaults[key]
18
+ }
19
+ )
20
+ return super.startSpan(this.operationName(), options)
21
+ }
7
22
  }
8
23
 
9
24
  module.exports = ConsumerPlugin
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ const TracingPlugin = require('./tracing')
4
+
5
+ class InboundPlugin extends TracingPlugin {}
6
+
7
+ module.exports = InboundPlugin
@@ -4,7 +4,7 @@ const { CLIENT_PORT_KEY } = require('../constants')
4
4
  const TracingPlugin = require('./tracing')
5
5
 
6
6
  // TODO: Exit span on finish when AsyncResource instances are removed.
7
- class OutgoingPlugin extends TracingPlugin {
7
+ class OutboundPlugin extends TracingPlugin {
8
8
  constructor (...args) {
9
9
  super(...args)
10
10
 
@@ -29,4 +29,4 @@ class OutgoingPlugin extends TracingPlugin {
29
29
  }
30
30
  }
31
31
 
32
- module.exports = OutgoingPlugin
32
+ module.exports = OutboundPlugin
@@ -2,7 +2,7 @@
2
2
 
3
3
  // TODO: move anything related to tracing to TracingPlugin instead
4
4
 
5
- const dc = require('diagnostics_channel')
5
+ const dc = require('../../../diagnostics_channel')
6
6
  const { storage } = require('../../../datadog-core')
7
7
 
8
8
  class Subscription {
@@ -1,9 +1,24 @@
1
1
  'use strict'
2
2
 
3
- const OutgoingPlugin = require('./outgoing')
3
+ const OutboundPlugin = require('./outbound')
4
4
 
5
- class ProducerPlugin extends OutgoingPlugin {
5
+ class ProducerPlugin extends OutboundPlugin {
6
6
  static get operation () { return 'publish' }
7
+ static get kind () { return 'producer' }
8
+ static get type () { return 'messaging' }
9
+
10
+ startSpan (options) {
11
+ const spanDefaults = {
12
+ service: this.config.service || this.serviceName(),
13
+ kind: this.constructor.kind
14
+ }
15
+ Object.keys(spanDefaults).forEach(
16
+ key => {
17
+ if (!options[key]) options[key] = spanDefaults[key]
18
+ }
19
+ )
20
+ return super.startSpan(this.operationName(), options)
21
+ }
7
22
  }
8
23
 
9
24
  module.exports = ProducerPlugin
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const IncomingPlugin = require('./incoming')
3
+ const InboundPlugin = require('./inbound')
4
4
 
5
- class ServerPlugin extends IncomingPlugin {
5
+ class ServerPlugin extends InboundPlugin {
6
6
  static get operation () { return 'request' }
7
7
  }
8
8
 
@@ -4,6 +4,7 @@ const Plugin = require('./plugin')
4
4
  const { storage } = require('../../../datadog-core')
5
5
  const analyticsSampler = require('../analytics_sampler')
6
6
  const { COMPONENT } = require('../constants')
7
+ const Nomenclature = require('../service-naming')
7
8
 
8
9
  class TracingPlugin extends Plugin {
9
10
  constructor (...args) {
@@ -31,6 +32,16 @@ class TracingPlugin extends Plugin {
31
32
  return store && store.span
32
33
  }
33
34
 
35
+ serviceName (serviceArgs) {
36
+ const { type, id, kind } = this.constructor
37
+ return Nomenclature.serviceName(type, kind, id, serviceArgs)
38
+ }
39
+
40
+ operationName (opNameArgs) {
41
+ const { type, id, kind } = this.constructor
42
+ return Nomenclature.opName(type, kind, id, opNameArgs)
43
+ }
44
+
34
45
  configure (config) {
35
46
  return super.configure({
36
47
  ...config,
@@ -36,6 +36,7 @@ const TEST_SKIP_REASON = 'test.skip_reason'
36
36
  const TEST_IS_RUM_ACTIVE = 'test.is_rum_active'
37
37
  const TEST_CODE_OWNERS = 'test.codeowners'
38
38
  const TEST_SOURCE_FILE = 'test.source.file'
39
+ const TEST_SOURCE_START = 'test.source.start'
39
40
  const LIBRARY_VERSION = 'library_version'
40
41
  const TEST_COMMAND = 'test.command'
41
42
  const TEST_MODULE = 'test.module'
@@ -77,6 +78,7 @@ module.exports = {
77
78
  LIBRARY_VERSION,
78
79
  JEST_WORKER_TRACE_PAYLOAD_CODE,
79
80
  JEST_WORKER_COVERAGE_PAYLOAD_CODE,
81
+ TEST_SOURCE_START,
80
82
  getTestEnvironmentMetadata,
81
83
  getTestParametersString,
82
84
  finishAllTraceSpans,
@@ -104,7 +106,8 @@ module.exports = {
104
106
  getCoveredFilenamesFromCoverage,
105
107
  resetCoverage,
106
108
  mergeCoverage,
107
- fromCoverageMapToCoverage
109
+ fromCoverageMapToCoverage,
110
+ getTestLineStart
108
111
  }
109
112
 
110
113
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
@@ -381,3 +384,18 @@ function fromCoverageMapToCoverage (coverageMap) {
381
384
  return acc
382
385
  }, {})
383
386
  }
387
+
388
+ // Get the start line of a test by inspecting a given error's stack trace
389
+ function getTestLineStart (err, testSuitePath) {
390
+ if (!err.stack) {
391
+ return null
392
+ }
393
+ // From https://github.com/felixge/node-stack-trace/blob/ba06dcdb50d465cd440d84a563836e293b360427/index.js#L40
394
+ const testFileLine = err.stack.split('\n').find(line => line.includes(testSuitePath))
395
+ try {
396
+ const testFileLineMatch = testFileLine.match(/at (?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/)
397
+ return parseInt(testFileLineMatch[3], 10) || null
398
+ } catch (e) {
399
+ return null
400
+ }
401
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { storage } = require('../../../../datadog-core')
4
4
 
5
- const dc = require('diagnostics_channel')
5
+ const dc = require('../../../../diagnostics_channel')
6
6
 
7
7
  const beforeCh = dc.channel('dd-trace:storage:before')
8
8
  const afterCh = dc.channel('dd-trace:storage:after')
@@ -3,7 +3,7 @@
3
3
  const path = require('path')
4
4
  const Module = require('module')
5
5
  const parse = require('module-details-from-path')
6
- const dc = require('diagnostics_channel')
6
+ const dc = require('../../diagnostics_channel')
7
7
 
8
8
  const origRequire = Module.prototype.require
9
9