dd-trace 3.55.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 (48) hide show
  1. package/index.d.ts +15 -0
  2. package/package.json +2 -1
  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 +77 -10
  7. package/packages/datadog-instrumentations/src/mongoose.js +2 -1
  8. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +6 -1
  9. package/packages/datadog-instrumentations/src/selenium.js +69 -0
  10. package/packages/datadog-plugin-cucumber/src/index.js +2 -2
  11. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
  12. package/packages/datadog-plugin-cypress/src/support.js +19 -3
  13. package/packages/datadog-plugin-fetch/src/index.js +17 -11
  14. package/packages/datadog-plugin-jest/src/index.js +7 -2
  15. package/packages/datadog-plugin-mocha/src/index.js +4 -5
  16. package/packages/datadog-plugin-openai/src/services.js +2 -1
  17. package/packages/datadog-plugin-playwright/src/index.js +2 -2
  18. package/packages/datadog-plugin-selenium/src/index.js +71 -0
  19. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  20. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +70 -0
  21. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +14 -0
  22. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +12 -0
  23. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-rule-type.js +6 -0
  24. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-analyzer.js +5 -50
  25. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +742 -0
  26. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +539 -66
  27. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  28. package/packages/dd-trace/src/appsec/reporter.js +11 -10
  29. package/packages/dd-trace/src/appsec/telemetry.js +36 -7
  30. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  31. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
  32. package/packages/dd-trace/src/config.js +94 -9
  33. package/packages/dd-trace/src/dogstatsd.js +13 -11
  34. package/packages/dd-trace/src/index.js +5 -1
  35. package/packages/dd-trace/src/noop/dogstatsd.js +11 -0
  36. package/packages/dd-trace/src/noop/proxy.js +3 -0
  37. package/packages/dd-trace/src/opentracing/propagation/text_map.js +10 -4
  38. package/packages/dd-trace/src/opentracing/span.js +2 -0
  39. package/packages/dd-trace/src/plugins/index.js +2 -0
  40. package/packages/dd-trace/src/plugins/util/test.js +34 -3
  41. package/packages/dd-trace/src/profiling/config.js +8 -4
  42. package/packages/dd-trace/src/profiling/profiler.js +4 -0
  43. package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +33 -0
  44. package/packages/dd-trace/src/profiling/ssi-telemetry.js +167 -0
  45. package/packages/dd-trace/src/proxy.js +7 -1
  46. package/packages/dd-trace/src/tagger.js +13 -3
  47. package/packages/dd-trace/src/telemetry/index.js +5 -4
  48. package/packages/dd-trace/src/telemetry/metrics.js +2 -2
@@ -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
 
@@ -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
 
@@ -89,7 +145,8 @@ function propagationStyle (key, option, defaultValue) {
89
145
 
90
146
  // Otherwise, fallback to env var parsing
91
147
  const envKey = `DD_TRACE_PROPAGATION_STYLE_${key.toUpperCase()}`
92
- 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)
93
150
  if (typeof envVar !== 'undefined') {
94
151
  return envVar.split(',')
95
152
  .filter(v => v !== '')
@@ -108,15 +165,19 @@ class Config {
108
165
  iastOptions: options.experimental?.iast
109
166
  }
110
167
 
168
+ checkIfBothOtelAndDdEnvVarSet()
169
+
111
170
  // Configure the logger first so it can be used to warn about other configs
112
171
  this.debug = isTrue(coalesce(
113
172
  process.env.DD_TRACE_DEBUG,
114
173
  false
115
174
  ))
116
175
  this.logger = options.logger
176
+
117
177
  this.logLevel = coalesce(
118
178
  options.logLevel,
119
179
  process.env.DD_TRACE_LOG_LEVEL,
180
+ process.env.OTEL_LOG_LEVEL,
120
181
  'debug'
121
182
  )
122
183
 
@@ -163,12 +224,12 @@ class Config {
163
224
  'environment variables'
164
225
  )
165
226
  }
