dd-trace 3.54.0 → 3.56.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 (53) hide show
  1. package/index.d.ts +15 -0
  2. package/package.json +3 -2
  3. package/packages/datadog-instrumentations/src/fetch.js +6 -45
  4. package/packages/datadog-instrumentations/src/helpers/fetch.js +17 -0
  5. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -1
  6. package/packages/datadog-instrumentations/src/jest.js +161 -14
  7. package/packages/datadog-instrumentations/src/kafkajs.js +4 -7
  8. package/packages/datadog-instrumentations/src/mongoose.js +2 -1
  9. package/packages/datadog-instrumentations/src/oracledb.js +1 -1
  10. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +6 -1
  11. package/packages/datadog-instrumentations/src/selenium.js +69 -0
  12. package/packages/datadog-plugin-cucumber/src/index.js +2 -2
  13. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
  14. package/packages/datadog-plugin-cypress/src/support.js +19 -3
  15. package/packages/datadog-plugin-fetch/src/index.js +17 -11
  16. package/packages/datadog-plugin-jest/src/index.js +7 -2
  17. package/packages/datadog-plugin-mocha/src/index.js +4 -5
  18. package/packages/datadog-plugin-openai/src/services.js +2 -1
  19. package/packages/datadog-plugin-playwright/src/index.js +2 -2
  20. package/packages/datadog-plugin-selenium/src/index.js +71 -0
  21. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  22. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +70 -0
  23. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +14 -0
  24. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +12 -0
  25. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-rule-type.js +6 -0
  26. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-analyzer.js +5 -50
  27. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +742 -0
  28. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +539 -66
  29. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +6 -2
  30. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  31. package/packages/dd-trace/src/appsec/reporter.js +11 -10
  32. package/packages/dd-trace/src/appsec/telemetry.js +36 -7
  33. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  34. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +9 -2
  35. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
  36. package/packages/dd-trace/src/config.js +97 -10
  37. package/packages/dd-trace/src/dogstatsd.js +13 -11
  38. package/packages/dd-trace/src/index.js +5 -1
  39. package/packages/dd-trace/src/noop/dogstatsd.js +11 -0
  40. package/packages/dd-trace/src/noop/proxy.js +3 -0
  41. package/packages/dd-trace/src/opentracing/propagation/text_map.js +10 -4
  42. package/packages/dd-trace/src/opentracing/span.js +2 -0
  43. package/packages/dd-trace/src/plugins/index.js +2 -0
  44. package/packages/dd-trace/src/plugins/util/git.js +33 -11
  45. package/packages/dd-trace/src/plugins/util/test.js +34 -3
  46. package/packages/dd-trace/src/profiling/config.js +8 -4
  47. package/packages/dd-trace/src/profiling/profiler.js +4 -0
  48. package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +33 -0
  49. package/packages/dd-trace/src/profiling/ssi-telemetry.js +167 -0
  50. package/packages/dd-trace/src/proxy.js +7 -1
  51. package/packages/dd-trace/src/tagger.js +13 -3
  52. package/packages/dd-trace/src/telemetry/index.js +5 -4
  53. package/packages/dd-trace/src/telemetry/metrics.js +2 -2
@@ -20,11 +20,15 @@ const EXCLUDED_LOCATIONS = getNodeModulesPaths(
20
20
  'pusher/lib/utils.js',
21
21
  'redlock/dist/cjs',
22
22
  'sqreen/lib/package-reader/index.js',
23
- 'ws/lib/websocket-server.js'
23
+ 'ws/lib/websocket-server.js',
24
+ 'google-gax/build/src/grpc.js',
25
+ 'cookie-signature/index.js'
24
26
  )
25
27
 
26
28
  const EXCLUDED_PATHS_FROM_STACK = [
27
- path.join('node_modules', 'object-hash', path.sep)
29
+ path.join('node_modules', 'object-hash', path.sep),
30
+ path.join('node_modules', 'aws-sdk', 'lib', 'util.js'),
31
+ path.join('node_modules', 'keygrip', path.sep)
28
32
  ]