166
- const DD_TRACE_PROPAGATION_STYLE_INJECT = propagationStyle(
227
+ const PROPAGATION_STYLE_INJECT = propagationStyle(
167
228
  'inject',
168
229
  options.tracePropagationStyle,
169
230
  defaultPropagationStyle
170
231
  )
171
- const DD_TRACE_PROPAGATION_STYLE_EXTRACT = propagationStyle(
232
+ const PROPAGATION_STYLE_EXTRACT = propagationStyle(
172
233
  'extract',
173
234
  options.tracePropagationStyle,
174
235
  defaultPropagationStyle
@@ -253,8 +314,13 @@ class Config {
253
314
  this.apiKey = DD_API_KEY
254
315
  this.serviceMapping = DD_SERVICE_MAPPING
255
316
  this.tracePropagationStyle = {
256
- inject: DD_TRACE_PROPAGATION_STYLE_INJECT,
257
- 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
258
324
  }
259
325
  this.tracePropagationExtractFirst = isTrue(DD_TRACE_PROPAGATION_EXTRACT_FIRST)
260
326
  this.sampler = sampler
@@ -526,12 +592,18 @@ class Config {
526
592
  DD_TRACE_TELEMETRY_ENABLED,
527
593
  DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
528
594
  DD_TRACING_ENABLED,
529
- 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
530
601
  } = process.env
531
602
 
532
603
  const tags = {}
533
604
  const env = this._env = {}
534
605
 
606
+ tagger.add(tags, OTEL_RESOURCE_ATTRIBUTES, true)
535
607
  tagger.add(tags, DD_TAGS)
536
608
  tagger.add(tags, DD_TRACE_TAGS)
537
609
  tagger.add(tags, DD_TRACE_GLOBAL_TAGS)
@@ -592,11 +664,24 @@ class Config {
592
664
  ))
593
665
  this._setValue(env, 'remoteConfig.pollInterval', maybeFloat(DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS))
594
666
  this._setBoolean(env, 'reportHostname', DD_TRACE_REPORT_HOSTNAME)
595
- this._setBoolean(env, 'runtimeMetrics', DD_RUNTIME_METRICS_ENABLED)
596
- 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])
597
682
  this._setValue(env, 'sampler.rateLimit', DD_TRACE_RATE_LIMIT)
598
683
  this._setString(env, 'scope', DD_TRACE_SCOPE)
599
- 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)
600
685
  this._setString(env, 'site', DD_SITE)
601
686
  if (DD_TRACE_SPAN_ATTRIBUTE_SCHEMA) {
602
687
  this._setString(env, 'spanAttributeSchema', validateNamingVersion(DD_TRACE_SPAN_ATTRIBUTE_SCHEMA))
@@ -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') }
@@ -53,7 +53,8 @@ const TEST_CONFIGURATION_BROWSER_NAME = 'test.configuration.browser_name'
53
53
  // Early flake detection
54
54
  const TEST_IS_NEW = 'test.is_new'
55
55
  const TEST_IS_RETRY = 'test.is_retry'
56
- const TEST_EARLY_FLAKE_IS_ENABLED = 'test.early_flake.is_enabled'
56
+ const TEST_EARLY_FLAKE_ENABLED = 'test.early_flake.enabled'
57
+ const TEST_EARLY_FLAKE_ABORT_REASON = 'test.early_flake.abort_reason'
57
58
 
58
59
  const CI_APP_ORIGIN = 'ciapp-test'
59
60
 
@@ -71,6 +72,12 @@ const ITR_CORRELATION_ID = 'itr_correlation_id'
71
72
 
72
73
  const TEST_CODE_COVERAGE_LINES_PCT = 'test.code_coverage.lines_pct'
73
74
 
75
+ // selenium tags
76
+ const TEST_BROWSER_DRIVER = 'test.browser.driver'
77
+ const TEST_BROWSER_DRIVER_VERSION = 'test.browser.driver_version'
78
+ const TEST_BROWSER_NAME = 'test.browser.name'
79
+ const TEST_BROWSER_VERSION = 'test.browser.version'
80
+
74
81
  // jest worker variables
75
82
  const JEST_WORKER_TRACE_PAYLOAD_CODE = 60
76
83
  const JEST_WORKER_COVERAGE_PAYLOAD_CODE = 61
@@ -102,7 +109,8 @@ module.exports = {
102
109
  TEST_CONFIGURATION_BROWSER_NAME,
103
110
  TEST_IS_NEW,
104
111
  TEST_IS_RETRY,
105
- TEST_EARLY_FLAKE_IS_ENABLED,
112
+ TEST_EARLY_FLAKE_ENABLED,
113
+ TEST_EARLY_FLAKE_ABORT_REASON,
106
114
  getTestEnvironmentMetadata,
107
115
  getTestParametersString,
108
116
  finishAllTraceSpans,
@@ -141,7 +149,12 @@ module.exports = {
141
149
  EFD_STRING,
142
150
  EFD_TEST_NAME_REGEX,
143
151
  removeEfdStringFromTestName,
144
- addEfdStringToTestName
152
+ addEfdStringToTestName,
153
+ getIsFaultyEarlyFlakeDetection,
154
+ TEST_BROWSER_DRIVER,
155
+ TEST_BROWSER_DRIVER_VERSION,
156
+ TEST_BROWSER_NAME,
157
+ TEST_BROWSER_VERSION
145
158
  }
146
159
 
147
160
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
@@ -571,3 +584,21 @@ function addEfdStringToTestName (testName, numAttempt) {
571
584
  function removeEfdStringFromTestName (testName) {
572
585
  return testName.replace(EFD_TEST_NAME_REGEX, '')
573
586
  }
587
+
588
+ function getIsFaultyEarlyFlakeDetection (projectSuites, testsBySuiteName, faultyThresholdPercentage) {
589
+ let newSuites = 0
590
+ for (const suite of projectSuites) {
591
+ if (!testsBySuiteName[suite]) {
592
+ newSuites++
593
+ }
594
+ }
595
+ const newSuitesPercentage = (newSuites / projectSuites.length) * 100
596
+
597
+ // The faulty threshold represents a percentage, but we also want to consider
598
+ // smaller projects, where big variations in the % are more likely.
599
+ // This is why we also check the absolute number of new suites.
600
+ return (
601
+ newSuites > faultyThresholdPercentage &&
602
+ newSuitesPercentage > faultyThresholdPercentage
603
+ )
604
+ }
@@ -21,6 +21,7 @@ class Config {
21
21
  DD_AGENT_HOST,
22
22
  DD_ENV,
23
23
  DD_PROFILING_CODEHOTSPOTS_ENABLED,
24
+ DD_PROFILING_CPU_ENABLED,
24
25
  DD_PROFILING_DEBUG_SOURCE_MAPS,
25
26
  DD_PROFILING_ENABLED,
26
27
  DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
@@ -165,7 +166,7 @@ class Config {
165
166
 
166
167
  this.timelineEnabled = isTrue(coalesce(options.timelineEnabled,
167
168
  DD_PROFILING_TIMELINE_ENABLED,
168
- DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED, false))
169
+ DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED, samplingContextsAvailable))
169
170
  logExperimentalVarDeprecation('TIMELINE_ENABLED')
170
171
  checkOptionWithSamplingContextAllowed(this.timelineEnabled, 'Timeline view')
171
172
 
@@ -176,7 +177,9 @@ class Config {
176
177
  checkOptionWithSamplingContextAllowed(this.codeHotspotsEnabled, 'Code hotspots')
177
178
 
178
179
  this.cpuProfilingEnabled = isTrue(coalesce(options.cpuProfilingEnabled,
179
- DD_PROFILING_EXPERIMENTAL_CPU_ENABLED, false))
180
+ DD_PROFILING_CPU_ENABLED,
181
+ DD_PROFILING_EXPERIMENTAL_CPU_ENABLED, samplingContextsAvailable))
182
+ logExperimentalVarDeprecation('CPU_ENABLED')
180
183
  checkOptionWithSamplingContextAllowed(this.cpuProfilingEnabled, 'CPU profiling')
181
184
 
182
185
  this.profilers = ensureProfilers(profilers, this)
@@ -288,8 +291,9 @@ function ensureProfilers (profilers, options) {
288
291
  }
289
292
  }
290
293
 
291
- // Events profiler is a profiler for timeline events
292
- if (options.timelineEnabled) {
294
+ // Events profiler is a profiler that produces timeline events. It is only
295
+ // added if timeline is enabled and there's a wall profiler.
296
+ if (options.timelineEnabled && profilers.some(p => p instanceof WallProfiler)) {
293
297
  profilers.push(new EventsProfiler(options))
294
298
  }
295
299
 
@@ -4,6 +4,9 @@ const { EventEmitter } = require('events')
4
4
  const { Config } = require('./config')
5
5
  const { snapshotKinds } = require('./constants')
6
6
  const { threadNamePrefix } = require('./profilers/shared')
7
+ const dc = require('dc-polyfill')
8
+
9
+ const profileSubmittedChannel = dc.channel('datadog:profiling:profile-submitted')
7
10
 
8
11
  function maybeSourceMap (sourceMap, SourceMapper, debug) {
9
12
  if (!sourceMap) return
@@ -161,6 +164,7 @@ class Profiler extends EventEmitter {
161
164
  this._capture(this._timeoutInterval, endDate)
162
165
  }
163
166
  await this._submit(encodedProfiles, startDate, endDate, snapshotKind)
167
+ profileSubmittedChannel.publish()
164
168
  this._logger.debug('Submitted profiles')
165
169
  } catch (err) {
166
170
  this._logger.error(err)
@@ -0,0 +1,33 @@
1
+ 'use strict'
2
+
3
+ const dc = require('dc-polyfill')
4
+ const coalesce = require('koalas')
5
+ const profileSubmittedChannel = dc.channel('datadog:profiling:mock-profile-submitted')
6
+ const { DD_PROFILING_UPLOAD_PERIOD } = process.env
7
+
8
+ let timerId
9
+
10
+ module.exports = {
11
+ start: config => {
12
+ // Copied from packages/dd-trace/src/profiler.js
13
+ const flushInterval = coalesce(config.interval, Number(DD_PROFILING_UPLOAD_PERIOD) * 1000, 65 * 1000)
14
+
15
+ function scheduleProfileSubmit () {
16
+ timerId = setTimeout(emitProfileSubmit, flushInterval)
17
+ }
18
+
19
+ function emitProfileSubmit () {
20
+ profileSubmittedChannel.publish()
21
+ scheduleProfileSubmit()
22
+ }
23
+
24
+ scheduleProfileSubmit()
25
+ },
26
+
27
+ stop: () => {
28
+ if (timerId !== undefined) {
29
+ clearTimeout(timerId)
30
+ timerId = undefined
31
+ }
32
+ }
33
+ }