29
33
  class WeakHashAnalyzer extends Analyzer {
30
34
  constructor () {
@@ -1,5 +1,6 @@
1
1
  module.exports = {
2
2
  COMMAND_INJECTION: 'COMMAND_INJECTION',
3
+ HARDCODED_PASSWORD: 'HARDCODED_PASSWORD',
3
4
  HARDCODED_SECRET: 'HARDCODED_SECRET',
4
5
  HEADER_INJECTION: 'HEADER_INJECTION',
5
6
  HSTS_HEADER_MISSING: 'HSTS_HEADER_MISSING',
@@ -8,7 +8,8 @@ const {
8
8
  incrementWafInitMetric,
9
9
  updateWafRequestsMetricTags,
10
10
  incrementWafUpdatesMetric,
11
- incrementWafRequestsMetric
11
+ incrementWafRequestsMetric,
12
+ getRequestMetrics
12
13
  } = require('./telemetry')
13
14
  const zlib = require('zlib')
14
15
 
@@ -92,19 +93,10 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
92
93
  }
93
94
 
94
95
  function reportMetrics (metrics) {
95
- // TODO: metrics should be incremental, there already is an RFC to report metrics
96
96
  const store = storage.getStore()
97
97
  const rootSpan = store?.req && web.root(store.req)
98
98
  if (!rootSpan) return
99
99
 
100
- if (metrics.duration) {
101
- rootSpan.setTag('_dd.appsec.waf.duration', metrics.duration)
102
- }
103
-
104
- if (metrics.durationExt) {
105
- rootSpan.setTag('_dd.appsec.waf.duration_ext', metrics.durationExt)
106
- }
107
-
108
100
  if (metrics.rulesVersion) {
109
101
  rootSpan.setTag('_dd.appsec.event_rules.version', metrics.rulesVersion)
110
102
  }
@@ -179,6 +171,15 @@ function finishRequest (req, res) {
179
171
  metricsQueue.clear()
180
172
  }
181
173
 
174
+ const metrics = getRequestMetrics(req)
175
+ if (metrics?.duration) {
176
+ rootSpan.setTag('_dd.appsec.waf.duration', metrics.duration)
177
+ }
178
+
179
+ if (metrics?.durationExt) {
180
+ rootSpan.setTag('_dd.appsec.waf.duration_ext', metrics.durationExt)
181
+ }
182
+
182
183
  incrementWafRequestsMetric(req)
183
184
 
184
185
  // collect some headers even when no attack is detected
@@ -5,6 +5,7 @@ const telemetryMetrics = require('../telemetry/metrics')
5
5
  const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
6
6
 
7
7
  const DD_TELEMETRY_WAF_RESULT_TAGS = Symbol('_dd.appsec.telemetry.waf.result.tags')
8
+ const DD_TELEMETRY_REQUEST_METRICS = Symbol('_dd.appsec.telemetry.request.metrics')
8
9
 
9
10
  const tags = {
10
11
  REQUEST_BLOCKED: 'request_blocked',
@@ -26,10 +27,19 @@ function disable () {
26
27
  enabled = false
27
28
  }
28
29
 
30
+ function newStore () {
31
+ return {
32
+ [DD_TELEMETRY_REQUEST_METRICS]: {
33
+ duration: 0,
34
+ durationExt: 0
35
+ }
36
+ }
37
+ }
38
+
29
39
  function getStore (req) {
30
40
  let store = metricsStoreMap.get(req)
31
41
  if (!store) {
32
- store = {}
42
+ store = newStore()
33
43
  metricsStoreMap.set(req, store)
34
44
  }
35
45
  return store
@@ -51,9 +61,7 @@ function trackWafDurations (metrics, versionsTags) {
51
61
  }
52
62
  }
53
63
 
54
- function getOrCreateMetricTags (req, versionsTags) {
55
- const store = getStore(req)
56
-
64
+ function getOrCreateMetricTags (store, versionsTags) {
57
65
  let metricTags = store[DD_TELEMETRY_WAF_RESULT_TAGS]
58
66
  if (!metricTags) {
59
67
  metricTags = {
@@ -69,13 +77,20 @@ function getOrCreateMetricTags (req, versionsTags) {
69
77
  }
70
78
 
71
79
  function updateWafRequestsMetricTags (metrics, req) {
72
- if (!req || !enabled) return
80
+ if (!req) return
81
+
82
+ const store = getStore(req)
83
+
84
+ // it does not depend on whether telemetry is enabled or not
85
+ addRequestMetrics(store, metrics)
86
+
87
+ if (!enabled) return
73
88
 
74
89
  const versionsTags = getVersionsTags(metrics.wafVersion, metrics.rulesVersion)
75
90
 
76
91
  trackWafDurations(metrics, versionsTags)
77
92
 
78
- const metricTags = getOrCreateMetricTags(req, versionsTags)
93
+ const metricTags = getOrCreateMetricTags(store, versionsTags)
79
94
 
80
95
  const { blockTriggered, ruleTriggered, wafTimeout } = metrics
81
96
 
@@ -121,6 +136,18 @@ function incrementWafRequestsMetric (req) {
121
136
  metricsStoreMap.delete(req)
122
137
  }
123
138
 
139
+ function addRequestMetrics (store, { duration, durationExt }) {
140
+ store[DD_TELEMETRY_REQUEST_METRICS].duration += duration || 0
141
+ store[DD_TELEMETRY_REQUEST_METRICS].durationExt += durationExt || 0
142
+ }
143
+
144
+ function getRequestMetrics (req) {
145
+ if (req) {
146
+ const store = getStore(req)
147
+ return store?.[DD_TELEMETRY_REQUEST_METRICS]
148
+ }
149
+ }
150
+
124
151
  module.exports = {
125
152
  enable,
126
153
  disable,
@@ -128,5 +155,7 @@ module.exports = {
128
155
  updateWafRequestsMetricTags,
129
156
  incrementWafInitMetric,
130
157
  incrementWafUpdatesMetric,
131
- incrementWafRequestsMetric
158
+ incrementWafRequestsMetric,
159
+
160
+ getRequestMetrics
132
161
  }
@@ -189,7 +189,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
189
189
  isItrEnabled,
190
190
  requireGit,
191
191
  isEarlyFlakeDetectionEnabled,
192
- earlyFlakeDetectionNumRetries
192
+ earlyFlakeDetectionNumRetries,
193
+ earlyFlakeDetectionFaultyThreshold
193
194
  } = remoteConfiguration
194
195
  return {
195
196
  isCodeCoverageEnabled,
@@ -197,7 +198,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
197
198
  isItrEnabled,
198
199
  requireGit,
199
200
  isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
200
- earlyFlakeDetectionNumRetries
201
+ earlyFlakeDetectionNumRetries,
202
+ earlyFlakeDetectionFaultyThreshold
201
203
  }
202
204
  }
203
205
 
@@ -114,6 +114,10 @@ function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy, ev
114
114
 
115
115
  const commitsToUpload = getCommitsRevList(alreadySeenCommits, commitsToInclude)
116
116
 
117
+ if (commitsToUpload === null) {
118
+ return callback(new Error('git rev-list failed'))
119
+ }
120
+
117
121
  callback(null, commitsToUpload)
118
122
  })
119
123
  }
@@ -252,9 +256,8 @@ function sendGitMetadata (url, { isEvpProxy, evpProxyPrefix }, configRepositoryU
252
256
  return callback(new Error('Repository URL is empty'))
253
257
  }
254
258
 
255
- const latestCommits = getLatestCommits()
259
+ let latestCommits = getLatestCommits()
256
260
  log.debug(`There were ${latestCommits.length} commits since last month.`)
257
- const [headCommit] = latestCommits
258
261
 
259
262
  const getOnFinishGetCommitsToUpload = (hasCheckedShallow) => (err, commitsToUpload) => {
260
263
  if (err) {
@@ -268,6 +271,7 @@ function sendGitMetadata (url, { isEvpProxy, evpProxyPrefix }, configRepositoryU
268
271
 
269
272
  // If it has already unshallowed or the clone is not shallow, we move on
270
273
  if (hasCheckedShallow || !isShallowRepository()) {
274
+ const [headCommit] = latestCommits
271
275
  return generateAndUploadPackFiles({
272
276
  url,
273
277
  isEvpProxy,
@@ -280,6 +284,9 @@ function sendGitMetadata (url, { isEvpProxy, evpProxyPrefix }, configRepositoryU
280
284
  // Otherwise we unshallow and get commits to upload again
281
285
  log.debug('It is shallow clone, unshallowing...')
282
286
  unshallowRepository()
287
+
288
+ // The latest commits change after unshallowing
289
+ latestCommits = getLatestCommits()
283
290
  getCommitsToUpload({
284
291
  url,
285
292
  repositoryUrl,
@@ -12,6 +12,7 @@ const {
12
12
  } = require('../telemetry')
13
13
 
14
14
  const DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES = 2
15
+ const DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD = 30
15
16
 
16
17
  function getLibraryConfiguration ({
17
18
  url,
@@ -104,7 +105,9 @@ function getLibraryConfiguration ({
104
105
  requireGit,
105
106
  isEarlyFlakeDetectionEnabled: earlyFlakeDetectionConfig?.enabled ?? false,
106
107
  earlyFlakeDetectionNumRetries:
107
- earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES
108
+ earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES,
109
+ earlyFlakeDetectionFaultyThreshold:
110
+ earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD
108
111
  }
109
112
 
110
113
  log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
@@ -15,9 +15,65 @@ const { isTrue, isFalse } = require('./util')
15
15
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags')
16
16
  const { getGitMetadataFromGitProperties, removeUserSensitiveInfo } = require('./git_properties')
17
17
  const { updateConfig } = require('./telemetry')
18
+ const telemetryMetrics = require('./telemetry/metrics')
18
19
  const { getIsGCPFunction, getIsAzureFunctionConsumptionPlan } = require('./serverless')
19
20
  const { ORIGIN_KEY } = require('./constants')
20
21
 
22
+ const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
23
+
24
+ const telemetryCounters = {
25
+ 'otel.env.hiding': {},
26
+ 'otel.env.invalid': {}
27
+ }
28
+
29
+ function getCounter (event, ddVar, otelVar, otelTracesSamplerArg) {
30
+ const counters = telemetryCounters[event]
31
+ const tags = []
32
+
33
+ if (ddVar) tags.push(ddVar)
34
+ if (otelVar) tags.push(otelVar)
35
+ if (otelTracesSamplerArg) tags.push(otelTracesSamplerArg)
36
+
37
+ if (!(ddVar in counters)) counters[ddVar] = {}
38
+
39
+ const counter = tracerMetrics.count(event, tags)
40
+ counters[ddVar][otelVar] = counter
41
+ return counter
42
+ }
43
+
44
+ const otelDdEnvMapping = {
45
+ DD_TRACE_LOG_LEVEL: 'OTEL_LOG_LEVEL',
46
+ DD_TRACE_PROPAGATION_STYLE: 'OTEL_PROPAGATORS',
47
+ DD_SERVICE: 'OTEL_SERVICE_NAME',
48
+ DD_TRACE_SAMPLE_RATE: 'OTEL_TRACES_SAMPLER',
49
+ DD_TRACE_ENABLED: 'OTEL_TRACES_EXPORTER',
50
+ DD_RUNTIME_METRICS_ENABLED: 'OTEL_METRICS_EXPORTER',
51
+ DD_TAGS: 'OTEL_RESOURCE_ATTRIBUTES',
52
+ DD_TRACE_OTEL_ENABLED: 'OTEL_SDK_DISABLED'
53
+ }
54
+
55
+ const otelInvalidEnv = ['OTEL_LOGS_EXPORTER']
56
+
57
+ function checkIfBothOtelAndDdEnvVarSet () {
58
+ for (const [ddVar, otelVar] of Object.entries(otelDdEnvMapping)) {
59
+ if (process.env[ddVar] && process.env[otelVar]) {
60
+ log.warn(`both ${ddVar} and ${otelVar} environment variables are set`)
61
+ getCounter('otel.env.hiding', ddVar, otelVar,
62
+ otelVar === 'OTEL_TRACES_SAMPLER' &&
63
+ process.env.OTEL_TRACES_SAMPLER_ARG
64
+ ? 'OTEL_TRACES_SAMPLER_ARG'
65
+ : undefined).inc()
66
+ }
67
+ }
68
+
69
+ for (const otelVar of otelInvalidEnv) {
70
+ if (process.env[otelVar]) {
71
+ log.warn(`${otelVar} is not supported by the Datadog SDK`)
72
+ getCounter('otel.env.invalid', otelVar).inc()
73
+ }
74
+ }
75
+ }
76
+
21
77
  const fromEntries = Object.fromEntries || (entries =>
22
78
  entries.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {}))
23
79
 
@@ -27,6 +83,7 @@ const qsRegex = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private
27
83
  const defaultWafObfuscatorKeyRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?)key)|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization'
28
84
  // eslint-disable-next-line max-len
29
85
  const defaultWafObfuscatorValueRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:\\s*=[^;]|"\\s*:\\s*"[^"]+")|bearer\\s+[a-z0-9\\._\\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=-]+\\.ey[I-L][\\w=-]+(?:\\.[\\w.+\\/=-]+)?|[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*[a-z0-9\\/\\.+]{100,}'
86
+ const runtimeId = uuid()
30
87
 
31
88
  function maybeFile (filepath) {
32
89
  if (!filepath) return
@@ -88,7 +145,8 @@ function propagationStyle (key, option, defaultValue) {
88
145
 
89
146
  // Otherwise, fallback to env var parsing
90
147
  const envKey = `DD_TRACE_PROPAGATION_STYLE_${key.toUpperCase()}`
91
- const envVar = coalesce(process.env[envKey], process.env.DD_TRACE_PROPAGATION_STYLE)
148
+
149
+ const envVar = coalesce(process.env[envKey], process.env.DD_TRACE_PROPAGATION_STYLE, process.env.OTEL_PROPAGATORS)
92
150
  if (typeof envVar !== 'undefined') {
93
151
  return envVar.split(',')
94
152
  .filter(v => v !== '')
@@ -107,15 +165,19 @@ class Config {
107
165
  iastOptions: options.experimental?.iast
108
166
  }
109
167
 
168
+ checkIfBothOtelAndDdEnvVarSet()
169
+
110
170
  // Configure the logger first so it can be used to warn about other configs
111
171
  this.debug = isTrue(coalesce(
112
172
  process.env.DD_TRACE_DEBUG,
113
173
  false
114
174
  ))
115
175
  this.logger = options.logger
176
+
116
177
  this.logLevel = coalesce(
117
178
  options.logLevel,
118
179
  process.env.DD_TRACE_LOG_LEVEL,
180
+ process.env.OTEL_LOG_LEVEL,
119
181
  'debug'
120
182
  )
121
183
 
@@ -162,12 +224,12 @@ class Config {
162
224
  'environment variables'
163
225
  )
164
226
  }
165
- const DD_TRACE_PROPAGATION_STYLE_INJECT = propagationStyle(
227
+ const PROPAGATION_STYLE_INJECT = propagationStyle(
166
228
  'inject',
167
229
  options.tracePropagationStyle,
168
230
  defaultPropagationStyle
169
231
  )
170
- const DD_TRACE_PROPAGATION_STYLE_EXTRACT = propagationStyle(
232
+ const PROPAGATION_STYLE_EXTRACT = propagationStyle(
171
233
  'extract',
172
234
  options.tracePropagationStyle,
173
235
  defaultPropagationStyle
@@ -252,8 +314,13 @@ class Config {
252
314
  this.apiKey = DD_API_KEY
253
315
  this.serviceMapping = DD_SERVICE_MAPPING
254
316
  this.tracePropagationStyle = {
255
- inject: DD_TRACE_PROPAGATION_STYLE_INJECT,
256
- extract: DD_TRACE_PROPAGATION_STYLE_EXTRACT
317
+ inject: PROPAGATION_STYLE_INJECT,
318
+ extract: PROPAGATION_STYLE_EXTRACT,
319
+ otelPropagators: process.env.DD_TRACE_PROPAGATION_STYLE ||
320
+ process.env.DD_TRACE_PROPAGATION_STYLE_INJECT ||
321
+ process.env.DD_TRACE_PROPAGATION_STYLE_EXTRACT
322
+ ? false
323
+ : !!process.env.OTEL_PROPAGATORS
257
324
  }
258
325
  this.tracePropagationExtractFirst = isTrue(DD_TRACE_PROPAGATION_EXTRACT_FIRST)
259
326
  this.sampler = sampler
@@ -291,7 +358,7 @@ class Config {
291
358
  service: this.service,
292
359
  env: this.env,
293
360
  version: this.version,
294
- 'runtime-id': uuid()
361
+ 'runtime-id': runtimeId
295
362
  })
296
363
 
297
364
  if (this.isCiVisibility) {
@@ -525,12 +592,18 @@ class Config {
525
592
  DD_TRACE_TELEMETRY_ENABLED,
526
593
  DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
527
594
  DD_TRACING_ENABLED,
528
- DD_VERSION
595
+ DD_VERSION,
596
+ OTEL_SERVICE_NAME,
597
+ OTEL_RESOURCE_ATTRIBUTES,
598
+ OTEL_TRACES_SAMPLER,
599
+ OTEL_TRACES_SAMPLER_ARG,
600
+ OTEL_METRICS_EXPORTER
529
601
  } = process.env
530
602
 
531
603
  const tags = {}
532
604
  const env = this._env = {}
533
605
 
606
+ tagger.add(tags, OTEL_RESOURCE_ATTRIBUTES, true)
534
607
  tagger.add(tags, DD_TAGS)
535
608
  tagger.add(tags, DD_TRACE_TAGS)
536
609
  tagger.add(tags, DD_TRACE_GLOBAL_TAGS)
@@ -591,11 +664,24 @@ class Config {
591
664
  ))
592
665
  this._setValue(env, 'remoteConfig.pollInterval', maybeFloat(DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS))
593
666
  this._setBoolean(env, 'reportHostname', DD_TRACE_REPORT_HOSTNAME)
594
- this._setBoolean(env, 'runtimeMetrics', DD_RUNTIME_METRICS_ENABLED)
595
- this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE)
667
+ // only used to explicitly set runtimeMetrics to false
668
+ const otelSetRuntimeMetrics = String(OTEL_METRICS_EXPORTER).toLowerCase() === 'none'
669
+ ? false
670
+ : undefined
671
+ this._setBoolean(env, 'runtimeMetrics', DD_RUNTIME_METRICS_ENABLED ||
672
+ otelSetRuntimeMetrics)
673
+ const OTEL_TRACES_SAMPLER_MAPPING = {
674
+ always_on: '1.0',
675
+ always_off: '0.0',
676
+ traceidratio: OTEL_TRACES_SAMPLER_ARG,
677
+ parentbased_always_on: '1.0',
678
+ parentbased_always_off: '0.0',
679
+ parentbased_traceidratio: OTEL_TRACES_SAMPLER_ARG
680
+ }
681
+ this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE || OTEL_TRACES_SAMPLER_MAPPING[OTEL_TRACES_SAMPLER])
596
682
  this._setValue(env, 'sampler.rateLimit', DD_TRACE_RATE_LIMIT)
597
683
  this._setString(env, 'scope', DD_TRACE_SCOPE)
598
- this._setString(env, 'service', DD_SERVICE || DD_SERVICE_NAME || tags.service)
684
+ this._setString(env, 'service', DD_SERVICE || DD_SERVICE_NAME || tags.service || OTEL_SERVICE_NAME)
599
685
  this._setString(env, 'site', DD_SITE)
600
686
  if (DD_TRACE_SPAN_ATTRIBUTE_SCHEMA) {
601
687
  this._setString(env, 'spanAttributeSchema', validateNamingVersion(DD_TRACE_SPAN_ATTRIBUTE_SCHEMA))
@@ -823,6 +909,7 @@ class Config {
823
909
  : undefined
824
910
 
825
911
  tagger.add(tags, options.tracing_tags)
912
+ if (Object.keys(tags).length) tags['runtime-id'] = runtimeId
826
913
 
827
914
  this._setUnit(opts, 'sampleRate', options.tracing_sampling_rate)
828
915
  this._setBoolean(opts, 'logInjection', options.log_injection_enabled)
@@ -12,6 +12,7 @@ const MAX_BUFFER_SIZE = 1024 // limit from the agent
12
12
  const TYPE_COUNTER = 'c'
13
13
  const TYPE_GAUGE = 'g'
14
14
  const TYPE_DISTRIBUTION = 'd'
15
+ const TYPE_HISTOGRAM = 'h'
15
16
 
16
17
  class DogStatsDClient {
17
18
  constructor (options = {}) {
@@ -46,6 +47,10 @@ class DogStatsDClient {
46
47
  this._add(stat, value, TYPE_DISTRIBUTION, tags)
47
48
  }
48
49
 
50
+ histogram (stat, value, tags) {
51
+ this._add(stat, value, TYPE_HISTOGRAM, tags)
52
+ }
53
+
49
54
  flush () {
50
55
  const queue = this._enqueue()
51
56
 
@@ -180,16 +185,6 @@ class DogStatsDClient {
180
185
  }
181
186
  }
182
187
 
183
- class NoopDogStatsDClient {
184
- gauge () { }
185
-
186
- increment () { }
187
-
188
- distribution () { }
189
-
190
- flush () { }
191
- }
192
-
193
188
  // This is a simplified user-facing proxy to the underlying DogStatsDClient instance
194
189
  class CustomMetrics {
195
190
  constructor (config) {
@@ -229,6 +224,14 @@ class CustomMetrics {
229
224
  )
230
225
  }
231
226
 
227
+ histogram (stat, value, tags) {
228
+ return this.dogstatsd.histogram(
229
+ stat,
230
+ value,
231
+ CustomMetrics.tagTranslator(tags)
232
+ )
233
+ }
234
+
232
235
  flush () {
233
236
  return this.dogstatsd.flush()
234
237
  }
@@ -252,6 +255,5 @@ class CustomMetrics {
252
255
 
253
256
  module.exports = {
254
257
  DogStatsDClient,
255
- NoopDogStatsDClient,
256
258
  CustomMetrics
257
259
  }
@@ -5,6 +5,10 @@ const { isFalse } = require('./util')
5
5
  // Global `jest` is only present in Jest workers.
6
6
  const inJestWorker = typeof jest !== 'undefined'
7
7
 
8
- module.exports = isFalse(process.env.DD_TRACE_ENABLED) || inJestWorker
8
+ const ddTraceDisabled = process.env.DD_TRACE_ENABLED
9
+ ? isFalse(process.env.DD_TRACE_ENABLED)
10
+ : String(process.env.OTEL_TRACES_EXPORTER).toLowerCase() === 'none'
11
+
12
+ module.exports = ddTraceDisabled || inJestWorker
9
13
  ? require('./noop/proxy')
10
14
  : require('./proxy')
@@ -0,0 +1,11 @@
1
+ module.exports = class NoopDogStatsDClient {
2
+ increment () { }
3
+
4
+ gauge () { }
5
+
6
+ distribution () { }
7
+
8
+ histogram () { }
9
+
10
+ flush () { }
11
+ }
@@ -2,14 +2,17 @@
2
2
 
3
3
  const NoopTracer = require('./tracer')
4
4
  const NoopAppsecSdk = require('../appsec/sdk/noop')
5
+ const NoopDogStatsDClient = require('./dogstatsd')
5
6
 
6
7
  const noop = new NoopTracer()
7
8
  const noopAppsec = new NoopAppsecSdk()
9
+ const noopDogStatsDClient = new NoopDogStatsDClient()
8
10
 
9
11
  class Tracer {
10
12
  constructor () {
11
13
  this._tracer = noop
12
14
  this.appsec = noopAppsec
15
+ this.dogstatsd = noopDogStatsDClient
13
16
  }
14
17
 
15
18
  init () {
@@ -224,13 +224,19 @@ class TextMapPropagator {
224
224
  case 'tracecontext':
225
225
  spanContext = this._extractTraceparentContext(carrier)
226
226
  break
227
- case 'b3': // TODO: should match "b3 single header" in next major
228
- case 'b3multi':
229
- spanContext = this._extractB3MultiContext(carrier)
230
- break
227
+ case 'b3' && this
228
+ ._config
229
+ .tracePropagationStyle
230
+ .otelPropagators: // TODO: should match "b3 single header" in next major
231
231
  case 'b3 single header': // TODO: delete in major after singular "b3"
232
232
  spanContext = this._extractB3SingleContext(carrier)
233
233
  break
234
+ case 'b3':
235
+ case 'b3multi':
236
+ spanContext = this._extractB3MultiContext(carrier)
237
+ break
238
+ default:
239
+ log.warn(`Unknown propagation style: ${extractor}`)
234
240
  }
235
241
 
236
242
  if (spanContext !== null) {
@@ -33,6 +33,7 @@ const integrationCounters = {
33
33
  spans_finished: {}
34
34
  }
35
35
 
36
+ const startCh = channel('dd-trace:span:start')
36
37
  const finishCh = channel('dd-trace:span:finish')
37
38
 
38
39
  function getIntegrationCounter (event, integration) {
@@ -96,6 +97,7 @@ class DatadogSpan {
96
97
  unfinishedRegistry.register(this, operationName, this)
97
98
  }
98
99
  spanleak.addSpan(this, operationName)
100
+ startCh.publish(this)
99
101
  }
100
102
 
101
103
  toString () {
@@ -45,6 +45,7 @@ module.exports = {
45
45
  get 'jest-environment-node' () { return require('../../../datadog-plugin-jest/src') },
46
46
  get 'jest-environment-jsdom' () { return require('../../../datadog-plugin-jest/src') },
47
47
  get 'jest-jasmine2' () { return require('../../../datadog-plugin-jest/src') },
48
+ get 'jest-runtime' () { return require('../../../datadog-plugin-jest/src') },
48
49
  get 'jest-worker' () { return require('../../../datadog-plugin-jest/src') },
49
50
  get koa () { return require('../../../datadog-plugin-koa/src') },
50
51
  get 'koa-router' () { return require('../../../datadog-plugin-koa/src') },
@@ -77,6 +78,7 @@ module.exports = {
77
78
  get restify () { return require('../../../datadog-plugin-restify/src') },
78
79
  get rhea () { return require('../../../datadog-plugin-rhea/src') },
79
80
  get router () { return require('../../../datadog-plugin-router/src') },
81
+ get 'selenium-webdriver' () { return require('../../../datadog-plugin-selenium/src') },
80
82
  get sharedb () { return require('../../../datadog-plugin-sharedb/src') },
81
83
  get tedious () { return require('../../../datadog-plugin-tedious/src') },
82
84
  get winston () { return require('../../../datadog-plugin-winston/src') }